This commit is contained in:
Bozhidar 2024-04-22 14:14:05 +03:00
parent bef4d9816c
commit a9d5fc38a1
1134 changed files with 42316 additions and 1 deletions

103
.github/workflows/app-unit-test.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: Phyre Panel - Unit Test & Build
on: [push]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
phyre-panel-unit-test:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-20.04]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v2
with:
repository: ${{ github.repository }}
ref: ${{ github.sha }}
- name: Install Base
run: |
ls -la
sudo mkdir /phyre-panel
sudo cp installers/${{ matrix.os }}/install-partial/install_base.sh /phyre-panel/install_base.sh
sudo chmod +x /phyre-panel/install_base.sh
sudo /phyre-panel/install_base.sh
sudo cp installers/${{ matrix.os }}/install-partial/install_web.sh /phyre-panel/install_web.sh
sudo chmod +x /phyre-panel/install_web.sh
- name: Run Unit Test
run: |
sudo cp -r web /usr/local/phyre/web/
cd /usr/local/phyre/web/
ls -la
sudo wget https://getcomposer.org/download/latest-stable/composer.phar
sudo COMPOSER_ALLOW_SUPERUSER=1 phyre-php composer.phar install
sudo /phyre-panel/install_web.sh
sudo phyre-php artisan test
compile-phyre-web-panel:
runs-on: ubuntu-22.04
needs: phyre-panel-unit-test
steps:
- uses: actions/checkout@v2
with:
repository: ${{ github.repository }}
- name: Npm install
uses: actions/setup-node@v3
with:
node-version: 16
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install Composer Dependencies
working-directory: ./web
run: |
composer install
composer dump-autoload
- name: Install NODE Dependencies
working-directory: ./web
run: |
npm install
npm run build
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v3.x
- name: Zip the files
working-directory: ./web
run: |
rm -rf .git
rm -rf .github
rm -rf .nmp
rm -rf node_modules
rm -rf .phpunit.cache
rm -rf vendor/composer/tmp-*.zip
find . \( -name ".git" -o -name ".gitignore" -o -name ".gitmodules" -o -name ".gitattributes" \) -exec rm -rf -- {} +
zip -r phyre-web-panel-build.zip `ls -A`
mkdir -p ../dist
mv ./phyre-web-panel-build.zip ../dist/phyre-web-panel.zip
- name: Pushes to Phyre Panel Dist Repo
uses: cpina/github-action-push-to-another-repository@main
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
with:
source-directory: './dist'
destination-github-username: 'CloudVisionApps'
destination-repository-name: 'PhyrePanelWebDist'
user-email: bobicloudvision@gmail.com
target-branch: main

View File

@ -0,0 +1,66 @@
name: Compile Phyre Web Panel
on:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
compile-phyre-web-panel:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
repository: ${{ github.repository }}
- name: Npm install
uses: actions/setup-node@v3
with:
node-version: 16
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
- name: Install Composer Dependencies
working-directory: ./web
run: |
composer install
composer dump-autoload
- name: Install NODE Dependencies
working-directory: ./web
run: |
npm install
npm run build
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v3.x
- name: Zip the files
working-directory: ./web
run: |
rm -rf .git
rm -rf .github
rm -rf .nmp
rm -rf node_modules
rm -rf .phpunit.cache
rm -rf vendor/composer/tmp-*.zip
find . \( -name ".git" -o -name ".gitignore" -o -name ".gitmodules" -o -name ".gitattributes" \) -exec rm -rf -- {} +
zip -r phyre-web-panel-build.zip `ls -A`
mkdir -p ../dist
mv ./phyre-web-panel-build.zip ../dist/phyre-web-panel.zip
- name: Pushes to Phyre Panel Dist Repo
uses: cpina/github-action-push-to-another-repository@main
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
with:
source-directory: './dist'
destination-github-username: 'CloudVisionApps'
destination-repository-name: 'PhyrePanelWebDist'
user-email: bobicloudvision@gmail.com
target-branch: main

View File

@ -0,0 +1,42 @@
name: Compile Phyre Web Terminal Package
on:
workflow_dispatch:
push:
# Pattern matched against refs/tags
tags:
- '**'
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- name: Compile WEB Terminal Package
run: |
cd compilators/debian/web-terminal
chmod 775 ./web-terminal-compile.sh
./web-terminal-compile.sh
ls
- name: Pushes to Phyre Panel Dist Repo
uses: cpina/github-action-push-to-another-repository@main
env:
SSH_DEPLOY_KEY: ${{ secrets.SSH_DEPLOY_KEY }}
API_TOKEN_GITHUB: ${{ secrets.API_TOKEN_GITHUB }}
with:
source-directory: './compilators/debian/web-terminal/dist'
target-directory: './debian/web-terminal/dist'
destination-github-username: 'CloudVisionApps'
destination-repository-name: 'PhyrePanelWebTerminalDist'
user-email: bobicloudvision@gmail.com
target-branch: main

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
web/storage/installed
/docker/e2e-tests/node_modules/
compilators/
web/thirdparty/

View File

@ -1 +1,43 @@
# PhyrePanel
# PHYRE - WEB HOSTING PANEL
![InstallScreen](screenshots/install-screen.png)
![Dashboard](screenshots/dashboard.png)
## Introduction
PhyrePanel is a web-based panel for linux. It is written in PHP and uses the Laravel framework.
## Installation
To install PhyrePanel, you need to run this commands:
```
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/install.sh && chmod +x install.sh && ./install.sh
```
The admin panel can be opened on port: yourserver.com:8443
## Features
### Server Applications
Apache + PHP 7.4, 8.0, 8.1, 8.3, 8.4
Apache + NGINX
Apache + Python
Apache + Ruby
### Databases
MySQL
SQLITE
Remote Database Connections
### UI/UX / Developing
Clean Admin Panel
Server Clustering
Easy Module Developing
## Join to our discord group
https://discord.gg/yfFWfrfwTZ

View File

@ -0,0 +1 @@
ls

19
bin/cron-job-add.sh Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
username=$1
schedule=$2
command=$3
# Create a temporary file to hold the existing user's crontab
crontab -u $username -l > /tmp/temp_crontab
# Add a new cron job to the temporary file
echo "$schedule $command" >> /tmp/temp_crontab
# Install the modified crontab from the temporary file
crontab -u $username /tmp/temp_crontab
# Remove the temporary file
rm /tmp/temp_crontab
echo "done!"

10
bin/cron-job-delete.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
username=$1
schedule=$2
command=$3
# Get the user's crontab, filter out the specific command and schedule, and install the updated crontab
crontab -u $username -l | grep -v -F "$schedule $command" | crontab -u $username -
echo "done!"

7
bin/cron-jobs-list.sh Normal file
View File

@ -0,0 +1,7 @@
#!/bin/bash
# Replace 'username' with the actual username you want to retrieve cron jobs for
username=$1
# Get the user's crontab entries and convert them to JSON
crontab -u $username -l | grep -v -e '^#' -e '^\s*$' | awk '{print "{\"schedule\":\"" $1 " " $2 " " $3 " " $4 " " $5 "\", \"command\":\"" substr($0, index($0,$6)) "\"}"}' | jq -s .

View File

@ -0,0 +1,22 @@
#!/bin/bash
echo "Creating MySQL user and database"
PASS=$3
if [ -z "$3" ]; then
PASS=`openssl rand -base64 8`
fi
mysql -u root <<MYSQL_SCRIPT
CREATE DATABASE $1;
CREATE USER '$2'@'localhost' IDENTIFIED BY '$PASS';
GRANT ALL PRIVILEGES ON $1.* TO '$2'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
echo "MySQL user and database created."
echo "Database: $1"
echo "Username: $2"
echo "Password: $PASS"
echo "Success!"

View File

@ -0,0 +1,34 @@
#!/bin/bash
DOMAIN=$1
USER=$2
# Path to NGINX sites-available directory
SITES_AVAILABLE_DIR="/etc/nginx/sites-available"
SITES_ENABLED_DIR="/etc/nginx/sites-enabled"
# Create the site
SERVER_ROOT="/var/www/$DOMAIN/public_html"
cp -f /usr/local/phyre/samples/ubuntu/nginx.conf.sample $SITES_AVAILABLE_DIR/$DOMAIN.conf
ln -s $SITES_AVAILABLE_DIR/$DOMAIN.conf $SITES_ENABLED_DIR/$DOMAIN.conf
mkdir -p $SERVER_ROOT
chown -R www-data:www-data $SERVER_ROOT
# Replace the domain name in the NGINX config
sed -i "s/%SERVER_NAME%/${DOMAIN}/g" $SITES_AVAILABLE_DIR/$DOMAIN.conf
sed -i "s/%USER%/${USER}/g" $SITES_AVAILABLE_DIR/$DOMAIN.conf
SERVER_ROOT_ESCAPED=$(printf '%s\n' "$SERVER_ROOT" | sed -e 's/[\/&]/\\&/g')
sed -i "s#%SERVER_ROOT%#${SERVER_ROOT_ESCAPED}#g" $SITES_AVAILABLE_DIR/$DOMAIN.conf
cp -f /usr/local/phyre/samples/sample-website-index.html $SERVER_ROOT/index.html
sed -i "s/%DOMAIN%/${DOMAIN}/g" $SERVER_ROOT/index.html
# Reload NGINX
service nginx reload
echo "Created site $DOMAIN"
echo "done!"

View File

@ -0,0 +1,16 @@
#!/bin/bash
# Path to NGINX sites-available directory
SITES_AVAILABLE_DIR="/etc/nginx/sites-available"
SITES_ENABLED_DIR="/etc/nginx/sites-enabled"
# Delete the site
rm -rf $SITES_AVAILABLE_DIR/$1.conf
rm -rf $SITES_ENABLED_DIR/$1.conf
rm -rf /var/www/$1
# Reload NGINX
service nginx reload
echo "Deleted site $1"
echo "done!"

View File

@ -0,0 +1,24 @@
#!/bin/bash
# Path to NGINX sites-available directory
sites_available_dir="/etc/nginx/sites-available"
# Array to hold site configurations
declare -a sites_array=()
# Loop through NGINX site configuration files and collect data
for file in "$sites_available_dir"/*; do
if [ -f "$file" ] && [ "$(basename "$file")" != "default" ]; then
server_name=$(awk '$1 == "server_name" {gsub(/;/, "", $2); print $2; exit}' "$file")
root=$(awk '$1 == "root" {gsub(/;/, "", $2); print $2; exit}' "$file")
# Append site data to the array
sites_array+=("{\"file\": \"$file\", \"server_name\": \"$server_name\", \"root\": \"$root\"}")
fi
done
# Convert array to JSON
json_output=$(printf '%s\n' "${sites_array[@]}" | jq -s '.')
# Output JSON
echo "$json_output"

13
docker-compose.yml Normal file
View File

@ -0,0 +1,13 @@
services:
phyre:
build:
context: ./docker
dockerfile: Dockerfile
ports:
- "80:80"
- "443:443"
- "3306:3306"
- "8443:8443"
volumes:
- .:/usr/local/phyre

34
docker/.dockerignore Normal file
View File

@ -0,0 +1,34 @@
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/
**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

116
docker/Dockerfile Normal file
View File

@ -0,0 +1,116 @@
# syntax=docker/dockerfile:1
FROM ubuntu:22.04
LABEL maintainer="CloudVisionApps1"
# Install initial dependencies
RUN apt-get update \
&& apt-get install -y wget dos2unix ca-certificates \
&& apt-get install -y \
jq \
curl \
wget \
unzip \
zip \
tar \
mysql-common \
mysql-server \
mysql-client \
lsb-release \
gnupg2 \
ca-certificates \
apt-transport-https \
software-properties-common \
supervisor \
libonig-dev \
libsodium23 \
libpq5 \
libzip-dev \
libcurl4-openssl-dev \
libssl-dev \
zlib1g-dev
# Start MySQL service
RUN service mysql start
# Set up environment variables
ENV INSTALL_DIR="/phyre/install" \
MYSQL_PHYRE_ROOT_USERNAME="phyre" \
PHYRE_PHP="/usr/local/phyre/php/bin/php"
# Create installation directory
RUN mkdir -p $INSTALL_DIR
# Change directory to installation directory
WORKDIR $INSTALL_DIR
# Install PHYRE PHP
RUN wget https://github.com/CloudVisionApps/PhyrePanelPHPDist/raw/main/debian/php/dist/phyre-php-8.2.0.deb \
&& dpkg -i phyre-php-8.2.0.deb
# Install PHYRE NGINX
RUN wget https://github.com/CloudVisionApps/PhyrePanelNginxDist/raw/main/debian/nginx/dist/phyre-nginx-1.24.0.deb \
&& dpkg -i phyre-nginx-1.24.0.deb
# Start PHYRE service
RUN service phyre start
# Create symbolic link for PHYRE PHP
RUN ln -s $PHYRE_PHP /usr/bin/phyre-php
# Set permissions
RUN chmod 711 /home \
&& chmod -R 750 /usr/local/phyre
# Change directory to web directory
WORKDIR /usr/local/phyre/web
# Create MySQL user
RUN mysql -uroot -proot -e "CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD'; \
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION; \
FLUSH PRIVILEGES;"
# Create database
RUN PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)" \
&& PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)" \
&& PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)" \
&& mysql -uroot -proot -e "CREATE DATABASE $PANEL_DB_NAME; \
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD'; \
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost'; \
FLUSH PRIVILEGES;"
# Secure MySQL installation
RUN mysql_secure_installation --use-default
# Change MySQL root password
RUN MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)" \
&& mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD'; \
FLUSH PRIVILEGES;"
# Save MySQL root password
RUN echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
RUN cp .env.example .env \
&& sed -i "s/^APP_URL=.*/APP_URL=http://127.0.0.1:8443/" .env \
&& sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env \
&& sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env \
&& sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env \
&& sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env \
&& sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env \
&& sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env \
&& sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
# Generate application key and migrate database
RUN phyre-php artisan key:generate \
&& phyre-php artisan migrate \
&& phyre-php artisan db:seed
# Set permissions for storage and cache directories
RUN chmod -R o+w /usr/local/phyre/web/storage/ \
&& chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
# Get current IP address
RUN CURRENT_IP=$(curl -s ipinfo.io/ip) \
&& echo "PhyrePanel downloaded successfully." \
&& echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

17
docker/README.Docker.md Normal file
View File

@ -0,0 +1,17 @@
### Building and running your application
When you're ready, start your application by running:
`docker compose up --build`.
### Deploying your application to the cloud
First, build your image, e.g.: `docker build -t myapp .`.
If your cloud uses a different CPU architecture than your development
machine (e.g., you are on a Mac M1 and your cloud provider is amd64),
you'll want to build the image for that platform, e.g.:
`docker build --platform=linux/amd64 -t myapp .`.
Then, push it to your registry, e.g. `docker push myregistry.com/myapp`.
Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/)
docs for more detail on building and pushing.

22
docker/compose.yaml Normal file
View File

@ -0,0 +1,22 @@
# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Docker compose reference guide at
# https://docs.docker.com/go/compose-spec-reference/
# Here the instructions define your application as a service called "app".
# This service is built from the Dockerfile in the current directory.
# You can add other services your application may depend on here, such as a
# database or a cache. For examples, see the Awesome Compose repository:
# https://github.com/docker/awesome-compose
services:
app:
platform: linux/amd64
container_name: phyrepanel
build:
context: .
# If your application exposes a port, uncomment the following lines and change
# the port numbers as needed. The first number is the host port and the second
# is the port inside the container.
ports:
- 8080:8080
- 8443:8443
- 3306:3306

147
docker/docker-entrypoint.sh Normal file
View File

@ -0,0 +1,147 @@
#!/bin/bash
service phyre start
apt-get update && apt-get install -y wget dos2unix
#
#apt-get install libsodium-dev -y
#
#wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/install.sh && chmod +x install.sh && ./install.sh
#
#ls -la
#curl http://localhost:8443
#tail -f /dev/null
#chmod +x /usr/local/phyre/installers/Ubuntu/22.04/install.sh
#dos2unix /usr/local/phyre/installers/Ubuntu/22.04/install.sh
#bash +x /usr/local/phyre/installers/Ubuntu/22.04/install.sh
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/Ubuntu/22.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHPDist/raw/main/debian/php/dist/phyre-php-8.2.0.deb
dpkg -i phyre-php-8.2.0.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNginxDist/raw/main/debian/nginx/dist/phyre-nginx-1.24.0.deb
dpkg -i phyre-nginx-1.24.0.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php
chmod 711 /home
chmod -R 750 /usr/local/phyre
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=http://127.0.0.1:8443/" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

View File

@ -0,0 +1,76 @@
#!/bin/bash
apt-get update && apt-get install -y wget
#
#apt-get install libsodium-dev -y
#
#wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/install.sh && chmod +x install.sh && ./install.sh
#
#ls -la
#curl http://localhost:8443
#tail -f /dev/null
cd e2e-tests
apt-get update && \
apt-get install --no-install-recommends -y \
libgtk2.0-0 \
libgtk-3-0 \
libnotify-dev \
libgconf-2-4 \
libgbm-dev \
libnss3 \
libxss1 \
libasound2 \
libxtst6 \
xauth \
xvfb \
ttf-wqy-zenhei \
ttf-wqy-microhei \
xfonts-wqy \
fonts-liberation \
libgbm1 \
libu2f-udev \
libvulkan1
rm -rf /var/lib/apt/lists/*
add-apt-repository ppa:webupd8team/y-ppa-manager
apt-get update
apt-get install y-ppa-manager
# Get Chrome
wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | apt-key add -
sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
apt-get update
apt-get install -y google-chrome-stable
/usr/bin/google-chrome
apt install -y curl
wget https://deb.nodesource.com/setup_20.x
mv setup_20.x /tmp/nodesource_setup.sh
bash /tmp/nodesource_setup.sh
apt --fix-broken install -y
apt install nodejs -y
apt install npm -y
npm install -g npm@latest --force
npm --version
npm install -g yarn@latest --force
yarn --version
npm install
npm run test

View File

@ -0,0 +1,9 @@
const { defineConfig } = require("cypress");
module.exports = defineConfig({
e2e: {
setupNodeEvents(on, config) {
// implement node event listeners here
},
},
});

View File

@ -0,0 +1,7 @@
describe('Try to access admin panel', () => {
it('Go to admin panel', () => {
cy.visit('http://localhost:8443')
})
})

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add('login', (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })

View File

@ -0,0 +1,20 @@
// ***********************************************************
// This example support/e2e.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@ -0,0 +1,14 @@
{
"name": "e2e-tests",
"version": "1.0.0",
"description": "",
"main": "cypress.config.js",
"scripts": {
"test": "cypress run"
},
"author": "",
"license": "ISC",
"devDependencies": {
"cypress": "^13.7.3"
}
}

26
docker/reinstall.sh Executable file
View File

@ -0,0 +1,26 @@
# stop all containers
docker stop $(docker ps -aq)
# remove all containers
docker rm -f $(docker ps -aq)
#remove all images
docker image rm $(docker images -q)
#remove all unused containers, networks, images, and volumes
docker system prune -f
#remove all unused volumes
docker volume prune -f
#remove all unused networks
docker network prune -f
#remove all builds
docker builder prune -f
#remove all completed builds
docker builder prune -a -f
# install new
docker compose up -d

3
docker/update.sh Executable file
View File

@ -0,0 +1,3 @@
docker compose down
docker compose up -d
docker update --restart unless-stopped $(docker ps -q)

View File

@ -0,0 +1,13 @@
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo " \
____ _ ___ ______ _____ ____ _ _ _ _____ _
| _ \| | | \ \ / / _ \| ____| | _ \ / \ | \ | | ____| |
| |_) | |_| |\ V /| |_) | _| | |_) / _ \ | \| | _| | |
| __/| _ | | | | _ <| |___ | __/ ___ \| |\ | |___| |___
|_| |_| |_| |_| |_| \_\_____| |_| /_/ \_\_| \_|_____|_____
WELCOME TO PHYRE PANEL!
You can login at: http://$CURRENT_IP:8443
"
# File can be saved at: /etc/profile.d/greeting.sh

View File

@ -0,0 +1,127 @@
#!/bin/bash
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libsodium23"
"libpq5"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/Ubuntu/22.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHPDist/raw/main/debian/php/dist/phyre-php-8.2.0.deb
dpkg -i phyre-php-8.2.0.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNginxDist/raw/main/debian/nginx/dist/phyre-nginx-1.24.0.deb
dpkg -i phyre-nginx-1.24.0.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
unzip -qq -o phyre-web-panel.zip -d /usr/local/phyre/web
rm -rf phyre-web-panel.zip
chmod 711 /home
chmod -R 750 /usr/local/phyre
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=http://127.0.0.1:8443/" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

View File

@ -0,0 +1,210 @@
#!/bin/bash
MAIN_DIR="/phyre/raw-repo"
apt-get update && apt-get install ca-certificates
apt install -y git
cd /
mkdir -p $MAIN_DIR
git clone https://github.com/CloudVisionApps/PhyrePanel.git $MAIN_DIR
HELPERS_DIR=$MAIN_DIR"/shell/helpers/ubuntu"
. $HELPERS_DIR"/common.sh"
. $HELPERS_DIR"/create-mysql-db-and-user.sh"
# Create the new phyreweb user
random_password="$(openssl rand -base64 32)"
email="admin@phyrepanel.com"
# Create the new phyreweb user
/usr/sbin/useradd "phyreweb" -c "$email" --no-create-home
# Add the phyreweb user to the www-data group
sudo usermod -a -G www-data phyreweb
# Add the root user to the www-data group
#sudo usermod -a -G www-data root
# do not allow login into phyreweb user
echo phyreweb:$random_password | sudo chpasswd -e
mkdir -p /etc/sudoers.d
cp -f $MAIN_DIR/installers/Ubuntu/22.04/sudo/phyreweb /etc/sudoers.d/
chmod 440 /etc/sudoers.d/phyreweb
# Update the system
apt update -y
REPOSITORIES_LIST=(
"ppa:ondrej/php"
)
# Check if the repositories are installed
for REPOSITORY in "${REPOSITORIES_LIST[@]}"; do
add-apt-repository -y $REPOSITORY
done
DEPENDENCIES_LIST=(
"jq"
"curl"
"wget"
"git"
"apache2"
"apache2-suexec-custom"
"nodejs"
"npm"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libssl-dev"
"zlib1g-dev"
"libapache2-mod-ruid2"
# "libapache2-mod-fcgid"
# "libapache2-mod-php8.1"
"libapache2-mod-php8.2"
# "libapache2-mod-php8.3"
# "php7.4"
# "php7.4-fpm"
# "php7.4-{bcmath,xml,bz2,intl,curl,dom,fileinfo,gd,intl,mbstring,mysql,opcache,sqlite3,xmlrpc,zip}"
# "php8.1"
# "php8.1-fpm"
# "php8.1-{bcmath,xml,bz2,intl,curl,dom,fileinfo,gd,intl,mbstring,mysql,opcache,sqlite3,xmlrpc,zip}"
"php8.2"
"php8.2-fpm"
"php8.2-{bcmath,xml,bz2,intl,curl,dom,fileinfo,gd,intl,mbstring,mysql,opcache,sqlite3,xmlrpc,zip}"
# "php8.3"
# "php8.3-fpm"
# "php8.3-{bcmath,xml,bz2,intl,curl,dom,fileinfo,gd,intl,mbstring,mysql,opcache,sqlite3,xmlrpc,zip}"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
if ! command_is_installed $DEPENDENCY; then
echo "Dependency $DEPENDENCY is not installed."
echo "Installing $DEPENDENCY..."
apt install -y $DEPENDENCY
else
echo "Dependency $DEPENDENCY is installed."
fi
done
sudo a2enmod php8.2
sudo a2enmod rewrite
sudo a2enmod env
sudo a2enmod ssl
sudo a2enmod actions
sudo a2enmod headers
sudo a2enmod suexec
sudo a2enmod ruid2
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enconf ssl-params
#sudo a2enmod fcgid
#sudo a2enmod alias
#sudo a2enmod proxy_fcgi
#sudo a2enmod setenvif
sudo ufw allow in "Apache Full"
#sudo a2ensite default-ssl
#sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/ssl/private/apache-selfsigned.key -out /etc/ssl/certs/apache-selfsigned.crt
systemctl restart apache2
#DEPENDENCIES_FOR_REMOVE_LIST=(
# "apache2"
#)
## Check if the dependencies are installed
#for DEPENDENCY in "${DEPENDENCIES_FOR_REMOVE_LIST[@]}"; do
# if command_is_installed $DEPENDENCY; then
# echo "Dependency $DEPENDENCY is installed."
# echo "Removing $DEPENDENCY..."
# apt purge -y $DEPENDENCY
# apt autoremove -y
# fi
#done
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHPDist/raw/main/debian/php/dist/phyre-php-8.2.0.deb
sudo dpkg -i phyre-php-8.2.0.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNginxDist/raw/main/debian/nginx/dist/phyre-nginx-1.24.0.deb
sudo dpkg -i phyre-nginx-1.24.0.deb
# sudo ufw allow proto tcp from any to any port 80,443
# Run Nginx
#systemctl start nginx
#systemctl enable nginx
service phyre start
# Change NGINX index.html
rm -rf /var/www/html/*
cp $MAIN_DIR/samples/sample-index.html /var/www/html/index.html
# Restart NGINX
#systemctl restart nginx
PHYRE_PHP=/usr/local/phyre/php/bin/php
mkdir -p /usr/local/phyre/web
cp -r $MAIN_DIR/web/* /usr/local/phyre/web
cp $MAIN_DIR/web/.env.example /usr/local/phyre/web/.env.example
mkdir -p /usr/local/phyre/bin
cp -r $MAIN_DIR/bin/* /usr/local/phyre/bin
cp -r $MAIN_DIR/samples/* /usr/local/phyre/samples
mkdir -p /usr/local/phyre/update
cp -r $MAIN_DIR/update/* /usr/local/phyre/update
chmod +x /usr/local/phyre/update/*
# Install Composer
cd /usr/local/phyre/web
$PHYRE_PHP -v
$PHYRE_PHP -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
$PHYRE_PHP ./composer-setup.php
$PHYRE_PHP -r "unlink('composer-setup.php');"
COMPOSER_ALLOW_SUPERUSER=1 $PHYRE_PHP ./composer.phar install --no-interaction
# Create database
PANEL_DB_NAME="phyredb"
PANEL_DB_USER="phyreuser"
PANEL_DB_PASSWORD="phyrepass"
create_mysql_db_and_user $PANEL_DB_NAME $PANEL_DB_USER $PANEL_DB_PASSWORD
# Configure the application
cp .env.example .env
sed -i "s/^APP_NAME=.*/APP_NAME=PhyrePanel/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
$PHYRE_PHP artisan key:generate
$PHYRE_PHP artisan migrate
$PHYRE_PHP artisan db:seed
sudo chmod -R o+w /usr/local/phyre/web/storage/
sudo chmod -R o+w /usr/local/phyre/web/bootstrap/cache/

View File

@ -0,0 +1,6 @@
Defaults:root !requiretty
# sudo is limited to PhyrePanel scripts
# phyreweb ALL=NOPASSWD:/usr/local/phyre/bin/*
phyreweb ALL=(ALL) NOPASSWD: ALL

View File

@ -0,0 +1,24 @@
# Compile ubuntu-20.04 installers
# get content from file
INSTALL_BASE=$(cat ubuntu-20.04/install-partial/install_base.sh)
DOWNLOAD_WEB=$(cat ubuntu-20.04/install-partial/download_web.sh)
INSTALL_WEB=$(cat ubuntu-20.04/install-partial/install_web.sh)
# create installer
echo "$INSTALL_BASE" >> ubuntu-20.04/install.sh
echo "$DOWNLOAD_WEB" >> ubuntu-20.04/install.sh
echo "$INSTALL_WEB" >> ubuntu-20.04/install.sh
# Compile ubuntu-22.04 installers
# get content from file
INSTALL_BASE=$(cat ubuntu-22.04/install-partial/install_base.sh)
DOWNLOAD_WEB=$(cat ubuntu-22.04/install-partial/download_web.sh)
INSTALL_WEB=$(cat ubuntu-22.04/install-partial/install_web.sh)
# create installer
echo "$INSTALL_BASE" >> ubuntu-22.04/install.sh
echo "$DOWNLOAD_WEB" >> ubuntu-22.04/install.sh
echo "$INSTALL_WEB" >> ubuntu-22.04/install.sh

60
installers/install.sh Normal file
View File

@ -0,0 +1,60 @@
#!/bin/bash
# Check if the user is root
if [[ $EUID -ne 0 ]]; then
echo "This script must be run as root. Exiting..."
exit 1
fi
# Check if the user is running a 64-bit system
if [[ $(uname -m) != "x86_64" ]]; then
echo "This script must be run on a 64-bit system. Exiting..."
exit 1
fi
# Check if the user is running a supported shell
if [[ $(echo $SHELL) != "/bin/bash" ]]; then
echo "This script must be run on a system running Bash. Exiting..."
exit 1
fi
# Check if the user is running a supported OS
if [[ $(uname -s) != "Linux" ]]; then
echo "This script must be run on a Linux system. Exiting..."
exit 1
fi
# Check if the user is running a supported distro
if [[ $(cat /etc/os-release | grep -w "ID_LIKE" | cut -d "=" -f 2) != "debian" ]]; then
echo "This script must be run on a Debian-based system. Exiting..."
exit 1
fi
# Check if the user is running a supported distro version
DISTRO_VERSION=$(cat /etc/os-release | grep -w "VERSION_ID" | cut -d "=" -f 2)
DISTRO_VERSION=${DISTRO_VERSION//\"/} # Remove quotes from version string
DISTRO_NAME=$(cat /etc/os-release | grep -w "NAME" | cut -d "=" -f 2)
DISTRO_NAME=${DISTRO_NAME//\"/} # Remove quotes from name string
# Lowercase the distro name
DISTRO_NAME=$(echo $DISTRO_NAME | tr '[:upper:]' '[:lower:]')
INSTALLER_URL="https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/${DISTRO_NAME}-${DISTRO_VERSION}/install.sh"
INSTALLER_CONTENT=$(wget ${INSTALLER_URL} 2>&1)
if [[ "$INSTALLER_CONTENT" =~ 404\ Not\ Found ]]; then
echo "PhyrePanel not supporting this version of distribution"
echo "Distro: ${DISTRO_NAME} Version: ${DISTRO_VERSION}"
echo "Exiting..."
exit 1
fi
# Check is PHYRE is already installed
if [ -d "/usr/local/phyre" ]; then
echo "PhyrePanel is already installed. Exiting..."
exit 0
fi
wget $INSTALLER_URL -O ./phyre-installer.sh
chmod +x ./phyre-installer.sh
bash ./phyre-installer.sh

View File

@ -0,0 +1,14 @@
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo " \
____ _ ___ ______ _____ ____ _ _ _ _____ _
| _ \| | | \ \ / / _ \| ____| | _ \ / \ | \ | | ____| |
| |_) | |_| |\ V /| |_) | _| | |_) / _ \ | \| | _| | |
| __/| _ | | | | _ <| |___ | __/ ___ \| |\ | |___| |___
|_| |_| |_| |_| |_| \_\_____| |_| /_/ \_\_| \_|_____|_____
WELCOME TO PHYRE PANEL!
OS: Ubuntu 20.04
You can login at: http://$CURRENT_IP:8443
"
# File can be saved at: /etc/profile.d/greeting.sh

View File

@ -0,0 +1,8 @@
#!/bin/bash
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
unzip -qq -o phyre-web-panel.zip -d /usr/local/phyre/web
rm -rf phyre-web-panel.zip
chmod 711 /home
chmod -R 750 /usr/local/phyre

View File

@ -0,0 +1,59 @@
#!/bin/bash
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
apt-get upgrade -y
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"openssl"
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libsodium23"
"libpq5"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/ubuntu-20.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHP/raw/main/compilators/debian/php/dist/phyre-php-8.2.0-ubuntu-20.04.deb
dpkg -i phyre-php-8.2.0-ubuntu-20.04.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNGINX/raw/main/compilators/debian/nginx/dist/phyre-nginx-1.24.0-ubuntu-20.04.deb
dpkg -i phyre-nginx-1.24.0-ubuntu-20.04.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php

View File

@ -0,0 +1,70 @@
#!/bin/bash
# Check dir exists
if [ ! -d "/usr/local/phyre/web" ]; then
echo "PhyrePanel directory not found."
return 1
fi
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=127.0.0.1:8443" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

View File

@ -0,0 +1,137 @@
#!/bin/bash
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
apt-get upgrade -y
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"openssl"
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libsodium23"
"libpq5"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/ubuntu-20.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHP/raw/main/compilators/debian/php/dist/phyre-php-8.2.0-ubuntu-20.04.deb
dpkg -i phyre-php-8.2.0-ubuntu-20.04.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNGINX/raw/main/compilators/debian/nginx/dist/phyre-nginx-1.24.0-ubuntu-20.04.deb
dpkg -i phyre-nginx-1.24.0-ubuntu-20.04.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php
#!/bin/bash
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
unzip -qq -o phyre-web-panel.zip -d /usr/local/phyre/web
rm -rf phyre-web-panel.zip
chmod 711 /home
chmod -R 750 /usr/local/phyre
#!/bin/bash
# Check dir exists
if [ ! -d "/usr/local/phyre/web" ]; then
echo "PhyrePanel directory not found."
return 1
fi
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=127.0.0.1:8443" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

View File

@ -0,0 +1,14 @@
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo " \
____ _ ___ ______ _____ ____ _ _ _ _____ _
| _ \| | | \ \ / / _ \| ____| | _ \ / \ | \ | | ____| |
| |_) | |_| |\ V /| |_) | _| | |_) / _ \ | \| | _| | |
| __/| _ | | | | _ <| |___ | __/ ___ \| |\ | |___| |___
|_| |_| |_| |_| |_| \_\_____| |_| /_/ \_\_| \_|_____|_____
WELCOME TO PHYRE PANEL!
OS: Ubuntu 22.04
You can login at: http://$CURRENT_IP:8443
"
# File can be saved at: /etc/profile.d/greeting.sh

View File

@ -0,0 +1,8 @@
#!/bin/bash
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
unzip -qq -o phyre-web-panel.zip -d /usr/local/phyre/web
rm -rf phyre-web-panel.zip
chmod 711 /home
chmod -R 750 /usr/local/phyre

View File

@ -0,0 +1,59 @@
#!/bin/bash
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
apt-get upgrade -y
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"openssl"
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libsodium23"
"libpq5"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/ubuntu-22.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHP/raw/main/compilators/debian/php/dist/phyre-php-8.2.0-ubuntu-22.04.deb
dpkg -i phyre-php-8.2.0-ubuntu-22.04.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNGINX/raw/main/compilators/debian/nginx/dist/phyre-nginx-1.24.0-ubuntu-22.04.deb
dpkg -i phyre-nginx-1.24.0-ubuntu-22.04.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php

View File

@ -0,0 +1,70 @@
#!/bin/bash
# Check dir exists
if [ ! -d "/usr/local/phyre/web" ]; then
echo "PhyrePanel directory not found."
return 1
fi
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=127.0.0.1:8443" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

View File

@ -0,0 +1,137 @@
#!/bin/bash
INSTALL_DIR="/phyre/install"
apt-get update && apt-get install ca-certificates
apt-get upgrade -y
mkdir -p $INSTALL_DIR
cd $INSTALL_DIR
DEPENDENCIES_LIST=(
"openssl"
"jq"
"curl"
"wget"
"unzip"
"zip"
"tar"
"mysql-common"
"mysql-server"
"mysql-client"
"lsb-release"
"gnupg2"
"ca-certificates"
"apt-transport-https"
"software-properties-common"
"supervisor"
"libonig-dev"
"libzip-dev"
"libcurl4-openssl-dev"
"libsodium23"
"libpq5"
"libssl-dev"
"zlib1g-dev"
)
# Check if the dependencies are installed
for DEPENDENCY in "${DEPENDENCIES_LIST[@]}"; do
apt install -y $DEPENDENCY
done
# Start MySQL
service mysql start
wget https://raw.githubusercontent.com/CloudVisionApps/PhyrePanel/main/installers/ubuntu-22.04/greeting.sh
mv greeting.sh /etc/profile.d/phyre-greeting.sh
# Install PHYRE PHP
wget https://github.com/CloudVisionApps/PhyrePanelPHP/raw/main/compilators/debian/php/dist/phyre-php-8.2.0-ubuntu-22.04.deb
dpkg -i phyre-php-8.2.0-ubuntu-22.04.deb
# Install PHYRE NGINX
wget https://github.com/CloudVisionApps/PhyrePanelNGINX/raw/main/compilators/debian/nginx/dist/phyre-nginx-1.24.0-ubuntu-22.04.deb
dpkg -i phyre-nginx-1.24.0-ubuntu-22.04.deb
service phyre start
PHYRE_PHP=/usr/local/phyre/php/bin/php
ln -s $PHYRE_PHP /usr/bin/phyre-php
#!/bin/bash
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
unzip -qq -o phyre-web-panel.zip -d /usr/local/phyre/web
rm -rf phyre-web-panel.zip
chmod 711 /home
chmod -R 750 /usr/local/phyre
#!/bin/bash
# Check dir exists
if [ ! -d "/usr/local/phyre/web" ]; then
echo "PhyrePanel directory not found."
return 1
fi
# Go to web directory
cd /usr/local/phyre/web
# Create MySQL user
MYSQL_PHYRE_ROOT_USERNAME="phyre"
MYSQL_PHYRE_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE USER '$MYSQL_PHYRE_ROOT_USERNAME'@'%' IDENTIFIED BY '$MYSQL_PHYRE_ROOT_PASSWORD';
GRANT ALL PRIVILEGES ON *.* TO '$MYSQL_PHYRE_ROOT_USERNAME'@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Create database
PANEL_DB_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
PANEL_DB_NAME="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
PANEL_DB_USER="phyre$(tr -dc a-za-z0-9 </dev/urandom | head -c 13; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
CREATE DATABASE $PANEL_DB_NAME;
CREATE USER '$PANEL_DB_USER'@'localhost' IDENTIFIED BY '$PANEL_DB_PASSWORD';
GRANT ALL PRIVILEGES ON $PANEL_DB_NAME.* TO '$PANEL_DB_USER'@'localhost';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
mysql_secure_installation --use-default
# Change mysql root password
MYSQL_ROOT_PASSWORD="$(tr -dc a-za-z0-9 </dev/urandom | head -c 32; echo)"
mysql -uroot -proot <<MYSQL_SCRIPT
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password by '$MYSQL_ROOT_PASSWORD';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
# Save mysql root password
echo "$MYSQL_ROOT_PASSWORD" > /root/.phyre_mysql_root_password
# Configure the application
cp .env.example .env
sed -i "s/^APP_URL=.*/APP_URL=127.0.0.1:8443" .env
sed -i "s/^APP_NAME=.*/APP_NAME=PHYRE_PANEL/" .env
sed -i "s/^DB_DATABASE=.*/DB_DATABASE=$PANEL_DB_NAME/" .env
sed -i "s/^DB_USERNAME=.*/DB_USERNAME=$PANEL_DB_USER/" .env
sed -i "s/^DB_PASSWORD=.*/DB_PASSWORD=$PANEL_DB_PASSWORD/" .env
sed -i "s/^DB_CONNECTION=.*/DB_CONNECTION=mysql/" .env
sed -i "s/^MYSQl_ROOT_USERNAME=.*/MYSQl_ROOT_USERNAME=$MYSQL_ROOT_USERNAME/" .env
sed -i "s/^MYSQL_ROOT_PASSWORD=.*/MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD/" .env
phyre-php artisan key:generate
phyre-php artisan migrate
phyre-php artisan db:seed
chmod -R o+w /usr/local/phyre/web/storage/
chmod -R o+w /usr/local/phyre/web/bootstrap/cache/
CURRENT_IP=$(curl -s ipinfo.io/ip)
echo "PhyrePanel downloaded successfully."
echo "Please visit http://$CURRENT_IP:8443 to continue installation of the panel."

159
samples/sample-index.html Normal file
View File

@ -0,0 +1,159 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex flex-col bg-gray-50 justify-center h-screen text-center">
<div>
<div class="mb-4">
<svg class="w-[26rem] m-auto" id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 808.52 192.65">
<defs>
<style>
.cls-1 {
fill: url(#linear-gradient-12);
}
.cls-1, .cls-2, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-8, .cls-9, .cls-10, .cls-11, .cls-12, .cls-13 {
stroke-width: 0px;
}
.cls-2 {
fill: url(#linear-gradient);
}
.cls-3 {
fill: url(#linear-gradient-6);
}
.cls-4 {
fill: url(#linear-gradient-11);
}
.cls-5 {
fill: url(#linear-gradient-5);
}
.cls-6 {
fill: #f9be22;
}
.cls-7 {
fill: url(#linear-gradient-2);
}
.cls-8 {
fill: url(#linear-gradient-7);
}
.cls-9 {
fill: url(#linear-gradient-3);
}
.cls-10 {
fill: url(#linear-gradient-9);
}
.cls-11 {
fill: url(#linear-gradient-8);
}
.cls-12 {
fill: url(#linear-gradient-10);
}
.cls-13 {
fill: url(#linear-gradient-4);
}
</style>
<linearGradient id="linear-gradient" x1="5.89" y1="67.83" x2="786.82" y2="71" gradientUnits="userSpaceOnUse">
<stop offset=".25" stop-color="#f8bd20"/>
<stop offset=".41" stop-color="#f9c628"/>
<stop offset=".69" stop-color="#fbd131"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="694.68" y1="13.95" x2="808.52" y2="13.95" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20"/>
<stop offset=".63" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="642.49" y1="69.62" x2="785.47" y2="69.62" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20" stop-opacity=".9"/>
<stop offset=".15" stop-color="rgba(249, 197, 39, .94)" stop-opacity=".94"/>
<stop offset=".37" stop-color="rgba(250, 206, 47, .97)" stop-opacity=".97"/>
<stop offset=".63" stop-color="rgba(251, 211, 51, .99)" stop-opacity=".99"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-4" x1="647.99" y1="69.56" x2="785.96" y2="69.56" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20"/>
<stop offset=".63" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-5" x1="161.7" y1="69.56" x2="212.2" y2="69.56" gradientUnits="userSpaceOnUse">
<stop offset=".45" stop-color="#f8bd20"/>
<stop offset=".54" stop-color="#f9c326"/>
<stop offset=".78" stop-color="#fbd031"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-6" x1="-14.8" y1="68.54" x2="812.93" y2="71.9" gradientUnits="userSpaceOnUse">
<stop offset=".23" stop-color="#f8bd20"/>
<stop offset=".23" stop-color="#f8bd20"/>
<stop offset=".4" stop-color="#faca2c"/>
<stop offset=".6" stop-color="#fbd232"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-7" x1="19.43" y1="68.42" x2="756.99" y2="71.42" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-8" x1="-12.06" y1="69.99" x2="808.02" y2="73.32" xlink:href="#linear-gradient-6"/>
<linearGradient id="linear-gradient-9" x1="0" y1="69.56" x2="54.12" y2="69.56" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20"/>
<stop offset=".21" stop-color="#f9c628"/>
<stop offset=".58" stop-color="#fbd131"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-10" x1="-24.07" y1="41.56" x2="837.97" y2="45.06" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20"/>
<stop offset=".07" stop-color="#f9c628"/>
<stop offset=".2" stop-color="#fbd131"/>
<stop offset=".34" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-11" x1="485.03" y1="69.56" x2="538.44" y2="69.56" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#f8bd20"/>
<stop offset=".21" stop-color="#f9c628"/>
<stop offset=".58" stop-color="#fbd131"/>
<stop offset="1" stop-color="#fcd535"/>
</linearGradient>
<linearGradient id="linear-gradient-12" x1="521.21" y1="69.56" x2="642.79" y2="69.56" xlink:href="#linear-gradient-11"/>
</defs>
<g>
<path class="cls-6" d="M383.66,189.79l.87-4.15c.31-1.47-.81-2.86-2.32-2.86h-10.04c-1.12,0-2.08.78-2.32,1.87l-1.31,6.12c-.23,1.09-1.2,1.87-2.32,1.87h0c-1.51,0-2.63-1.39-2.32-2.86l4.22-19.87c.23-1.09,1.2-1.88,2.32-1.88h0c1.51,0,2.63,1.39,2.32,2.86l-.88,4.17c-.31,1.47.81,2.86,2.32,2.86h10.04c1.12,0,2.08-.78,2.32-1.87l1.32-6.14c.23-1.09,1.2-1.87,2.32-1.87h0c1.51,0,2.63,1.39,2.32,2.86l-4.22,19.87c-.23,1.09-1.2,1.88-2.32,1.88h0c-1.51,0-2.63-1.39-2.32-2.86Z"/>
<path class="cls-6" d="M416.74,192.65h-11c-1.72,0-3.35-.79-4.41-2.14h0c-1.03-1.31-1.42-3-1.08-4.62l2.37-11.23c.33-1.56,1.18-2.96,2.41-3.97l.86-.7c1.53-1.25,3.45-1.94,5.43-1.94h10.39c1.72,0,3.35.79,4.41,2.14h0c1.03,1.31,1.42,3,1.08,4.62l-2.37,11.19c-.34,1.59-1.2,3.01-2.45,4.04l-1.27,1.04c-1.23,1.01-2.78,1.57-4.38,1.57ZM419.35,172.9h-8.48c-1.77,0-3.3,1.24-3.66,2.97l-1.7,7.97c-.43,2.03,1.12,3.95,3.2,3.95h7.89c1.76,0,3.28-1.23,3.64-2.95l1.83-8.58c.37-1.73-.95-3.35-2.72-3.35Z"/>
<path class="cls-6" d="M450.21,192.65h-13.09c-1.44,0-2.52-1.33-2.22-2.74l.07-.32c.22-1.05,1.15-1.8,2.22-1.8h14.24c1.27,0,2.51-.44,3.49-1.25h0c.35-.29.59-.68.69-1.12h0c.14-.6,0-1.22-.38-1.71l-.04-.05c-.43-.55-1.09-.88-1.79-.88h-10.13c-1.67,0-2.95-.63-3.99-1.94h0c-1.07-1.36-1.12-3.12-1.09-4.97l.07-1.88c0-.68.15-1.66.56-2.43.48-.92.95-1.56,2.87-2.8h0c1.32-.83,3.78-.72,6.91-.72h12.75c1.44,0,2.52,1.33,2.22,2.74l-.07.32c-.22,1.05-1.15,1.8-2.22,1.8h-14.3c-1.25,0-2.46.43-3.43,1.22h0c-.36.3-.61.7-.7,1.16h0c-.12.59.02,1.2.39,1.67h0c.48.61,1.21.97,1.99.97h10.37c1.41,0,2.74.64,3.61,1.75h0c1.23,1.56,1.71,3.59,1.29,5.53l-.25,1.19c-.28,1.33-1.01,2.52-2.05,3.38h0c-2.26,1.86-5.09,2.87-8.01,2.87Z"/>
<path class="cls-6" d="M488.37,174.78l-3.4,16c-.23,1.09-1.2,1.88-2.32,1.88h0c-1.51,0-2.63-1.39-2.32-2.86l2.98-14.03c.31-1.47-.81-2.86-2.32-2.86h-4.04c-1.51,0-2.63-1.39-2.32-2.86l.03-.12c.23-1.1,1.2-1.88,2.32-1.88h19.77c1.51,0,2.63,1.39,2.32,2.86l-.03.12c-.23,1.1-1.2,1.88-2.32,1.88h-6.04c-1.12,0-2.08.78-2.32,1.88Z"/>
<path class="cls-6" d="M505.5,189.68l4.2-19.69c.24-1.14,1.25-1.95,2.41-1.95h0c1.57,0,2.73,1.44,2.41,2.97l-4.2,19.69c-.24,1.14-1.25,1.95-2.41,1.95h0c-1.57,0-2.73-1.44-2.41-2.97Z"/>
<path class="cls-6" d="M539.69,191.61l-9.6-14.15c-.48-.7-1.56-.49-1.74.34l-2.76,12.97c-.23,1.09-1.2,1.88-2.32,1.88h0c-1.51,0-2.63-1.39-2.32-2.86l4.13-19.43c.29-1.35,1.48-2.31,2.86-2.31h.53c1.06,0,2.06.53,2.65,1.41l9.51,14.05c.41.6,1.34.42,1.49-.29l2.83-13.29c.23-1.09,1.2-1.88,2.32-1.88h0c1.51,0,2.63,1.39,2.32,2.86l-4.22,19.87c-.23,1.09-1.2,1.88-2.32,1.88h-1.4c-.79,0-1.52-.39-1.96-1.04Z"/>
<path class="cls-6" d="M573.43,192.64l-10.43.02c-1.83,0-3.56-.83-4.7-2.27l-.04-.05c-.95-1.2-1.31-2.74-.99-4.22l2.41-11.39c.34-1.6,1.21-3.04,2.48-4.08l1.22-1c1.26-1.04,2.85-1.6,4.48-1.6h10.94c1.72,0,3.34.79,4.4,2.14h0c1.02,1.3,1.42,2.99,1.08,4.62-.05.26-.11.51-.16.74-.16.75-.82,1.29-1.6,1.29h-1.49c-1.03,0-1.8-.96-1.59-1.97h0c.21-1.01-.55-1.96-1.59-1.96-3.11,0-9,0-11.62,0-.77,0-1.43.54-1.59,1.29l-2.46,11.65c-.21,1.01.56,1.97,1.6,1.97h11.59c.77,0,1.44-.54,1.6-1.29l.37-1.76c.21-1.01-.56-1.97-1.6-1.97h-5.86c-1.04,0-1.81-.96-1.59-1.97l.34-1.6c.16-.75.83-1.29,1.59-1.29h6.15c1.9,0,3.7.87,4.88,2.36l.15.19c.84,1.05,1.16,2.43.88,3.74l-.55,2.61c-.22,1.05-.8,1.99-1.63,2.67l-1.62,1.33c-1.42,1.17-3.21,1.81-5.05,1.81Z"/>
<path class="cls-6" d="M629.48,182.79h-10.75c-1.12,0-2.08.78-2.32,1.87l-1.31,6.12c-.23,1.09-1.2,1.87-2.32,1.87h0c-1.51,0-2.63-1.39-2.32-2.86l4.22-19.87c.23-1.09,1.2-1.88,2.32-1.88h15.74c1.73,0,3.37.8,4.44,2.16h0c1.02,1.3,1.41,2.98,1.07,4.59l-.43,2.02c-.24,1.13-.85,2.15-1.75,2.89l-1.51,1.25c-1.43,1.19-3.23,1.83-5.09,1.83ZM631.75,172.9h-11.58c-.73,0-1.35.51-1.51,1.22l-.4,1.88c-.21.99.55,1.92,1.56,1.92h11.52c.82,0,1.54-.58,1.71-1.38l.34-1.6c.23-1.05-.57-2.04-1.65-2.04Z"/>
<path class="cls-6" d="M665.75,189.79l.89-4.18c.31-1.47-.81-2.86-2.32-2.86h-10.07c-1.12,0-2.08.78-2.32,1.87l-1.32,6.15c-.23,1.1-1.21,1.88-2.33,1.87h-.02c-1.5,0-2.62-1.39-2.31-2.86l3.11-14.63c.4-1.88,1.43-3.57,2.92-4.79l.53-.43c1.5-1.22,3.37-1.89,5.3-1.89h10.74c1.55,0,3.01.71,3.97,1.93h0c1.13,1.45,1.57,3.32,1.19,5.11l-3.33,15.69c-.23,1.09-1.2,1.88-2.32,1.88h0c-1.51,0-2.63-1.39-2.32-2.86ZM667.11,172.9h-11.14c-.9,0-1.67.63-1.86,1.5l-.32,1.52c-.22,1.03.57,2,1.62,2h11.64c.71,0,1.33-.5,1.48-1.19l.35-1.64c.24-1.13-.61-2.19-1.77-2.19Z"/>
<path class="cls-6" d="M700.08,191.39l-9.28-13.69c-.54-.79-1.76-.55-1.96.38l-2.7,12.69c-.23,1.09-1.2,1.88-2.32,1.88h0c-1.51,0-2.63-1.39-2.32-2.86l4.16-19.56c.27-1.28,1.4-2.19,2.71-2.19h.51c1.15,0,2.23.57,2.88,1.53l9.3,13.74c.45.67,1.49.47,1.66-.32l2.78-13.07c.23-1.09,1.2-1.88,2.32-1.88h0c1.51,0,2.63,1.39,2.32,2.86l-4.12,19.37c-.29,1.39-1.52,2.38-2.94,2.38h-.62c-.95,0-1.84-.47-2.37-1.26Z"/>
<path class="cls-6" d="M717.03,189.71l4.2-19.75c.24-1.12,1.23-1.92,2.38-1.92h19.64c1.54,0,2.7,1.42,2.38,2.93h0c-.24,1.12-1.23,1.93-2.38,1.93h-15.81c-1.15,0-2.14.8-2.38,1.92l-.03.16c-.32,1.51.83,2.94,2.38,2.94h9.8c1.54,0,2.7,1.42,2.38,2.93h0c-.24,1.12-1.23,1.93-2.38,1.93h-11.87c-1.14,0-2.13.8-2.37,1.92l-.03.14c-.33,1.51.83,2.95,2.37,2.95h13.75c1.54,0,2.7,1.42,2.38,2.93h0c-.24,1.12-1.23,1.93-2.38,1.93h-19.64c-1.55,0-2.7-1.42-2.38-2.94Z"/>
<path class="cls-6" d="M752.53,189.67l4.22-19.85c.23-1.1,1.2-1.88,2.33-1.88h0c1.51,0,2.64,1.39,2.33,2.87l-2.99,14c-.32,1.48.81,2.87,2.33,2.87h13.78c1.57,0,2.74,1.44,2.41,2.97h0c-.09.41-.14.86-.42,1.18-.76.87-1.78.71-3.38.71h-12.75c-2.1,0-2.93.07-5.76.01-1.58-.03-2.19-.74-2.09-2.89Z"/>
</g>
<path class="cls-2" d="M428.17,83.3h-2.67c-6.07,0-11.32,4.24-12.59,10.18l-7.54,35.19c-1.27,5.94-6.52,10.18-12.59,10.18h-.87c-8.18,0-14.28-7.52-12.6-15.52l4.96-23.63c1.77-8.45-4.67-16.4-13.31-16.4h0c-9.55,0-18.57-4.38-24.47-11.89l-.06-.08c-5.89-7.49-8.16-17.22-6.17-26.54l7.29-34.33c1.26-5.95,6.52-10.2,12.6-10.2h.87c8.18,0,14.28,7.52,12.6,15.52l-5.17,24.62c-.77,3.69.11,7.53,2.41,10.51h0c2.44,3.16,6.21,5.01,10.2,5.01h50.24c5.4,0,10.62-1.9,14.76-5.38h0c3.4-2.85,5.74-6.76,6.66-11.1l6.16-28.99c1.26-5.95,6.52-10.2,12.6-10.2h.95c8.19,0,14.3,7.54,12.6,15.55l-5.43,25.56c-2.5,11.74-8.89,22.3-18.14,29.95h0c-9.36,7.74-21.12,11.98-33.26,11.98Z"/>
<path class="cls-7" d="M808.22,16.77h0c1.79-8.51-4.7-16.51-13.39-16.51h-94.61s-5.53,27.37-5.53,27.37h100.15c6.47,0,12.06-4.53,13.39-10.86Z"/>
<path class="cls-9" d="M774.88,72.44h0c1.79-8.51-4.7-16.51-13.39-16.51h-72.87l-5.77,27.37h78.64c6.47,0,12.06-4.53,13.39-10.86Z"/>
<path class="cls-13" d="M677.87,111.48l6.11-28.18h0l6.01-28.49h.05s5.78-27.18,5.78-27.18l5.79-27.03c.04-.18-.1-.34-.27-.34h-5.32c-12.69,0-23.65,8.88-26.29,21.29l-21.42,100.78c-1.81,8.51,4.68,16.53,13.39,16.53h110.59c6.47,0,12.06-4.53,13.39-10.86h0c1.79-8.51-4.7-16.51-13.39-16.51h-94.4Z"/>
<path class="cls-5" d="M203.58,55.93l8.32-39.57c1.75-8.3-4.59-16.1-13.06-16.1h0c-6.3,0-11.75,4.41-13.06,10.58l-23.78,111.89c-1.77,8.31,4.57,16.13,13.06,16.13h0c6.29,0,11.73-4.4,13.05-10.55l9.66-45.01h0l5.82-27.37h0Z"/>
<polygon class="cls-3" points="203.58 55.87 197.77 83.24 282.37 83.3 288.17 55.93 203.58 55.87"/>
<path class="cls-8" d="M311.44.26c-6.29,0-11.73,4.4-13.05,10.55l-9.59,44.73h0l-6.55,30.92h-.03s-7.64,36.29-7.64,36.29c-1.75,8.3,4.58,16.1,13.06,16.1h.01c6.3,0,11.75-4.41,13.06-10.57l23.78-111.9c1.77-8.3-4.57-16.12-13.06-16.12h0Z"/>
<polygon class="cls-11" points="288.73 55.94 288.7 55.93 288.7 55.93 288.17 55.93 282.37 83.3 282.88 83.3 282.22 86.46 282.25 86.47 288.73 55.94"/>
<path class="cls-10" d="M21.82,21.46L.3,122.73c-1.77,8.31,4.57,16.13,13.06,16.13h0c6.29,0,11.73-4.4,13.05-10.55l9.66-45.01-.02.12,5.84-27.49,6.01-28.3h0L54.12.26h-6.12c-12.64,0-23.55,8.84-26.18,21.2Z"/>
<path class="cls-12" d="M111.53,0l-57.79.26-5.95,27.37h66.44c9.04,0,15.77,8.34,13.87,17.18h0c-1.4,6.5-7.12,11.16-13.77,11.2l-72.69.53-5.73,27.37,69.83-.49c11.47-.08,24.22-2.55,33.06-9.86h0c6.97-5.77,14.29-13.29,16.46-31.62h0c-.56-20.89-7.73-28.5-7.73-28.5h0S140.06,1,111.53,0Z"/>
<path class="cls-4" d="M506.81,21.58l-21.48,101.08c-1.77,8.34,4.59,16.2,13.12,16.2h0c6.32,0,11.78-4.41,13.11-10.59l9.65-44.96h.02l5.71-27.37h0l5.96-28.17,5.55-27.42s-.02-.08-.07-.08h-5.23c-12.71,0-23.69,8.89-26.33,21.32Z"/>
<path class="cls-1" d="M636.82,13.71l-2.27-2.89c-5.24-6.67-13.25-10.57-21.73-10.57h-74.36l-5.55,27.37h78.11c3.05,0,5.35,2.78,4.77,5.78h0c-.21,1.11-.81,2.11-1.68,2.82l-16.44,13.56c-4.81,3.96-10.84,6.13-17.07,6.13h-53.68l-5.72,27.37h51.65c7.31,0,14.23,3.35,18.76,9.09l1.28,1.62c4.97,6.29,6.86,14.46,5.18,22.29l-1.48,6.87c-1.74,8.07,4.41,15.68,12.66,15.68h0c6.55,0,12.2-4.58,13.56-10.99l5.11-24.08c1.74-8.21-.24-16.77-5.42-23.38l-5.33-6.81c-1.76-2.25-1.41-5.48.79-7.3l14.59-12.04c4.67-3.85,7.88-9.18,9.12-15.1l.52-2.49c1.68-8.07-.28-16.47-5.38-22.95Z"/>
</svg>
</div>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,23 @@
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex flex-col bg-gray-50 justify-center h-screen text-center">
<div>
<h1 class="text-3xl font-bold">
%DOMAIN%
</h1>
</div>
<div class="mt-4">
<p class="text-xl">
Welcome to PhyrePanel! <br />
The simplest way to manage your websites.
</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,11 @@
server {
server_name %SERVER_NAME% www.%SERVER_NAME%;
root %SERVER_ROOT%;
charset utf-8;
location / {
}
}

BIN
screenshots/dashboard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

View File

@ -0,0 +1,54 @@
rm -rf /usr/local/phyre/update/web-panel-latest
rm -rf /usr/local/phyre/update/phyre-web-panel.zip
wget https://github.com/CloudVisionApps/PhyrePanelWebDist/raw/main/phyre-web-panel.zip
ls -la
unzip -o phyre-web-panel.zip -d /usr/local/phyre/update/web-panel-latest
rm -rf /usr/local/phyre/web/vendor
rm -rf /usr/local/phyre/web/composer.lock
rm -rf /usr/local/phyre/web/routes
rm -rf /usr/local/phyre/web/public
rm -rf /usr/local/phyre/web/resources
rm -rf /usr/local/phyre/web/database
rm -rf /usr/local/phyre/web/config
rm -rf /usr/local/phyre/web/app
rm -rf /usr/local/phyre/web/bootstrap
rm -rf /usr/local/phyre/web/lang
rm -rf /usr/local/phyre/web/Modules
rm -rf /usr/local/phyre/web/thirdparty
cp -r /usr/local/phyre/update/web-panel-latest/vendor /usr/local/phyre/web/vendor
cp /usr/local/phyre/update/web-panel-latest/composer.lock /usr/local/phyre/web/composer.lock
cp -r /usr/local/phyre/update/web-panel-latest/routes /usr/local/phyre/web/routes
cp -r /usr/local/phyre/update/web-panel-latest/public /usr/local/phyre/web/public
cp -r /usr/local/phyre/update/web-panel-latest/resources /usr/local/phyre/web/resources
cp -r /usr/local/phyre/update/web-panel-latest/database /usr/local/phyre/web/database
cp -r /usr/local/phyre/update/web-panel-latest/config /usr/local/phyre/web/config
cp -r /usr/local/phyre/update/web-panel-latest/app /usr/local/phyre/web/app
cp -r /usr/local/phyre/update/web-panel-latest/bootstrap /usr/local/phyre/web/bootstrap
cp -r /usr/local/phyre/update/web-panel-latest/lang /usr/local/phyre/web/lang
cp -r /usr/local/phyre/update/web-panel-latest/Modules /usr/local/phyre/web/Modules
#cp -r /usr/local/phyre/update/web-panel-latest/thirdparty /usr/local/phyre/web/thirdparty
cp -r /usr/local/phyre/update/web-panel-latest/db-migrate.sh /usr/local/phyre/web/db-migrate.sh
chmod +x /usr/local/phyre/web/db-migrate.sh
#
cd /usr/local/phyre/web
#
#
#
#PHYRE_PHP=/usr/local/phyre/php/bin/php
##
#$PHYRE_PHP -v
#$PHYRE_PHP -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
#$PHYRE_PHP ./composer-setup.php
#$PHYRE_PHP -r "unlink('composer-setup.php');"
#rm -rf composer.lock
#COMPOSER_ALLOW_SUPERUSER=1 $PHYRE_PHP composer.phar i --no-interaction --no-progress
#COMPOSER_ALLOW_SUPERUSER=1 $PHYRE_PHP composer.phar dump-autoload --no-interaction
./db-migrate.sh
service phyre restart

1
version.txt Normal file
View File

@ -0,0 +1 @@
0.0.2

18
web/.editorconfig Normal file
View File

@ -0,0 +1,18 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4

64
web/.env.example Normal file
View File

@ -0,0 +1,64 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=sqlite
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQl_ROOT_USERNAME=root
MYSQL_ROOT_PASSWORD=root
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_APP_NAME="${APP_NAME}"
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"

11
web/.gitattributes vendored Normal file
View File

@ -0,0 +1,11 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

18
web/.gitignore vendored Normal file
View File

@ -0,0 +1,18 @@
/.phpunit.cache
/node_modules
/public/hot
/public/storage
/storage/*.key
/vendor
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
auth.json
npm-debug.log
yarn-error.log
/.fleet
/.idea
/.vscode

View File

@ -0,0 +1,92 @@
<?php
namespace Modules\Docker\App\Console;
use Illuminate\Console\Command;
use Modules\Docker\App\Models\DockerContainer;
use Modules\Docker\App\Models\DockerImage;
use Modules\Docker\DockerApi;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class DockerContainers extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'docker:get-containers';
/**
* The console command description.
*/
protected $description = 'Command description.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$dockerApi = new DockerApi();
$containers = $dockerApi->getContainers();
if (!empty($containers)) {
foreach($containers as $container) {
$findDockerContainer = DockerContainer::where('docker_id', $container['ID'])->first();
// if ($findDockerContainer) {
// $findDockerContainer->delete();
// }
// continue;
if (!$findDockerContainer) {
$findDockerContainer = new DockerContainer();
$findDockerContainer->docker_id = $container['ID'];
$findDockerContainer->environment_variables = [];
}
$findDockerContainer->name = $container['Image'];
$findDockerContainer->image = $container['Image'];
$findDockerContainer->command = $container['Command'];
$findDockerContainer->labels = $container['Labels'];
$findDockerContainer->local_volumes = $container['LocalVolumes'];
$findDockerContainer->mounts = $container['Mounts'];
$findDockerContainer->names = $container['Names'];
$findDockerContainer->networks = $container['Networks'];
$findDockerContainer->ports = $container['Ports'];
$findDockerContainer->running_for = $container['RunningFor'];
$findDockerContainer->size = $container['Size'];
$findDockerContainer->state = $container['State'];
$findDockerContainer->status = $container['Status'];
$findDockerContainer->save();
}
}
}
/**
* Get the console command arguments.
*/
protected function getArguments(): array
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*/
protected function getOptions(): array
{
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View File

@ -0,0 +1,71 @@
<?php
namespace Modules\Docker\App\Console;
use Illuminate\Console\Command;
use Modules\Docker\App\Models\DockerImage;
use Modules\Docker\DockerApi;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class DockerRunImage extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'docker:run-image {name}';
/**
* The console command description.
*/
protected $description = 'Command description.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$name = $this->argument('name');
$dockerImage = DockerImage::where('name', $name)->first();
if ($dockerImage) {
$this->info('Running image: ' . $dockerImage->name);
$dockerApi = new DockerApi();
$pullCommand = $dockerApi->runImage($name);
$this->info($pullCommand);
} else {
$this->error('Image not found: ' . $name);
}
}
/**
* Get the console command arguments.
*/
protected function getArguments(): array
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*/
protected function getOptions(): array
{
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Modules\Docker\App\Console;
use Illuminate\Console\Command;
use Modules\Docker\App\Models\DockerImage;
use Modules\Docker\DockerApi;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
class DockerSearchImages extends Command
{
/**
* The name and signature of the console command.
*/
protected $signature = 'docker:search-images {name}';
/**
* The console command description.
*/
protected $description = 'Command description.';
/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*/
public function handle()
{
$this->info('Display this on the screen');
$name = $this->argument('name');
$this->info('You have entered: ' . $name);
$dockerApi = new DockerApi();
$dockerSearch = $dockerApi->searchImages($name);
foreach($dockerSearch as $dockerImage) {
if ($dockerImage['IsOfficial'] == 'true') {
$dockerImage['IsOfficial'] = 1;
} else {
$dockerImage['IsOfficial'] = 0;
}
if ($dockerImage['IsAutomated'] == 'true') {
$dockerImage['IsAutomated'] = 1;
} else {
$dockerImage['IsAutomated'] = 0;
}
$findDockerImage = DockerImage::where('name', $dockerImage['Name'])->first();
if ($findDockerImage === null) {
$findDockerImage = new DockerImage();
$findDockerImage->name = $dockerImage['Name'];
}
$findDockerImage->description = $dockerImage['Description'];
$findDockerImage->star_count = $dockerImage['StarCount'];
$findDockerImage->is_official = $dockerImage['IsOfficial'];
$findDockerImage->is_automated = $dockerImage['IsAutomated'];
$findDockerImage->save();
$this->info('Name: ' . $dockerImage['Name']);
$this->info('Description: ' . $dockerImage['Description']);
$this->info('Stars: ' . $dockerImage['StarCount']);
$this->info('Official: ' . $dockerImage['IsOfficial']);
$this->info('Automated: ' . $dockerImage['IsAutomated']);
$this->info('_______________________________________');
}
}
/**
* Get the console command arguments.
*/
protected function getArguments(): array
{
return [
['example', InputArgument::REQUIRED, 'An example argument.'],
];
}
/**
* Get the console command options.
*/
protected function getOptions(): array
{
return [
['example', null, InputOption::VALUE_OPTIONAL, 'An example option.', null],
];
}
}

View File

@ -0,0 +1,67 @@
<?php
namespace Modules\Docker\App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class DockerController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('docker::index');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('docker::create');
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
//
}
/**
* Show the specified resource.
*/
public function show($id)
{
return view('docker::show');
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
return view('docker::edit');
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id): RedirectResponse
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
//
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace Modules\Docker\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Modules\Docker\DockerApi;
use Modules\Docker\DockerContainerApi;
class DockerContainer extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'command',
'docker_id',
'image',
'labels',
'local_volumes',
'mounts',
'names',
'networks',
'ports',
'running_for',
'size',
'state',
'status',
'memory_limit',
'unlimited_memory',
'automatic_start',
'external_port',
'volume_mapping',
'environment_variables',
];
protected $casts = [
'environment_variables' => 'array',
'volume_mapping' => 'array',
];
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$dockerContainerApi = new DockerContainerApi();
$dockerContainerApi->setImage($model->image);
$dockerContainerApi->setEnvironmentVariables($model->environment_variables);
$dockerContainerApi->setVolumeMapping($model->volume_mapping);
// $dockerContainerApi->setMemoryLimit($model->memory_limit);
// $dockerContainerApi->setUnlimitedMemory($model->unlimited_memory);
// $dockerContainerApi->setAutomaticStart($model->automatic_start);
$dockerContainerApi->setPort($model->port);
$dockerContainerApi->setExternalPort($model->external_port);
$createContainer = $dockerContainerApi->run();
if (!isset($createContainer['ID'])) {
return false;
}
$model->image = $createContainer['Image'];
$model->command = $createContainer['Command'];
$model->labels = $createContainer['Labels'];
$model->local_volumes = $createContainer['LocalVolumes'];
$model->mounts = $createContainer['Mounts'];
$model->names = $createContainer['Names'];
$model->networks = $createContainer['Networks'];
$model->ports = $createContainer['Ports'];
$model->running_for = $createContainer['RunningFor'];
$model->size = $createContainer['Size'];
$model->state = $createContainer['State'];
$model->status = $createContainer['Status'];
$model->docker_id = $createContainer['ID'];
});
static::deleting(function ($model) {
$dockerApi = new DockerApi();
$dockerApi->removeContainerById($model->docker_id);
});
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Modules\Docker\App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
class DockerImage extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [];
}

View File

@ -0,0 +1,131 @@
<?php
namespace Modules\Docker\App\Providers;
use BladeUI\Icons\Factory;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\ServiceProvider;
use Modules\Docker\App\Console\DockerContainers;
use Modules\Docker\App\Console\DockerRunImage;
use Modules\Docker\App\Console\DockerSearchImages;
class DockerServiceProvider extends ServiceProvider
{
protected string $moduleName = 'Docker';
protected string $moduleNameLower = 'docker';
/**
* Boot the application events.
*/
public function boot(): void
{
$this->registerCommands();
$this->registerCommandSchedules();
$this->registerTranslations();
$this->registerConfig();
$this->registerViews();
$this->loadMigrationsFrom(module_path($this->moduleName, 'Database/migrations'));
}
/**
* Register the service provider.
*/
public function register(): void
{
// Register Phyre Icons set
$this->callAfterResolving(Factory::class, function (Factory $factory) {
$factory->add('docker', [
'path' => __DIR__ . '/../../resources/assets/docker-svg',
'prefix' => 'docker',
]);
});
$this->app->register(RouteServiceProvider::class);
}
/**
* Register commands in the format of Command::class
*/
protected function registerCommands(): void
{
$this->commands([
DockerSearchImages::class,
DockerRunImage::class,
DockerContainers::class
]);
}
/**
* Register command Schedules.
*/
protected function registerCommandSchedules(): void
{
// $this->app->booted(function () {
// $schedule = $this->app->make(Schedule::class);
// $schedule->command('inspire')->hourly();
// });
}
/**
* Register translations.
*/
public function registerTranslations(): void
{
$langPath = resource_path('lang/modules/'.$this->moduleNameLower);
if (is_dir($langPath)) {
$this->loadTranslationsFrom($langPath, $this->moduleNameLower);
$this->loadJsonTranslationsFrom($langPath);
} else {
$this->loadTranslationsFrom(module_path($this->moduleName, 'lang'), $this->moduleNameLower);
$this->loadJsonTranslationsFrom(module_path($this->moduleName, 'lang'));
}
}
/**
* Register config.
*/
protected function registerConfig(): void
{
$this->publishes([module_path($this->moduleName, 'config/config.php') => config_path($this->moduleNameLower.'.php')], 'config');
$this->mergeConfigFrom(module_path($this->moduleName, 'config/config.php'), $this->moduleNameLower);
}
/**
* Register views.
*/
public function registerViews(): void
{
$viewPath = resource_path('views/modules/'.$this->moduleNameLower);
$sourcePath = module_path($this->moduleName, 'resources/views');
$this->publishes([$sourcePath => $viewPath], ['views', $this->moduleNameLower.'-module-views']);
$this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), $this->moduleNameLower);
$componentNamespace = str_replace('/', '\\', config('modules.namespace').'\\'.$this->moduleName.'\\'.config('modules.paths.generator.component-class.path'));
Blade::componentNamespace($componentNamespace, $this->moduleNameLower);
}
/**
* Get the services provided by the provider.
*/
public function provides(): array
{
return [];
}
private function getPublishableViewPaths(): array
{
$paths = [];
foreach (config('view.paths') as $path) {
if (is_dir($path.'/modules/'.$this->moduleNameLower)) {
$paths[] = $path.'/modules/'.$this->moduleNameLower;
}
}
return $paths;
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Modules\Docker\App\Providers;
use Illuminate\Support\Facades\Route;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
class RouteServiceProvider extends ServiceProvider
{
/**
* The module namespace to assume when generating URLs to actions.
*/
protected string $moduleNamespace = 'Modules\Docker\App\Http\Controllers';
/**
* Called before routes are registered.
*
* Register any model bindings or pattern based filters.
*/
public function boot(): void
{
parent::boot();
}
/**
* Define the routes for the application.
*/
public function map(): void
{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "web" routes for the application.
*
* These routes all receive session state, CSRF protection, etc.
*/
protected function mapWebRoutes(): void
{
Route::middleware('web')
->namespace($this->moduleNamespace)
->group(module_path('Docker', '/routes/web.php'));
}
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*/
protected function mapApiRoutes(): void
{
Route::prefix('api')
->middleware('api')
->namespace($this->moduleNamespace)
->group(module_path('Docker', '/routes/api.php'));
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Modules\Docker\Database\Seeders;
use Illuminate\Database\Seeder;
class DockerDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// $this->call([]);
}
}

View File

@ -0,0 +1,52 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('docker_containers', function (Blueprint $table) {
$table->id();
$table->string('name')->nullable();
$table->string('command')->nullable();
$table->string('docker_id')->nullable();
$table->string('image')->nullable();
$table->longText('labels')->nullable();
$table->string('local_volumes')->nullable();
$table->string('mounts')->nullable();
$table->string('names')->nullable();
$table->string('networks')->nullable();
$table->string('ports')->nullable();
$table->string('running_for')->nullable();
$table->string('size')->nullable();
$table->string('state')->nullable();
$table->string('status')->nullable();
$table->string('memory_limit')->nullable();
$table->tinyInteger('unlimited_memory')->nullable();
$table->tinyInteger('automatic_start')->nullable();
$table->string('port')->nullable();
$table->string('external_port')->nullable();
$table->longText('volume_mapping')->nullable();
$table->longText('environment_variables')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('docker_containers');
}
};

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('docker_images', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->longText('description')->nullable();
$table->integer('star_count')->nullable();
$table->boolean('is_official')->default(false);
$table->boolean('is_automated')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('docker_images');
}
};

View File

@ -0,0 +1,144 @@
<?php
namespace Modules\Docker;
class DockerApi
{
public $logFile = '';
public function setLogFile($logFile)
{
$this->logFile = $logFile;
}
public function pullImage($name)
{
$commandId = rand(10000, 99999);
$commands = [];
$commands[] = 'docker pull ' . $name;
$shellFileContent = '';
foreach ($commands as $command) {
$shellFileContent .= $command . PHP_EOL;
}
$shellFileContent .= 'echo "DONE!"' . PHP_EOL;
$shellFileContent .= 'rm -f /tmp/docker-pull-image-'.$commandId.'.sh';
file_put_contents('/tmp/docker-pull-image-'.$commandId.'.sh', $shellFileContent);
shell_exec('bash /tmp/docker-pull-image-'.$commandId.'.sh >> ' . $this->logFile . ' &');
}
public function searchImages($keyword, $filters = [])
{
$filtersString = ''; // --filter is-official=true
$output = shell_exec('docker search --format "{{json .}}" --no-trunc '.$filtersString.' ' . $keyword);
$output = trim($output);
$output = str_replace("\n", ',', $output);
$output = '[' . $output . ']';
$dockerSearch = json_decode($output, true);
if ($dockerSearch === null) {
return [];
}
return $dockerSearch;
}
public function getContainers()
{
$output = shell_exec('docker ps -a --format "{{json .}}"');
$output = trim($output);
$output = str_replace("\n", ',', $output);
$output = '[' . $output . ']';
$dockerContainers = json_decode($output, true);
if ($dockerContainers === null) {
return [];
}
return $dockerContainers;
}
public function removeContainerById($id)
{
shell_exec('docker rm -f ' . $id);
}
public function restartContainer($id)
{
shell_exec('docker restart ' . $id);
}
public function stopContainer($id)
{
shell_exec('docker stop ' . $id);
}
public function startContainer($id)
{
shell_exec('docker start ' . $id);
}
public function getContainerLogs($id)
{
$output = shell_exec('docker logs ' . $id .' > /tmp/docker-logs-'.$id.'.log 2>&1');
$logContent = '';
if (file_exists('/tmp/docker-logs-'.$id.'.log')) {
$logContent = file_get_contents('/tmp/docker-logs-'.$id.'.log');
}
return $logContent;
}
public function getContainerStats($id)
{
$output = shell_exec('docker stats --format json --no-stream ' . $id);
$output = json_decode($output, true);
return $output;
}
public function getContainerProcesses($id)
{
$output = shell_exec('docker top ' . $id);
return $output;
}
public function getContainerById($id)
{
$output = shell_exec('docker ps -f id='.$id.' -a --format "{{json .}}"');
$output = trim($output);
$output = str_replace("\n", ',', $output);
$output = '[' . $output . ']';
$dockerContainer = json_decode($output, true);
if (!isset($dockerContainer[0])) {
return [];
}
return $dockerContainer[0];
}
public function getContainerInspect($id)
{
$output = shell_exec('docker inspect ' . $id);
return $output;
}
public function getDockerImageInspect($name)
{
$output = shell_exec('docker image inspect ' . $name);
$output = json_decode($output, true);
if (isset($output[0])) {
return $output[0];
}
return [];
}
}

View File

@ -0,0 +1,89 @@
<?php
namespace Modules\Docker;
use function AlibabaCloud\Client\json;
class DockerContainerApi
{
public $image = '';
public $environmentVariables = [];
public $volumeMapping = [];
public $port = '';
public $externalPort = '';
public function setImage($image)
{
$this->image = $image;
}
public function setEnvironmentVariables($environmentVariables)
{
$this->environmentVariables = $environmentVariables;
}
public function setVolumeMapping($volumeMapping)
{
$this->volumeMapping = $volumeMapping;
}
public function setPort($port)
{
$this->port = $port;
}
public function setExternalPort($externalPort)
{
$this->externalPort = $externalPort;
}
public function recreate($containerId)
{
shell_exec('docker stop ' . $containerId);
shell_exec('docker rm ' . $containerId);
return $this->run();
}
public function run()
{
$commandId = rand(10000, 99999);
$commands = [];
$commands[] = 'docker run -d ' . $this->image;
if (!empty($this->port)) {
$commands[] = '-p ' . $this->port . ':' . $this->externalPort;
}
if (!empty($this->environmentVariables)) {
foreach ($this->environmentVariables as $key => $value) {
$commands[] = '-e ' . $key . '=' . $value;
}
}
if (!empty($this->volumeMapping)) {
foreach ($this->volumeMapping as $key => $value) {
$commands[] = '-v ' . $key . ':' . $value;
}
}
$shellFileContent = '';
foreach ($commands as $command) {
$shellFileContent .= $command . PHP_EOL;
}
$shellFileContent .= 'rm -f /tmp/docker-run-container-'.$commandId.'.sh';
file_put_contents('/tmp/docker-run-container-'.$commandId.'.sh', $shellFileContent);
$output = shell_exec('bash /tmp/docker-run-container-'.$commandId.'.sh');
// Get docker container id from output
$dockerContainerId = trim($output);
$output = shell_exec('docker ps --format json --filter id='.$dockerContainerId);
$output = json_decode($output, true);
return $output;
}
}

View File

@ -0,0 +1,150 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Pages;
use Filament\Actions\Action;
use Filament\Forms\Components\Actions;
use Filament\Forms\Components\Checkbox;
use Filament\Forms\Components\Grid;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Tabs;
use Filament\Forms\Components\Tabs\Tab;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Concerns\InteractsWithForms;
use Filament\Forms\Contracts\HasForms;
use Filament\Pages\Page;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Str;
use Modules\Docker\App\Models\DockerContainer;
use Modules\Docker\App\Models\DockerImage;
use Modules\Docker\DockerApi;
use Modules\Docker\Filament\Clusters\DockerCluster;
class DockerCatalog extends Page implements HasForms
{
use InteractsWithForms;
// protected static ?string $navigationGroup = 'Docker';
protected static ?string $cluster = DockerCluster::class;
protected static ?string $navigationIcon = 'heroicon-o-cog';
protected static ?int $navigationSort = 1;
protected static string $view = 'docker::filament.pages.docker-catalog';
public $keyword = '';
public $filterOfficial = true;
public $filterAutomated = true;
public $filterStarred = true;
public $pullLog = '';
public $pullLogFile = '';
public $pullLogPulling = false;
public $pullImageName = '';
public function updatedKeyword()
{
if (empty($this->keyword)) {
return;
}
Artisan::call('docker:search-images "' . $this->keyword.'"');
}
public function getPullLog()
{
if (file_exists($this->pullLogFile)) {
$logContent = file_get_contents($this->pullLogFile);
$this->pullLog = str_replace("\n", "<br>", $logContent);
}
if (str_contains($this->pullLog, 'DONE!')) {
$this->pullLogPulling = false;
$this->dispatch('close-modal', id: 'pull-docker-image');
return $this->redirect(route('filament.admin.docker.resources.docker-containers.create') . '?dockerImage=' . $this->pullImageName);
}
}
public function pullDockerImage($dockerImageName)
{
$dockerImage = DockerImage::where('name', $dockerImageName)->first();
if ($dockerImage) {
$this->pullImageName = $dockerImageName;
$this->dispatch('open-modal', id: 'pull-docker-image');
$dockerLogPath = storage_path('logs/docker/pull-'.Str::slug($dockerImageName).'.log');
if (!is_dir(dirname($dockerLogPath))) {
shell_exec('mkdir -p '.dirname($dockerLogPath));
}
$this->pullLogFile = $dockerLogPath;
$dockerApi = new DockerApi();
$dockerApi->setLogFile(storage_path('logs/docker/pull-'.Str::slug($dockerImageName).'.log'));
$dockerApi->pullImage($dockerImage->name);
$this->pullLogPulling = true;
$this->getPullLog();
}
}
public function removeDockerContainer($containerId)
{
$findDockerContainer = DockerContainer::where('id', $containerId)->first();
if ($findDockerContainer) {
$dockerApi = new DockerApi();
$dockerApi->removeContainerById($findDockerContainer->docker_id);
$findDockerContainer->delete();
}
}
protected function getViewData(): array
{
$findImagesQuery = DockerImage::query();
if ($this->keyword) {
$findImagesQuery->where('name', 'like', '%' . $this->keyword . '%');
}
$findImages = $findImagesQuery->get();
$findDockerContainers = DockerContainer::orderBy('id', 'desc')->get();
return [
'dockerImages' => $findImages->toArray(),
'dockerContainers' => $findDockerContainers->toArray(),
];
}
protected function getFormSchema(): array
{
return [
TextInput::make('keyword')
->live()
->placeholder('Search for docker images...')
->label('Search'),
Grid::make('4')
->schema([
Checkbox::make('filterOfficial')
->label('Official'),
Checkbox::make('filterAutomated')
->label('Automated'),
Checkbox::make('filterStarred')
->label('Starred'),
])
];
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Pages;
use Filament\Forms\Components\Section;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Modules\Docker\Filament\Clusters\DockerCluster;
use Outerweb\FilamentSettings\Filament\Pages\Settings as BaseSettings;
class Settings extends BaseSettings
{
protected static ?string $navigationGroup = 'Docker';
protected static ?string $cluster = DockerCluster::class;
protected static ?string $navigationIcon = 'heroicon-o-cog';
protected static ?int $navigationSort = 3;
public function schema(): array
{
return [
Section::make('Settings')
->schema([
]),
];
}
}

View File

@ -0,0 +1,142 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Resources;
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Modules\Docker\App\Models\DockerContainer;
use Modules\Docker\DockerApi;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages\CreateDockerContainer;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages\EditDockerContainer;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages\ListDockerContainers;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages\ViewDockerContainer;
use Modules\Docker\Filament\Clusters\DockerCluster;
class DockerContainerResource extends Resource
{
protected static ?string $model = DockerContainer::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
protected static ?string $cluster = DockerCluster::class;
protected static ?int $navigationSort = 2;
public static function form(Form $form): Form
{
$dockerImage = request('dockerImage', null);
$environmentVariables = [];
$dockerFullImageName = '';
$dockerApi = new DockerApi();
$dockerImageInspect = $dockerApi->getDockerImageInspect($dockerImage);
if (isset($dockerImageInspect['RepoTags'][0])) {
$dockerFullImageName = $dockerImageInspect['RepoTags'][0];
}
if (isset($dockerImageInspect['Config']['Env'])) {
foreach ($dockerImageInspect['Config']['Env'] as $env) {
$envParts = explode('=', $env);
$environmentVariables[$envParts[0]] = $envParts[1];
}
}
$defaultPort = '';
if (isset($dockerImageInspect['Config']['ExposedPorts'])) {
foreach ($dockerImageInspect['Config']['ExposedPorts'] as $port => $value) {
$port = str_replace('/tcp', '', $port);
$port = str_replace('/udp', '', $port);
$port = str_replace('tcp', '', $port);
$port = str_replace('udp', '', $port);
$port = str_replace('/', '', $port);
$defaultPort = $port;
}
}
return $form
->schema([
Forms\Components\TextInput::make('name')
->default($dockerFullImageName)
->label('Container Name')->columnSpanFull(),
Forms\Components\TextInput::make('memory_limit')
->label('Memory Limit (MB)')
->default(512)
->numeric()
->columnSpanFull(),
Forms\Components\Toggle::make('unlimited_memory')
->label('Unlimited Memory')
->columnSpanFull(),
Forms\Components\Toggle::make('automatic_start')
->label('Automatic start after system reboot')
->columnSpanFull(),
Forms\Components\TextInput::make('port')
->label('Port')
->default($defaultPort)
->columnSpan(1),
Forms\Components\TextInput::make('external_port')
->label('External Port')
->default($defaultPort)
->columnSpan(1),
Forms\Components\Select::make('image')
->label('Image')
->options([$dockerImage=>$dockerFullImageName])
->default($dockerImage)->columnSpanFull(),
Forms\Components\KeyValue::make('volume_mapping')
->label('Volume Mapping')
->columnSpanFull(),
Forms\Components\KeyValue::make('environment_variables')
->label('Environment Variables')
->default($environmentVariables)
->columnSpanFull(),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => ListDockerContainers::route('/'),
'create' => CreateDockerContainer::route('/create'),
'edit' => EditDockerContainer::route('/{record}/edit'),
'view'=> ViewDockerContainer::route('/{record}'),
];
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource;
class CreateDockerContainer extends CreateRecord
{
protected static string $resource = DockerContainerResource::class;
}

View File

@ -0,0 +1,18 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource;
class EditDockerContainer extends EditRecord
{
protected static string $resource = DockerContainerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource;
class ListDockerContainers extends ListRecords
{
protected static string $resource = DockerContainerResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -0,0 +1,110 @@
<?php
namespace Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource\Pages;
use Filament\Forms\Components\TextInput;
use Filament\Infolists\Components\Actions;
use Filament\Infolists\Components\Entry;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Components\ViewEntry;
use Filament\Infolists\Infolist;
use Filament\Resources\Pages\EditRecord;
use Filament\Resources\Pages\ViewRecord;
use Modules\Docker\App\Models\DockerContainer;
use Modules\Docker\DockerApi;
use Modules\Docker\DockerContainerApi;
use Modules\Docker\Filament\Clusters\Docker\Resources\DockerContainerResource;
class ViewDockerContainer extends ViewRecord
{
protected static string $resource = DockerContainerResource::class;
public $containerLog = '';
public function infolist(Infolist $infolist): Infolist
{
$dockerApi = new DockerApi();
// $containerStats = $dockerApi->getContainerStats($this->record->docker_id);
// $containerProcesses = $dockerApi->getContainerProcesses($this->record->docker_id);
$containerLogs = $dockerApi->getContainerLogs($this->record->docker_id);
// $container = $dockerApi->getContainerById($this->record->docker_id);
if ($containerLogs) {
$containerLogs = str_replace("\n", "<br>", $containerLogs);
$this->containerLog = $containerLogs;
}
return $infolist
->schema([
ViewEntry::make('status')
->columnSpanFull()
->view('docker::filament.infolists.docker-container.actions')
->registerActions([
Actions\Action::make('stop')
->label('Stop')
->color('primary'),
Actions\Action::make('start')
->label('Start')
->color('primary'),
Actions\Action::make('restart')
->label('Restart')
->color('success'),
Actions\Action::make('recreate')
->label('Recreate')
->action('recreate')
->color('info'),
Actions\Action::make('delete')
->label('Delete')
->color('danger'),
]),
ViewEntry::make('containerLogs')
->columnSpanFull()
->view('docker::filament.infolists.docker-container.logs',[
'containerLog' => $containerLogs
])
]);
}
public function recreate()
{
$dockerContainerApi = new DockerContainerApi();
$dockerContainerApi->setImage($this->record->image);
$dockerContainerApi->setEnvironmentVariables($this->record->environment_variables);
$dockerContainerApi->setVolumeMapping($this->record->volume_mapping);
$dockerContainerApi->setPort($this->record->port);
$dockerContainerApi->setExternalPort($this->record->external_port);
$newDockerImage = $dockerContainerApi->recreate($this->record->docker_id);
if (!isset($newDockerImage['ID'])) {
return false;
}
$this->record->image = $newDockerImage['Image'];
$this->record->command = $newDockerImage['Command'];
$this->record->labels = $newDockerImage['Labels'];
$this->record->local_volumes = $newDockerImage['LocalVolumes'];
$this->record->mounts = $newDockerImage['Mounts'];
$this->record->names = $newDockerImage['Names'];
$this->record->networks = $newDockerImage['Networks'];
$this->record->ports = $newDockerImage['Ports'];
$this->record->running_for = $newDockerImage['RunningFor'];
$this->record->size = $newDockerImage['Size'];
$this->record->save();
}
protected function getHeaderActions(): array
{
return [
// DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Modules\Docker\Filament\Clusters;
use Filament\Clusters\Cluster;
class DockerCluster extends Cluster
{
protected static ?string $navigationIcon = 'docker-logo';
protected static ?string $navigationGroup = 'Server Management';
}

View File

@ -0,0 +1,15 @@
<?php
namespace Modules\Docker;
class PostInstall
{
public function run()
{
$installDockerShellFile = base_path('Modules/Docker/shell-scripts/install-docker.sh');
shell_exec("chmod +x $installDockerShellFile");
shell_exec("bash $installDockerShellFile");
}
}

View File

@ -0,0 +1,31 @@
{
"name": "nwidart/docker",
"description": "",
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"extra": {
"laravel": {
"providers": [],
"aliases": {
}
}
},
"autoload": {
"psr-4": {
"Modules\\Docker\\": "",
"Modules\\Docker\\App\\": "app/",
"Modules\\Docker\\Database\\Factories\\": "database/factories/",
"Modules\\Docker\\Database\\Seeders\\": "database/seeders/"
}
},
"autoload-dev": {
"psr-4": {
"Modules\\Docker\\Tests\\": "tests/"
}
}
}

View File

View File

@ -0,0 +1,5 @@
<?php
return [
'name' => 'Docker',
];

View File

@ -0,0 +1,11 @@
{
"name": "Docker",
"alias": "docker",
"description": "",
"keywords": [],
"priority": 0,
"providers": [
"Modules\\Docker\\App\\Providers\\DockerServiceProvider"
],
"files": []
}

View File

@ -0,0 +1,15 @@
{
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build"
},
"devDependencies": {
"axios": "^1.1.2",
"laravel-vite-plugin": "^0.7.5",
"sass": "^1.69.5",
"postcss": "^8.3.7",
"vite": "^4.0.0"
}
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="currentColor" width="800px" height="800px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg"><path d="M13.983 11.078h2.119a.186.186 0 0 0 .186-.185V9.006a.186.186 0 0 0-.186-.186h-2.119a.185.185 0 0 0-.185.185v1.888c0 .102.083.185.185.185m-2.954-5.43h2.118a.186.186 0 0 0 .186-.186V3.574a.186.186 0 0 0-.186-.185h-2.118a.185.185 0 0 0-.185.185v1.888c0 .102.082.185.185.185m0 2.716h2.118a.187.187 0 0 0 .186-.186V6.29a.186.186 0 0 0-.186-.185h-2.118a.185.185 0 0 0-.185.185v1.887c0 .102.082.185.185.186m-2.93 0h2.12a.186.186 0 0 0 .184-.186V6.29a.185.185 0 0 0-.185-.185H8.1a.185.185 0 0 0-.185.185v1.887c0 .102.083.185.185.186m-2.964 0h2.119a.186.186 0 0 0 .185-.186V6.29a.185.185 0 0 0-.185-.185H5.136a.186.186 0 0 0-.186.185v1.887c0 .102.084.185.186.186m5.893 2.715h2.118a.186.186 0 0 0 .186-.185V9.006a.186.186 0 0 0-.186-.186h-2.118a.185.185 0 0 0-.185.185v1.888c0 .102.082.185.185.185m-2.93 0h2.12a.185.185 0 0 0 .184-.185V9.006a.185.185 0 0 0-.184-.186h-2.12a.185.185 0 0 0-.184.185v1.888c0 .102.083.185.185.185m-2.964 0h2.119a.185.185 0 0 0 .185-.185V9.006a.185.185 0 0 0-.184-.186h-2.12a.186.186 0 0 0-.186.186v1.887c0 .102.084.185.186.185m-2.92 0h2.12a.185.185 0 0 0 .184-.185V9.006a.185.185 0 0 0-.184-.186h-2.12a.185.185 0 0 0-.184.185v1.888c0 .102.082.185.185.185M23.763 9.89c-.065-.051-.672-.51-1.954-.51-.338.001-.676.03-1.01.087-.248-1.7-1.653-2.53-1.716-2.566l-.344-.199-.226.327c-.284.438-.49.922-.612 1.43-.23.97-.09 1.882.403 2.661-.595.332-1.55.413-1.744.42H.751a.751.751 0 0 0-.75.748 11.376 11.376 0 0 0 .692 4.062c.545 1.428 1.355 2.48 2.41 3.124 1.18.723 3.1 1.137 5.275 1.137a15.74 15.74 0 0 0 2.93-.266 12.248 12.248 0 0 0 3.823-1.389 10.51 10.51 0 0 0 2.61-2.136c1.252-1.418 1.998-2.997 2.553-4.4h.221c1.372 0 2.215-.549 2.68-1.009.309-.293.55-.65.707-1.046l.098-.288z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,7 @@
<div>
{{ $getAction('stop') }}
{{ $getAction('start') }}
{{ $getAction('restart') }}
{{ $getAction('recreate') }}
{{ $getAction('delete') }}
</div>

View File

@ -0,0 +1,5 @@
<div>
<div class="bg-black/5 p-4 rounded">
{!! $this->containerLog !!}
</div>
</div>

View File

@ -0,0 +1,203 @@
<div xmlns:x-filament="http://www.w3.org/1999/html">
<div class=" mt-6">
<x-filament::breadcrumbs :breadcrumbs="[
'/' => 'Home',
'/admin/docker' => 'Docker',
]" />
</div>
@if (!empty($dockerContainers))
<div class="mt-6">
<div class="text-xl">Your Containers</div>
<div class="grid sm:grid-cols-2 gap-6 my-6">
@foreach($dockerContainers as $dockerContainer)
<div class="rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10">
<div class="flex items-center gap-2 px-6 py-2 rounded-t-xl bg-black/5">
<div>
<svg xmlns="http://www.w3.org/2000/svg" class="w-8" viewBox="0 0 32 32">
<path fill="#0096e6" d="M16.54 12.663h2.86v2.924h1.446a6.272 6.272 0 0 0 1.988-.333a5.091 5.091 0 0 0 .966-.436a3.584 3.584 0 0 1-.67-1.849a3.907 3.907 0 0 1 .7-2.753l.3-.348l.358.288a4.558 4.558 0 0 1 1.795 2.892a4.375 4.375 0 0 1 3.319.309l.393.226l-.207.4a4.141 4.141 0 0 1-4.157 1.983c-2.48 6.168-7.871 9.088-14.409 9.088c-3.378 0-6.476-1.263-8.241-4.259l-.029-.049l-.252-.519a8.316 8.316 0 0 1-.659-4.208l.04-.433h2.445v-2.923h2.861V9.8h5.721V6.942h3.432z"></path>
<path fill="#fff" d="M12.006 24.567a6.022 6.022 0 0 1-3.14-3.089a10.329 10.329 0 0 1-2.264.343q-.5.028-1.045.028q-.632 0-1.331-.037a9.051 9.051 0 0 0 7 2.769q.392 0 .78-.014M7.08 13.346h.2v2.067h-.2Zm-.376 0h.2v2.067H6.7v-2.067Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.368 0h.2v2.067h-.2zM5 13.14h2.482v2.479H5Zm2.859-2.861h2.48v2.479H7.863Zm2.077.207h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.368 0h.2v2.066h-.2Zm-.207 2.653h2.48v2.48H7.863V13.14Zm2.077.207h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.368 0h.2v2.067h-.2Zm2.654-.207H13.2v2.48h-2.48V13.14Zm2.076.207H13v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.368 0h.2v2.067h-.2Zm-.206-3.067H13.2v2.479h-2.48v-2.479Zm2.076.207H13v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.368 0h.2v2.066h-.2Zm2.654 2.653h2.479v2.48h-2.48V13.14Zm2.076.207h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.368 0h.192v2.067h-.2v-2.067Zm-.206-3.067h2.479v2.479h-2.48zm2.076.207h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.368 0h.192v2.066h-.2v-2.066Zm-.206-3.067h2.479V9.9h-2.48zm2.076.206h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.376 0h.2v2.066h-.2Zm-.368 0h.192v2.066h-.2V7.625Zm2.654 5.514h2.479v2.48h-2.48V13.14Zm2.076.207h.195v2.067h-.2v-2.067Zm-.376 0h.206v2.067h-.206Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.2Zm-.376 0h.2v2.067h-.205v-2.067Zm-.368 0h.2v2.067h-.194v-2.067Zm-6.442 6.292a.684.684 0 1 1-.684.684a.684.684 0 0 1 .684-.684m0 .194a.489.489 0 0 1 .177.033a.2.2 0 1 0 .275.269a.49.49 0 1 1-.453-.3Z"></path>
</svg>
</div>
<div class="font-extralight text-xl">
{{ $dockerContainer['name'] }} #{{ $dockerContainer['id']}}
</div>
<div>
@if($dockerContainer['state'] == 'running')
<x-filament::badge color="success">
Running
</x-filament::badge>
@else
<x-filament::badge color="danger">
Stopped
</x-filament::badge>
@endif
</div>
</div>
<div class="p-6">
<div>
Image: {{ $dockerContainer['image'] }}
</div>
@if (!empty($dockerContainer['port']))
<div>
Ports: {{ $dockerContainer['port'] }} -> {{ $dockerContainer['external_port'] }}
</div>
@endif
@if (!empty($dockerContainer['status']))
<div>
Status: {{ $dockerContainer['status'] }}
</div>
@endif
<div class="flex gap-3 mt-4">
<x-filament::button id="stop-docker-container-{{ $dockerContainer['id'] }}" wire:click="stopDockerContainer('{{ $dockerContainer['id'] }}')" size="xs">
Stop
</x-filament::button>
<x-filament::button id="restart-docker-container-{{ $dockerContainer['id'] }}" wire:click="restartDockerContainer('{{ $dockerContainer['id'] }}')" size="xs">
Restart
</x-filament::button>
<x-filament::button id="remove-docker-container-{{ $dockerContainer['id'] }}" wire:click="removeDockerContainer('{{ $dockerContainer['id'] }}')" size="xs" color="danger">
Remove
</x-filament::button>
</div>
<div class="mt-4">
<hr />
</div>
<div class="flex justify-between mt-4">
<x-filament::button id="docker-container-details-{{ $dockerContainer['id'] }}"
tag="a"
href="{{route('filament.admin.docker.resources.docker-containers.view', $dockerContainer['id'])}}"
size="md" outlined>
<div class="flex gap-2 items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6" viewBox="0 0 24 24">
<path fill="currentColor" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2m1 15h-2v-6h2zm0-8h-2V7h2z"></path>
</svg>
Details
</div>
</x-filament::button>
<x-filament::button id="docker-container-logs-{{ $dockerContainer['id'] }}"
tag="a"
href="{{route('filament.admin.docker.resources.docker-containers.view', $dockerContainer['id'])}}"
size="md" outlined>
<div class="flex gap-2 items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6" viewBox="0 0 15 15">
<path fill="currentColor" d="M4.5 6.995H4v1h.5zm6 1h.5v-1h-.5zm-6 1.998H4v1h.5zm6 1.007h.5v-1h-.5zm-6-7.003H4v1h.5zM8.5 5H9V4h-.5zm2-4.5l.354-.354L10.707 0H10.5zm3 3h.5v-.207l-.146-.147zm-9 4.495h6v-1h-6zm0 2.998l6 .007v-1l-6-.007zm0-5.996L8.5 5V4l-4-.003zm8 9.003h-10v1h10zM2 13.5v-12H1v12zM2.5 1h8V0h-8zM13 3.5v10h1v-10zM10.146.854l3 3l.708-.708l-3-3zM2.5 14a.5.5 0 0 1-.5-.5H1A1.5 1.5 0 0 0 2.5 15zm10 1a1.5 1.5 0 0 0 1.5-1.5h-1a.5.5 0 0 1-.5.5zM2 1.5a.5.5 0 0 1 .5-.5V0A1.5 1.5 0 0 0 1 1.5z"></path>
</svg>
Logs
</div>
</x-filament::button>
<x-filament::button id="docker-container-settings-{{ $dockerContainer['id'] }}"
tag="a"
href="{{route('filament.admin.docker.resources.docker-containers.edit', $dockerContainer['id'])}}"
size="md" outlined>
<div class="flex gap-2 items-center">
<svg xmlns="http://www.w3.org/2000/svg" class="w-6" viewBox="0 0 16 16">
<path fill="currentColor" d="M2.267 6.153A6 6 0 0 1 3.53 3.98a.36.36 0 0 1 .382-.095l1.36.484a.71.71 0 0 0 .935-.538l.26-1.416a.35.35 0 0 1 .274-.282a6.1 6.1 0 0 1 2.52 0c.14.03.248.141.274.282l.26 1.416a.708.708 0 0 0 .935.538l1.36-.484a.36.36 0 0 1 .382.095a6 6 0 0 1 1.262 2.173a.35.35 0 0 1-.108.378l-1.102.931a.703.703 0 0 0 0 1.076l1.102.931c.11.093.152.242.108.378a6 6 0 0 1-1.262 2.173a.36.36 0 0 1-.382.095l-1.36-.484a.71.71 0 0 0-.935.538l-.26 1.416a.35.35 0 0 1-.275.282a6.1 6.1 0 0 1-2.519 0a.35.35 0 0 1-.275-.282l-.259-1.416a.708.708 0 0 0-.935-.538l-1.36.484a.36.36 0 0 1-.382-.095a6 6 0 0 1-1.262-2.173a.35.35 0 0 1 .108-.378l1.102-.931a.704.704 0 0 0 0-1.076l-1.102-.931a.35.35 0 0 1-.108-.378M6.25 8a1.75 1.75 0 1 0 3.5 0a1.75 1.75 0 0 0-3.5 0"></path>
</svg>
Settings
</div>
</x-filament::button>
</div>
</div>
</div>
@endforeach
</div>
</div>
@endif
<div class="mt-6">
<div class="text-xl mb-2">Docker Image Catalog</div>
{{ $this->form }}
</div>
<x-filament::modal width="4xl" id="pull-docker-image">
<x-filament::modal.heading>
Pull Docker Image
</x-filament::modal.heading>
<x-filament::modal.description>
<div class="">
@if ($this->pullLogPulling)
<div class="w-full">
<div id="js-pull-log" wire:poll="getPullLog"
class="text-left text-sm font-medium text-gray-950 dark:text-yellow-500 h-[20rem] overflow-y-scroll">
{!! $this->pullLog !!}
</div>
<script>
window.setInterval(function() {
var elem = document.getElementById('js-pull-log');
elem.scrollTop = elem.scrollHeight;
}, 2000);
</script>
</div>
@endif
</div>
</x-filament::modal.description>
</x-filament::modal>
<div class="grid sm:grid-cols-2 gap-6 my-6">
@foreach($dockerImages as $dockerImage)
<div class="sm:flex gap-2 p-6 rounded-xl bg-white shadow-sm ring-1 ring-gray-950/5 dark:bg-gray-900 dark:ring-white/10">
<div class="flex flex-col items-center justify-center">
<div>
<svg xmlns="http://www.w3.org/2000/svg" class="w-[6rem]" viewBox="0 0 32 32">
<path fill="#3a4e55" d="M18.191 13.071H20.7v2.566h1.27a5.5 5.5 0 0 0 1.744-.292a4.462 4.462 0 0 0 .848-.383a3.149 3.149 0 0 1-.589-1.623a3.427 3.427 0 0 1 .616-2.416l.264-.305l.314.253a4 4 0 0 1 1.575 2.538a3.837 3.837 0 0 1 2.913.271l.345.2l-.181.354a3.629 3.629 0 0 1-3.648 1.74c-2.173 5.413-6.9 7.976-12.642 7.976A7.958 7.958 0 0 1 6.3 20.211l-.025-.043l-.226-.459a7.28 7.28 0 0 1-.579-3.693l.035-.38h2.143v-2.565h2.51v-2.51h5.02v-2.51h3.012v5.02Z"></path>
<path fill="#00aada" d="M26.324 14.021a3.311 3.311 0 0 0-1.418-2.821a3.072 3.072 0 0 0 .289 3.821a5.279 5.279 0 0 1-3.225 1.037H5.883a6.779 6.779 0 0 0 .667 3.737l.183.335a6.2 6.2 0 0 0 .379.569q.992.064 1.829.045a8.972 8.972 0 0 0 2.669-.389a.193.193 0 1 1 .126.365c-.09.031-.184.061-.281.088a8.4 8.4 0 0 1-1.845.3c.044 0-.046.007-.046.007l-.082.007a21.455 21.455 0 0 1-2.008-.006l-.01.007a7.882 7.882 0 0 0 6.063 2.41c5.56 0 10.276-2.465 12.365-8c1.482.152 2.906-.226 3.553-1.49a3.5 3.5 0 0 0-3.122-.022"></path>
<path fill="#27b9ec" d="M26.324 14.021a3.311 3.311 0 0 0-1.418-2.821a3.072 3.072 0 0 0 .289 3.821a5.279 5.279 0 0 1-3.225 1.037H6.836a5.223 5.223 0 0 0 2.106 4.686a8.972 8.972 0 0 0 2.669-.389a.193.193 0 1 1 .126.365c-.09.031-.184.061-.281.088a8.83 8.83 0 0 1-1.894.314l-.019-.022c1.892.971 4.636.967 7.782-.241a21.868 21.868 0 0 0 9.1-6.889l-.1.048"></path>
<path fill="#088cb9" d="M5.913 17.732a6.431 6.431 0 0 0 .637 2.061l.183.335a6.2 6.2 0 0 0 .379.569q.992.064 1.829.045a8.972 8.972 0 0 0 2.669-.389a.193.193 0 1 1 .126.365c-.09.031-.184.061-.281.088a8.826 8.826 0 0 1-1.891.307h-.1c-.291.016-.6.026-.922.026c-.351 0-.709-.007-1.1-.026a7.913 7.913 0 0 0 6.076 2.413c4.76 0 8.9-1.807 11.3-5.8Z"></path>
<path fill="#039cc7" d="M6.98 17.732a4.832 4.832 0 0 0 1.961 3.01a8.972 8.972 0 0 0 2.669-.389a.193.193 0 1 1 .126.365c-.09.031-.184.061-.281.088a8.959 8.959 0 0 1-1.9.307c1.892.971 4.628.957 7.773-.252a20.545 20.545 0 0 0 5.377-3.13Z"></path>
<path fill="#00acd3" d="M9.889 13.671h.172v1.813h-.172zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813H9.23v-1.813Zm-.33 0h.179v1.813H8.9v-1.813Zm-.33 0h.179v1.813H8.57zm-.323 0h.172v1.813h-.17v-1.813Zm-.181-.181h2.175v2.176H8.066V13.49Zm4.335-2.329h.172v1.813H12.4zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.178v1.813h-.178zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176h-2.175z"></path>
<path fill="#26c2ee" d="M12.4 13.671h.172v1.813H12.4zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.178v1.813h-.178zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176h-2.175z"></path>
<path fill="#00acd3" d="M14.909 13.671h.172v1.813h-.172zm-.33 0h.179v1.813h-.178zm-.33 0h.179v1.813h-.178zm-.33 0h.181v1.813h-.179v-1.813Zm-.33 0h.179v1.813h-.179zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176h-2.174V13.49Z"></path>
<path fill="#26c2ee" d="M14.909 11.161h.172v1.813h-.172zm-.33 0h.179v1.813h-.178zm-.33 0h.179v1.813h-.178zm-.33 0h.181v1.813h-.179v-1.813Zm-.33 0h.179v1.813h-.179zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176h-2.174v-2.177Zm4.335 2.691h.172v1.813h-.172zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813H16.1zm-.323 0h.172v1.813h-.172zm-.177-.181h2.176v2.176H15.6z"></path>
<path fill="#00acd3" d="M17.42 11.161h.172v1.813h-.172zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813H16.1zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176H15.6v-2.177Z"></path>
<path fill="#26c2ee" d="M17.42 8.65h.172v1.813h-.172zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813H16.1zm-.323 0h.172v1.813h-.172zm-.177-.181h2.176v2.176H15.6z"></path>
<path fill="#00acd3" d="M19.93 13.671h.17v1.813h-.17zm-.33 0h.178v1.813H19.6zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.33 0h.179v1.813h-.179zm-.323 0h.172v1.813h-.172zm-.181-.181h2.176v2.176h-2.175z"></path>
<path fill="#d5eef2" d="M12.616 19.193a.6.6 0 1 1-.6.6a.6.6 0 0 1 .6-.6"></path>
<path fill="#3a4e55" d="M12.616 19.363a.431.431 0 0 1 .156.029a.175.175 0 1 0 .241.236a.43.43 0 1 1-.4-.265M2 17.949h27.92c-.608-.154-1.923-.362-1.707-1.159c-1.105 1.279-3.771.9-4.444.267c-.749 1.087-5.111.674-5.415-.173c-.939 1.1-3.85 1.1-4.789 0c-.3.847-4.666 1.26-5.415.173c-.673.631-3.338 1.012-4.444-.267c.217.8-1.1 1.005-1.707 1.159"></path>
<path fill="#c0dbe1" d="M14.211 23.518a5.287 5.287 0 0 1-2.756-2.711a9.2 9.2 0 0 1-1.987.3q-.436.024-.917.025q-.554 0-1.168-.033a7.942 7.942 0 0 0 6.145 2.43q.344 0 .683-.013"></path>
<path fill="#d5eef2" d="M12.007 21.773a5.206 5.206 0 0 1-.552-.966a9.2 9.2 0 0 1-1.987.3a6.325 6.325 0 0 0 2.539.664"></path>
</svg>
</div>
</div>
<div class="w-full flex flex-col justify-center">
<div class="text-lg font-semibold">
{{ $dockerImage['name'] }}
</div>
<div class="text-sm mt-2">
{{ $dockerImage['description'] }}
</div>
<div class="flex justify-between items-center mt-4">
<div class="flex gap-2">
@if($dockerImage['is_official'])
<x-filament::badge color="success">
Official
</x-filament::badge>
@else
<x-filament::badge color="info">
Community
</x-filament::badge>
@endif
<x-filament::badge color="warning">
{{ $dockerImage['star_count'] }} Stars
</x-filament::badge>
</div>
<div>
<x-filament::button wire:key="docker-image-{{$dockerImage['id']}}" wire:click="pullDockerImage('{{ $dockerImage['name'] }}')" class="bg-blue-500/90 text-white">
Run
</x-filament::button>
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>

View File

@ -0,0 +1,7 @@
@extends('docker::layouts.master')
@section('content')
<h1>Hello World</h1>
<p>Module: {!! config('docker.name') !!}</p>
@endsection

View File

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>Docker Module - {{ config('app.name', 'Laravel') }}</title>
<meta name="description" content="{{ $description ?? '' }}">
<meta name="keywords" content="{{ $keywords ?? '' }}">
<meta name="author" content="{{ $author ?? '' }}">
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
{{-- Vite CSS --}}
{{-- {{ module_vite('build-docker', 'resources/assets/sass/app.scss') }} --}}
</head>
<body>
@yield('content')
{{-- Vite JS --}}
{{-- {{ module_vite('build-docker', 'resources/assets/js/app.js') }} --}}
</body>

View File

View File

@ -0,0 +1,19 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware(['auth:sanctum'])->prefix('v1')->name('api.')->group(function () {
Route::get('docker', fn (Request $request) => $request->user())->name('docker');
});

View File

@ -0,0 +1,19 @@
<?php
use Illuminate\Support\Facades\Route;
use Modules\Docker\App\Http\Controllers\DockerController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::group([], function () {
Route::resource('docker', DockerController::class)->names('docker');
});

View File

@ -0,0 +1,14 @@
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

View File

@ -0,0 +1,26 @@
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
build: {
outDir: '../../public/build-docker',
emptyOutDir: true,
manifest: true,
},
plugins: [
laravel({
publicDirectory: '../../public',
buildDirectory: 'build-docker',
input: [
__dirname + '/resources/assets/sass/app.scss',
__dirname + '/resources/assets/js/app.js'
],
refresh: true,
}),
],
});
//export const paths = [
// 'Modules/$STUDLY_NAME$/resources/assets/sass/app.scss',
// 'Modules/$STUDLY_NAME$/resources/assets/js/app.js',
//];

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More