Merge pull request #313 from pelican-dev/issue/311

Docker
This commit is contained in:
Lance Pioch 2024-09-27 18:01:00 -04:00 committed by GitHub
commit a067419d6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 204 additions and 137 deletions

10
.dockerignore Normal file
View 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

View File

@ -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

View File

@ -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 "$@"

View File

@ -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
View 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
}

View File

@ -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" ]

View File

@ -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();

View File

@ -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()

View File

@ -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)

View File

@ -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');
}

View File

@ -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
View 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

View File

@ -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",

View File

@ -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),
],

View File

@ -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),
],
],
/*