mirror of
https://github.com/pelican-dev/panel.git
synced 2025-05-20 00:34:44 +02:00
commit
a067419d6e
10
.dockerignore
Normal file
10
.dockerignore
Normal file
@ -0,0 +1,10 @@
|
||||
.git
|
||||
node_modules
|
||||
vendor
|
||||
database/database.sqlite
|
||||
storage/debugbar/*.json
|
||||
storage/logs/*.log
|
||||
storage/framework/cache/data/*
|
||||
storage/framework/sessions/*
|
||||
storage/framework/testing
|
||||
storage/framework/views/*.php
|
@ -4,7 +4,6 @@ APP_KEY=
|
||||
APP_TIMEZONE=UTC
|
||||
APP_URL=http://panel.test
|
||||
APP_LOCALE=en
|
||||
APP_INSTALLED=false
|
||||
|
||||
LOG_CHANNEL=daily
|
||||
LOG_STACK=single
|
||||
|
83
.github/docker/entrypoint.sh
vendored
83
.github/docker/entrypoint.sh
vendored
@ -1,81 +1,58 @@
|
||||
#!/bin/ash -e
|
||||
cd /app
|
||||
|
||||
mkdir -p /var/log/panel/logs/ /var/log/supervisord/ /var/log/nginx/ /var/log/php8/ \
|
||||
&& chmod 777 /var/log/panel/logs/ \
|
||||
&& ln -s /app/storage/logs/ /var/log/panel/
|
||||
#mkdir -p /var/log/supervisord/ /var/log/php8/ \
|
||||
|
||||
## check for .env file and generate app keys if missing
|
||||
if [ -f /app/var/.env ]; then
|
||||
if [ -f /pelican-data/.env ]; then
|
||||
echo "external vars exist."
|
||||
rm -rf /app/.env
|
||||
ln -s /app/var/.env /app/
|
||||
rm -rf /var/www/html/.env
|
||||
else
|
||||
echo "external vars don't exist."
|
||||
rm -rf /app/.env
|
||||
touch /app/var/.env
|
||||
rm -rf /var/www/html/.env
|
||||
touch /pelican-data/.env
|
||||
|
||||
## manually generate a key because key generate --force fails
|
||||
if [ -z $APP_KEY ]; then
|
||||
echo -e "Generating key."
|
||||
APP_KEY=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
|
||||
echo -e "Generated app key: $APP_KEY"
|
||||
echo -e "APP_KEY=$APP_KEY" > /app/var/.env
|
||||
echo -e "APP_KEY=$APP_KEY" > /pelican-data/.env
|
||||
else
|
||||
echo -e "APP_KEY exists in environment, using that."
|
||||
echo -e "APP_KEY=$APP_KEY" > /app/var/.env
|
||||
echo -e "APP_KEY=$APP_KEY" > /pelican-data/.env
|
||||
fi
|
||||
fi
|
||||
|
||||
ln -s /app/var/.env /app/
|
||||
fi
|
||||
mkdir /pelican-data/database
|
||||
ln -s /pelican-data/.env /var/www/html/
|
||||
ln -s /pelican-data/database/database.sqlite /var/www/html/database/
|
||||
|
||||
echo "Checking if https is required."
|
||||
if [ -f /etc/nginx/http.d/panel.conf ]; then
|
||||
echo "Using nginx config already in place."
|
||||
if [ $LE_EMAIL ]; then
|
||||
echo "Checking for cert update"
|
||||
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
|
||||
if ! grep -q "APP_KEY=" .env || grep -q "APP_KEY=$" .env; then
|
||||
echo "Generating APP_KEY..."
|
||||
php artisan key:generate --force
|
||||
else
|
||||
echo "No letsencrypt email is set"
|
||||
echo "APP_KEY is already set."
|
||||
fi
|
||||
else
|
||||
echo "Checking if letsencrypt email is set."
|
||||
if [ -z $LE_EMAIL ]; then
|
||||
echo "No letsencrypt email is set using http config."
|
||||
cp .github/docker/default.conf /etc/nginx/http.d/panel.conf
|
||||
else
|
||||
echo "writing ssl config"
|
||||
cp .github/docker/default_ssl.conf /etc/nginx/http.d/panel.conf
|
||||
echo "updating ssl config for domain"
|
||||
sed -i "s|<domain>|$(echo $APP_URL | sed 's~http[s]*://~~g')|g" /etc/nginx/http.d/panel.conf
|
||||
echo "generating certs"
|
||||
certbot certonly -d $(echo $APP_URL | sed 's~http[s]*://~~g') --standalone -m $LE_EMAIL --agree-tos -n
|
||||
fi
|
||||
echo "Removing the default nginx config"
|
||||
rm -rf /etc/nginx/http.d/default.conf
|
||||
fi
|
||||
|
||||
if [[ -z $DB_PORT ]]; then
|
||||
echo -e "DB_PORT not specified, defaulting to 3306"
|
||||
DB_PORT=3306
|
||||
fi
|
||||
|
||||
## check for DB up before starting the panel
|
||||
echo "Checking database status."
|
||||
until nc -z -v -w30 $DB_HOST $DB_PORT
|
||||
do
|
||||
echo "Waiting for database connection..."
|
||||
# wait for 1 seconds before check again
|
||||
sleep 1
|
||||
done
|
||||
|
||||
## make sure the db is set up
|
||||
echo -e "Migrating and Seeding D.B"
|
||||
php artisan migrate --seed --force
|
||||
echo -e "Migrating Database"
|
||||
php artisan migrate --force
|
||||
|
||||
## start cronjobs for the queue
|
||||
echo -e "Starting cron jobs."
|
||||
crond -L /var/log/crond -l 5
|
||||
|
||||
echo -e "Starting supervisord."
|
||||
export SUPERVISORD_CADDY=false
|
||||
|
||||
## disable caddy if SKIP_CADDY is set
|
||||
if [[ -z $SKIP_CADDY ]]; then
|
||||
echo "Starting PHP-FPM and Caddy"
|
||||
export SUPERVISORD_CADDY=true
|
||||
else
|
||||
echo "Starting PHP-FPM only"
|
||||
fi
|
||||
|
||||
chown -R www-data:www-data . /pelican-data/.env /pelican-data/database
|
||||
|
||||
echo "Starting Supervisord"
|
||||
exec "$@"
|
||||
|
12
.github/docker/supervisord.conf
vendored
12
.github/docker/supervisord.conf
vendored
@ -25,15 +25,15 @@ autostart=true
|
||||
autorestart=true
|
||||
|
||||
[program:queue-worker]
|
||||
command=/usr/local/bin/php /app/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
|
||||
user=nginx
|
||||
command=/usr/local/bin/php /var/www/html/artisan queue:work --queue=high,standard,low --sleep=3 --tries=3
|
||||
user=www-data
|
||||
autostart=true
|
||||
autorestart=true
|
||||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx -g 'daemon off;'
|
||||
autostart=true
|
||||
autorestart=true
|
||||
[program:caddy]
|
||||
command=caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
autostart=%(ENV_SUPERVISORD_CADDY)s
|
||||
autorestart=%(ENV_SUPERVISORD_CADDY)s
|
||||
priority=10
|
||||
stdout_events_enabled=true
|
||||
stderr_events_enabled=true
|
11
Caddyfile
Normal file
11
Caddyfile
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
email {$ADMIN_EMAIL}
|
||||
}
|
||||
|
||||
{$APP_URL} {
|
||||
root * /var/www/html/public
|
||||
encode gzip
|
||||
|
||||
php_fastcgi 127.0.0.1:9000
|
||||
file_server
|
||||
}
|
85
Dockerfile
85
Dockerfile
@ -1,41 +1,58 @@
|
||||
# Stage 0:
|
||||
# Build the assets that are needed for the frontend. This build stage is then discarded
|
||||
# since we won't need NodeJS anymore in the future. This Docker image ships a final production
|
||||
# level distribution
|
||||
FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine
|
||||
WORKDIR /app
|
||||
# Pelican Production Dockerfile
|
||||
|
||||
FROM node:20-alpine AS yarn
|
||||
#FROM --platform=$TARGETOS/$TARGETARCH node:20-alpine AS yarn
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
COPY . ./
|
||||
RUN yarn install --frozen-lockfile \
|
||||
&& yarn run build:production
|
||||
|
||||
# Stage 1:
|
||||
# Build the actual container with all of the needed PHP dependencies that will run the application.
|
||||
FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
|
||||
WORKDIR /app
|
||||
COPY . ./
|
||||
COPY --from=0 /app/public/assets ./public/assets
|
||||
RUN apk add --no-cache --update ca-certificates dcron curl git supervisor tar unzip nginx libpng-dev libxml2-dev libzip-dev icu-dev certbot certbot-nginx \
|
||||
&& docker-php-ext-configure zip \
|
||||
&& docker-php-ext-install bcmath gd intl pdo_mysql zip \
|
||||
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
|
||||
&& cp .env.example .env \
|
||||
&& mkdir -p bootstrap/cache/ storage/logs storage/framework/sessions storage/framework/views storage/framework/cache \
|
||||
&& chmod 777 -R bootstrap storage \
|
||||
&& composer install --no-dev --optimize-autoloader \
|
||||
&& rm -rf .env bootstrap/cache/*.php \
|
||||
&& mkdir -p /app/storage/logs/ \
|
||||
&& chown -R nginx:nginx .
|
||||
RUN yarn install --frozen-lockfile && yarn run build:production
|
||||
|
||||
RUN rm /usr/local/etc/php-fpm.conf \
|
||||
&& echo "* * * * * /usr/local/bin/php /app/artisan schedule:run >> /dev/null 2>&1" >> /var/spool/cron/crontabs/root \
|
||||
&& echo "0 23 * * * certbot renew --nginx --quiet" >> /var/spool/cron/crontabs/root \
|
||||
&& sed -i s/ssl_session_cache/#ssl_session_cache/g /etc/nginx/nginx.conf \
|
||||
&& mkdir -p /var/run/php /var/run/nginx
|
||||
FROM php:8.3-fpm-alpine
|
||||
# FROM --platform=$TARGETOS/$TARGETARCH php:8.3-fpm-alpine
|
||||
|
||||
COPY .github/docker/default.conf /etc/nginx/http.d/default.conf
|
||||
COPY .github/docker/www.conf /usr/local/etc/php-fpm.conf
|
||||
COPY .github/docker/supervisord.conf /etc/supervisord.conf
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Install dependencies
|
||||
RUN apk update && apk add --no-cache \
|
||||
libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev icu-dev \
|
||||
zip unzip curl \
|
||||
caddy ca-certificates supervisor \
|
||||
&& docker-php-ext-install bcmath gd intl zip opcache pcntl posix pdo_mysql
|
||||
|
||||
# Copy the Caddyfile to the container
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
|
||||
# Copy the application code to the container
|
||||
COPY . .
|
||||
|
||||
COPY --from=yarn /build/public/assets ./public/assets
|
||||
|
||||
RUN touch .env
|
||||
|
||||
RUN composer install --no-dev --optimize-autoloader
|
||||
|
||||
# Set file permissions
|
||||
RUN chmod -R 755 /var/www/html/storage \
|
||||
&& chmod -R 755 /var/www/html/bootstrap/cache
|
||||
|
||||
# Add scheduler to cron
|
||||
RUN echo "* * * * * php /var/www/html/artisan schedule:run >> /dev/null 2>&1" | crontab -u www-data -
|
||||
|
||||
## supervisord config and log dir
|
||||
RUN cp .github/docker/supervisord.conf /etc/supervisord.conf && \
|
||||
mkdir /var/log/supervisord/
|
||||
|
||||
HEALTHCHECK --interval=5m --timeout=10s --start-period=5s --retries=3 \
|
||||
CMD curl -f http://localhost/up || exit 1
|
||||
|
||||
EXPOSE 80:2019
|
||||
EXPOSE 443
|
||||
|
||||
VOLUME /pelican-data
|
||||
|
||||
EXPOSE 80 443
|
||||
ENTRYPOINT [ "/bin/ash", ".github/docker/entrypoint.sh" ]
|
||||
CMD [ "supervisord", "-n", "-c", "/etc/supervisord.conf" ]
|
||||
|
@ -7,6 +7,7 @@ use App\Filament\Pages\Installer\Steps\DatabaseStep;
|
||||
use App\Filament\Pages\Installer\Steps\EnvironmentStep;
|
||||
use App\Filament\Pages\Installer\Steps\RedisStep;
|
||||
use App\Filament\Pages\Installer\Steps\RequirementsStep;
|
||||
use App\Models\User;
|
||||
use App\Services\Users\UserCreationService;
|
||||
use App\Traits\CheckMigrationsTrait;
|
||||
use App\Traits\EnvironmentWriterTrait;
|
||||
@ -43,11 +44,22 @@ class PanelInstaller extends SimplePage implements HasForms
|
||||
return MaxWidth::SevenExtraLarge;
|
||||
}
|
||||
|
||||
public static function show(): bool
|
||||
{
|
||||
if (User::count() <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (config('panel.client_features.installer.enabled')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function mount()
|
||||
{
|
||||
if (is_installed()) {
|
||||
abort(404);
|
||||
}
|
||||
abort_unless(self::show(), 404);
|
||||
|
||||
$this->form->fill();
|
||||
}
|
||||
@ -122,7 +134,7 @@ class PanelInstaller extends SimplePage implements HasForms
|
||||
$user = app(UserCreationService::class)->handle($userData);
|
||||
|
||||
// Install setup complete
|
||||
$this->writeToEnvironment(['APP_INSTALLED' => 'true']);
|
||||
$this->writeToEnvironment(['APP_INSTALLER' => 'false']);
|
||||
|
||||
$this->rememberData();
|
||||
|
||||
|
@ -16,11 +16,11 @@ class AdminUserStep
|
||||
->label('Admin E-Mail')
|
||||
->required()
|
||||
->email()
|
||||
->default('admin@example.com'),
|
||||
->placeholder('admin@example.com'),
|
||||
TextInput::make('user.username')
|
||||
->label('Admin Username')
|
||||
->required()
|
||||
->default('admin'),
|
||||
->placeholder('admin'),
|
||||
TextInput::make('user.password')
|
||||
->label('Admin Password')
|
||||
->required()
|
||||
|
@ -23,9 +23,9 @@ class EnvironmentStep
|
||||
];
|
||||
|
||||
public const QUEUE_DRIVERS = [
|
||||
'sync' => 'Sync',
|
||||
'database' => 'Database',
|
||||
'redis' => 'Redis',
|
||||
'sync' => 'Synchronous',
|
||||
];
|
||||
|
||||
public const DATABASE_DRIVERS = [
|
||||
@ -76,7 +76,7 @@ class EnvironmentStep
|
||||
ToggleButtons::make('env.QUEUE_CONNECTION')
|
||||
->label('Queue Driver')
|
||||
->hintIcon('tabler-question-mark')
|
||||
->hintIconTooltip('The driver used for handling queues. We recommend "Database".')
|
||||
->hintIconTooltip('The driver used for handling queues. We recommend "Sync" or "Database".')
|
||||
->required()
|
||||
->inline()
|
||||
->options(self::QUEUE_DRIVERS)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Filament\Pages\Installer\PanelInstaller;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Http\Request;
|
||||
@ -17,8 +18,12 @@ class LoginController extends AbstractLoginController
|
||||
* base authentication view component. React will take over at this point and
|
||||
* turn the login area into an SPA.
|
||||
*/
|
||||
public function index(): View
|
||||
public function index()
|
||||
{
|
||||
if (PanelInstaller::show()) {
|
||||
return redirect('/installer');
|
||||
}
|
||||
|
||||
return view('templates/auth.core');
|
||||
}
|
||||
|
||||
|
@ -17,34 +17,3 @@ if (!function_exists('is_ip')) {
|
||||
return $address !== null && filter_var($address, FILTER_VALIDATE_IP) !== false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('object_get_strict')) {
|
||||
/**
|
||||
* Get an object using dot notation. An object key with a value of null is still considered valid
|
||||
* and will not trigger the response of a default value (unlike object_get).
|
||||
*/
|
||||
function object_get_strict(object $object, ?string $key, mixed $default = null): mixed
|
||||
{
|
||||
if (is_null($key) || trim($key) == '') {
|
||||
return $object;
|
||||
}
|
||||
|
||||
foreach (explode('.', $key) as $segment) {
|
||||
if (!is_object($object) || !property_exists($object, $segment)) {
|
||||
return value($default);
|
||||
}
|
||||
|
||||
$object = $object->{$segment};
|
||||
}
|
||||
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('is_installed')) {
|
||||
function is_installed(): bool
|
||||
{
|
||||
// This defaults to true so existing panels count as "installed"
|
||||
return env('APP_INSTALLED', true);
|
||||
}
|
||||
}
|
||||
|
58
compose.yml
Normal file
58
compose.yml
Normal file
@ -0,0 +1,58 @@
|
||||
x-common:
|
||||
panel:
|
||||
&panel-environment
|
||||
APP_URL: "https://localhost" # can be set to 'http://localhost' on port 80 only
|
||||
ADMIN_EMAIL: "USEYOUROWNEMAILHERE@example.com"
|
||||
|
||||
APP_DEBUG: "false"
|
||||
APP_ENVIRONMENT_ONLY: "false"
|
||||
APP_ENV: "production"
|
||||
SESSION_DRIVER: "file"
|
||||
|
||||
mail:
|
||||
&mail-environment
|
||||
MAIL_DRIVER: "log"
|
||||
# MAIL_HOST: ""
|
||||
# MAIL_PORT: ""
|
||||
# MAIL_FROM: ""
|
||||
# MAIL_USERNAME: ""
|
||||
# MAIL_PASSWORD: ""
|
||||
# MAIL_ENCRYPTION: ""
|
||||
|
||||
#
|
||||
# ------------------------------------------------------------------------------------------
|
||||
# DANGER ZONE BELOW
|
||||
#
|
||||
# The remainder of this file likely does not need to be changed. Please only make modifications
|
||||
# below if you understand what you are doing.
|
||||
#
|
||||
|
||||
services:
|
||||
panel:
|
||||
image: ghcr.io/pelican-dev/panel:latest
|
||||
restart: always
|
||||
networks:
|
||||
- default
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
# - "9000:9000" # enable when not using caddy to be abel to reach php-fpm
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway" # shows the panel on te internal docker network as well. usually '172.17.0.1'
|
||||
volumes:
|
||||
- pelican-data:/pelican-data
|
||||
- pelican-logs:/var/www/html/storage/logs
|
||||
environment:
|
||||
<<: [*panel-environment, *mail-environment]
|
||||
XDG_DATA_HOME: /pelican-data
|
||||
# SKIP_CADDY: true # enable when not using caddy.
|
||||
|
||||
volumes:
|
||||
pelican-data:
|
||||
pelican-logs:
|
||||
|
||||
networks:
|
||||
default:
|
||||
ipam:
|
||||
config:
|
||||
- subnet: 172.20.0.0/16
|
@ -7,7 +7,6 @@
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-pdo": "*",
|
||||
"ext-pdo_mysql": "*",
|
||||
"ext-zip": "*",
|
||||
"abdelhamiderrahmouni/filament-monaco-editor": "0.2.1",
|
||||
"aws/aws-sdk-php": "~3.288.1",
|
||||
|
@ -1,5 +1,11 @@
|
||||
<?php
|
||||
|
||||
$database = env('DB_DATABASE', 'database.sqlite');
|
||||
$datapasePath = database_path($database);
|
||||
if (str($database)->startsWith('/')) {
|
||||
$databasePath = $database;
|
||||
}
|
||||
|
||||
return [
|
||||
|
||||
'default' => env('DB_CONNECTION', 'sqlite'),
|
||||
@ -8,7 +14,7 @@ return [
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => database_path(env('DB_DATABASE', 'database.sqlite')),
|
||||
'database' => $datapasePath,
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
],
|
||||
|
@ -74,7 +74,7 @@ return [
|
||||
| Client Features
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Allow clients to create their own databases.
|
||||
| Allow clients to turn features on or off
|
||||
*/
|
||||
|
||||
'client_features' => [
|
||||
@ -93,6 +93,10 @@ return [
|
||||
'range_start' => env('PANEL_CLIENT_ALLOCATIONS_RANGE_START'),
|
||||
'range_end' => env('PANEL_CLIENT_ALLOCATIONS_RANGE_END'),
|
||||
],
|
||||
|
||||
'installer' => [
|
||||
'enabled' => env('APP_INSTALLER', true),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user