chg: pkg: make compatible the whole project with bare metal AND with docker #4
This commit is contained in:
12
.dockerignore
Normal file
12
.dockerignore
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
node_modules
|
||||||
|
var/cache
|
||||||
|
var/log
|
||||||
|
var/sessions
|
||||||
|
public/build
|
||||||
|
*.md
|
||||||
|
docker-compose*.yml
|
||||||
|
compose.override.yaml
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
@@ -16,7 +16,8 @@ APP_SECRET=c1c278747d952ea66326352b72bb8ec6
|
|||||||
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
|
DATABASE_URL=mysql://db_user:db_password@127.0.0.1:3306/db_name
|
||||||
###< doctrine/doctrine-bundle ###
|
###< doctrine/doctrine-bundle ###
|
||||||
###> symfony/mailer ###
|
###> symfony/mailer ###
|
||||||
# MAILER_DSN=smtp://localhost
|
MAILER_DSN=smtp://localhost:1025
|
||||||
|
MAIL_DOMAIN=localhost
|
||||||
###< symfony/mailer ###
|
###< symfony/mailer ###
|
||||||
|
|
||||||
###> symfony/mercure-bundle ###
|
###> symfony/mercure-bundle ###
|
||||||
|
|||||||
25
Caddyfile
Normal file
25
Caddyfile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
{$CADDY_GLOBAL_OPTIONS}
|
||||||
|
|
||||||
|
frankenphp {
|
||||||
|
{$FRANKENPHP_CONFIG}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{$SERVER_NAME:localhost} {
|
||||||
|
log
|
||||||
|
|
||||||
|
root * /app/public
|
||||||
|
|
||||||
|
encode zstd br gzip
|
||||||
|
|
||||||
|
mercure {
|
||||||
|
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
|
||||||
|
publisher_jwt {$MERCURE_JWT_SECRET} HS256
|
||||||
|
subscriber_jwt {$MERCURE_JWT_SECRET} HS256
|
||||||
|
anonymous
|
||||||
|
subscriptions
|
||||||
|
}
|
||||||
|
|
||||||
|
php_server
|
||||||
|
}
|
||||||
52
Dockerfile
Normal file
52
Dockerfile
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
FROM node:22-alpine AS assets
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm ci --ignore-scripts
|
||||||
|
|
||||||
|
COPY vite.config.js ./
|
||||||
|
COPY assets/ assets/
|
||||||
|
COPY public/ public/
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM dunglas/frankenphp:latest
|
||||||
|
|
||||||
|
RUN install-php-extensions \
|
||||||
|
pdo_pgsql \
|
||||||
|
gd \
|
||||||
|
intl \
|
||||||
|
zip \
|
||||||
|
opcache \
|
||||||
|
apcu \
|
||||||
|
sodium
|
||||||
|
|
||||||
|
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
|
||||||
|
RUN printf '[opcache]\nopcache.enable=1\nopcache.memory_consumption=256\nopcache.max_accelerated_files=20000\nopcache.validate_timestamps=0\n' \
|
||||||
|
> "$PHP_INI_DIR/conf.d/opcache.ini"
|
||||||
|
|
||||||
|
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
|
||||||
|
|
||||||
|
ENV APP_ENV=prod
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN composer install \
|
||||||
|
--no-dev \
|
||||||
|
--no-interaction \
|
||||||
|
--no-scripts \
|
||||||
|
--optimize-autoloader
|
||||||
|
|
||||||
|
COPY --from=assets /app/public/build ./public/build
|
||||||
|
|
||||||
|
RUN mkdir -p var/cache var/log var/sessions && \
|
||||||
|
chown -R www-data:www-data var/
|
||||||
|
|
||||||
|
COPY Caddyfile /etc/caddy/Caddyfile
|
||||||
|
COPY docker/entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
48
Makefile
Normal file
48
Makefile
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
.PHONY: help start start-build stop build down ps logs prune-everything db-reset
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := help
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "Available commands:"
|
||||||
|
@echo " make start - Start services without building (uses existing images)"
|
||||||
|
@echo " make start-build - Start services and build images if needed"
|
||||||
|
@echo " make stop - Stop running services"
|
||||||
|
@echo " make build - Build Docker images only"
|
||||||
|
@echo " make down - Stop and remove containers/networks"
|
||||||
|
@echo " make prune-everything - Prune volumes, networks and images (DANGEROUS!)"
|
||||||
|
@echo " make db-reset - Reset the database (drop, create, migrate) (DANGEROUS!)"
|
||||||
|
|
||||||
|
start:
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
start-build:
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
stop:
|
||||||
|
docker compose stop
|
||||||
|
|
||||||
|
build:
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
down:
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
prune-everything:
|
||||||
|
@echo "WARNING: This will remove ALL containers, volumes, networks and images!"
|
||||||
|
@read -p "Type 'yes' to confirm: " confirm; \
|
||||||
|
if [ "$$confirm" != "yes" ]; then \
|
||||||
|
echo "Aborted."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
docker compose down -v --rmi all --remove-orphans
|
||||||
|
|
||||||
|
db-reset:
|
||||||
|
@echo "WARNING: This will DROP and RECREATE the database!"
|
||||||
|
@read -p "Type 'yes' to confirm: " confirm; \
|
||||||
|
if [ "$$confirm" != "yes" ]; then \
|
||||||
|
echo "Aborted."; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
bin/console doctrine:database:drop --force --if-exists --no-interaction
|
||||||
|
bin/console doctrine:database:create --if-not-exists --no-interaction
|
||||||
|
bin/console doctrine:migrations:migrate --no-interaction
|
||||||
69
compose.yaml
Normal file
69
compose.yaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "10080:80"
|
||||||
|
environment:
|
||||||
|
SERVER_NAME: ${SERVER_NAME:-:80}
|
||||||
|
APP_ENV: prod
|
||||||
|
APP_SECRET: ${APP_SECRET}
|
||||||
|
DATABASE_URL: >-
|
||||||
|
postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}?serverVersion=${POSTGRES_VERSION}&charset=utf8
|
||||||
|
POSTGRES_URL: db
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
MERCURE_URL: http://localhost/.well-known/mercure
|
||||||
|
MERCURE_PUBLIC_URL: https://${PUBLIC_HOSTNAME:-localhost}/.well-known/mercure
|
||||||
|
MERCURE_JWT_SECRET: ${MERCURE_JWT_SECRET}
|
||||||
|
MERCURE_JWT_TOKEN: ${MERCURE_JWT_TOKEN}
|
||||||
|
MERCURE_SUBSCRIBER_JWT: ${MERCURE_SUBSCRIBER_JWT}
|
||||||
|
MAILER_DSN: smtp://mail:25?verify_peer=0
|
||||||
|
RECAPTCHA_SITE_KEY: ${RECAPTCHA_SITE_KEY}
|
||||||
|
RECAPTCHA_SECRET_KEY: ${RECAPTCHA_SECRET_KEY}
|
||||||
|
WEBAUTHN_RP_ID: ${WEBAUTHN_RP_ID:-localhost}
|
||||||
|
WEBAUTHN_ORIGIN: ${WEBAUTHN_ORIGIN:-https://localhost}
|
||||||
|
volumes:
|
||||||
|
- app_var:/app/var
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
depends_on:
|
||||||
|
db:
|
||||||
|
condition: service_healthy
|
||||||
|
mail:
|
||||||
|
condition: service_started
|
||||||
|
mail:
|
||||||
|
image: boky/postfix:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
ALLOWED_SENDER_DOMAINS: ${MAIL_DOMAIN:-localhost}
|
||||||
|
# Optional: set a relay host if you need to forward to an external SMTP
|
||||||
|
# RELAYHOST: "[smtp.example.com]:587"
|
||||||
|
volumes:
|
||||||
|
- postfix_spool:/var/spool/postfix
|
||||||
|
db:
|
||||||
|
image: postgres:${POSTGRES_VERSION:-latest}-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: ${POSTGRES_USER}
|
||||||
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
|
||||||
|
POSTGRES_DB: ${POSTGRES_DB}
|
||||||
|
volumes:
|
||||||
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 3s
|
||||||
|
retries: 10
|
||||||
|
start_period: 10s
|
||||||
|
ports:
|
||||||
|
- "15432:5432"
|
||||||
|
volumes:
|
||||||
|
app_var:
|
||||||
|
postgres_data:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
postfix_spool:
|
||||||
@@ -7,14 +7,7 @@ parameters:
|
|||||||
|
|
||||||
doctrine:
|
doctrine:
|
||||||
dbal:
|
dbal:
|
||||||
# configure these for your database server
|
driver: 'pdo_pgsql'
|
||||||
driver: 'pdo_mysql'
|
|
||||||
server_version: '5.7'
|
|
||||||
charset: utf8mb4
|
|
||||||
default_table_options:
|
|
||||||
charset: utf8mb4
|
|
||||||
collate: utf8mb4_unicode_ci
|
|
||||||
|
|
||||||
url: '%env(resolve:DATABASE_URL)%'
|
url: '%env(resolve:DATABASE_URL)%'
|
||||||
|
|
||||||
orm:
|
orm:
|
||||||
|
|||||||
18
docker/entrypoint.sh
Normal file
18
docker/entrypoint.sh
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "[entrypoint] Waiting for database..."
|
||||||
|
until php -r "new PDO('pgsql:host=db;port=5432;dbname=${POSTGRES_DB}', '${POSTGRES_USER}', '${POSTGRES_PASSWORD}');" 2>/dev/null; do
|
||||||
|
echo "[entrypoint] Database not ready, retrying in 2s..."
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
echo "[entrypoint] Database is ready."
|
||||||
|
|
||||||
|
echo "[entrypoint] Warming up Symfony cache..."
|
||||||
|
php bin/console cache:warmup
|
||||||
|
|
||||||
|
echo "[entrypoint] Running database migrations..."
|
||||||
|
php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
|
||||||
|
|
||||||
|
echo "[entrypoint] Starting FrankenPHP..."
|
||||||
|
exec frankenphp run --config /etc/caddy/Caddyfile "$@"
|
||||||
@@ -42,6 +42,11 @@ class RecaptchaType extends AbstractType
|
|||||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event): void {
|
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event): void {
|
||||||
$request = $this->requestStack->getCurrentRequest();
|
$request = $this->requestStack->getCurrentRequest();
|
||||||
$token = $request?->request->getString('g-recaptcha-response') ?? '';
|
$token = $request?->request->getString('g-recaptcha-response') ?? '';
|
||||||
|
// For forms that set the token directly on the field (e.g. registration_form[recaptcha])
|
||||||
|
// rather than via a standalone g-recaptcha-response input, fall back to the submitted value.
|
||||||
|
if ($token === '') {
|
||||||
|
$token = (string) ($event->getData() ?? '');
|
||||||
|
}
|
||||||
$event->setData($token);
|
$event->setData($token);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,14 @@ readonly class RecaptchaService
|
|||||||
->request('POST', self::SITEVERIFY_URL, ['body' => $body])
|
->request('POST', self::SITEVERIFY_URL, ['body' => $body])
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
|
$this->logger->info('reCAPTCHA verify response', [
|
||||||
|
'success' => $data['success'] ?? null,
|
||||||
|
'score' => $data['score'] ?? null,
|
||||||
|
'hostname' => $data['hostname'] ?? null,
|
||||||
|
'error-codes' => $data['error-codes'] ?? [],
|
||||||
|
'token_length' => strlen($token),
|
||||||
|
]);
|
||||||
|
|
||||||
return ($data['success'] ?? false) === true
|
return ($data['success'] ?? false) === true
|
||||||
&& ($data['score'] ?? 0.0) >= self::SCORE_THRESHOLD;
|
&& ($data['score'] ?? 0.0) >= self::SCORE_THRESHOLD;
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user