7 Commits
Author SHA1 Message Date
Daniel-Garmig 0b36c1fd87 Updated to DBAL 4:
CI / Docker Lint (push) Successful in 30s
- Updated Doctrine/DBAL to last version (with DBAL 4).
- Migrated Postgres PK fields from SEQUENCE to IDENTITY.
2025-06-05 21:07:18 +02:00
Daniel-Garmig 2e3a9031c9 Symfony ux:icons - Added Icons. 2025-06-05 21:07:14 +02:00
Daniel-Garmig 280179ca7e Fixes and upgrades - Fix deprecated stuff. 2025-06-02 22:03:26 +02:00
Daniel-Garmig 25ff16a9db Easyadmin - Pretty URLs and FrankenPHP Workers.
- Configure Easyadmin "Pretty URLs"
- Activate FrankenPHP Workers (as it should work fine now with Easyadmin Pretty URLs).
2025-06-02 22:03:22 +02:00
Daniel-Garmig 89eef326a7 Improved deployment, logs and update docs. 2025-05-31 14:23:55 +02:00
Daniel-Garmig 1523a45008 Updated libs:
- Symfony 7.0 -> 7.3.
- Easyadmin 4.10 -> 4.24.
2025-05-30 22:51:28 +02:00
Daniel-Garmig 7bc4255a7b Symfony Docker - Updated template. 2025-05-30 20:25:43 +02:00
44 changed files with 1780 additions and 1041 deletions
+1
View File
@@ -27,6 +27,7 @@ public/bundles/
tests/
var/
vendor/
node_modules/
.editorconfig
.env.*.local
.env.local
+5 -6
View File
@@ -27,7 +27,7 @@ trim_trailing_whitespace = false
[*.sh]
indent_style = tab
[*.xml{,.dist}]
[*.xml.dist]
indent_style = space
indent_size = 4
@@ -40,19 +40,18 @@ indent_size = 2
[.gitmodules]
indent_style = tab
[.php_cs{,.dist}]
[.php_cs.dist]
indent_style = space
indent_size = 4
[composer.json]
indent_size = 4
[{,docker-}compose{,.*}.{yaml,yml}]
indent_style = space
[{compose,docker-compose}.*.{yaml,yml}]
indent_size = 2
[{,*.*}Dockerfile]
[*.*Dockerfile]
indent_style = tab
[{,*.*}Caddyfile]
[*.*Caddyfile]
indent_style = tab
+1 -1
View File
@@ -16,7 +16,7 @@
###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=d130654a5d6a8ab5945b20b22dfeee74
APP_SECRET=VerySecretKey
###< symfony/framework-bundle ###
###> doctrine/doctrine-bundle ###
+3
View File
@@ -12,6 +12,9 @@
# Draw.io backup file.
*bkp
# JS files
/node_modules/
###> symfony/asset-mapper ###
/public/assets/
/assets/vendor/
+17 -16
View File
@@ -13,6 +13,8 @@ FROM frankenphp_upstream AS frankenphp_base
WORKDIR /app
VOLUME /app/var/
# persistent / runtime deps
# hadolint ignore=DL3008
RUN apt-get update && apt-get install -y --no-install-recommends \
@@ -24,11 +26,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN set -eux; \
install-php-extensions \
@composer \
apcu \
intl \
opcache \
zip \
@composer \
apcu \
intl \
opcache \
zip \
;
# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
@@ -40,42 +42,41 @@ RUN install-php-extensions pdo_pgsql
###< doctrine/doctrine-bundle ###
###< recipes ###
COPY --link frankenphp/conf.d/app.ini $PHP_INI_DIR/conf.d/
COPY --link frankenphp/conf.d/10-app.ini $PHP_INI_DIR/app.conf.d/
COPY --link --chmod=755 frankenphp/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
COPY --link frankenphp/Caddyfile /etc/caddy/Caddyfile
COPY --link frankenphp/Caddyfile /etc/frankenphp/Caddyfile
ENTRYPOINT ["docker-entrypoint"]
HEALTHCHECK --start-period=60s CMD curl -f http://localhost:2019/metrics || exit 1
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile" ]
CMD [ "frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile" ]
# Dev FrankenPHP image
FROM frankenphp_base AS frankenphp_dev
ENV APP_ENV=dev XDEBUG_MODE=off
VOLUME /app/var/
ENV APP_ENV=dev
ENV XDEBUG_MODE=off
ENV FRANKENPHP_WORKER_CONFIG=watch
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
RUN set -eux; \
install-php-extensions \
xdebug \
xdebug \
;
COPY --link frankenphp/conf.d/app.dev.ini $PHP_INI_DIR/conf.d/
COPY --link frankenphp/conf.d/20-app.dev.ini $PHP_INI_DIR/app.conf.d/
CMD [ "frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--watch" ]
CMD [ "frankenphp", "run", "--config", "/etc/frankenphp/Caddyfile", "--watch" ]
# Prod FrankenPHP image
FROM frankenphp_base AS frankenphp_prod
ENV APP_ENV=prod
ENV FRANKENPHP_CONFIG="import worker.Caddyfile"
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
COPY --link frankenphp/conf.d/app.prod.ini $PHP_INI_DIR/conf.d/
COPY --link frankenphp/worker.Caddyfile /etc/caddy/worker.Caddyfile
COPY --link frankenphp/conf.d/20-app.prod.ini $PHP_INI_DIR/app.conf.d/
# prevent the reinstallation of vendors at every changes in the source code
COPY --link composer.* symfony.* ./
+1
View File
@@ -73,6 +73,7 @@ docker run -d -it -p 80:80 -p 443:443 --name somniarooms --env-file ./env.prod.l
When deploying to prod is recomended to mount some directories as a docker volume or hostpath. This makes sure data is preserved after deleting the container.
- Image directory (`/app/public/images`)
- Logs (`/somnia_logs`)
If you are deploying your DB as a container, check the database docs and make sure database data won't be lost when updating or deleting the container. This example configuration store postgress data on a unnamed docket volume.
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 257"><circle cx="128" cy="128.827" r="128" fill="#1a171b"/><path fill="#fff" d="M183.706 48.124c-12.986.453-24.32 7.61-32.757 17.51c-9.342 10.855-15.557 23.73-20.035 36.872c-8.01-6.565-14.19-15.064-27.041-18.77c-9.933-2.852-20.366-1.674-29.96 5.474c-4.545 3.395-7.676 8.527-9.165 13.351c-3.855 12.537 4.053 23.694 7.645 27.7l7.853 8.416c1.619 1.65 5.518 5.955 3.612 12.127c-2.06 6.71-10.15 11.055-18.448 8.495c-3.706-1.13-9.03-3.891-7.838-7.779c.493-1.59 1.631-2.78 2.241-4.155c.56-1.181.827-2.067.997-2.587c1.516-4.95-.555-11.39-5.857-13.025c-4.946-1.516-10.007-.315-11.969 6.054c-2.225 7.235 1.237 20.366 19.783 26.084c21.729 6.676 40.11-5.155 42.717-20.586c1.642-9.665-2.722-16.845-10.717-26.08l-6.514-7.204c-3.946-3.942-5.301-10.661-1.217-15.825c3.446-4.356 8.354-6.215 16.392-4.029c11.733 3.186 16.963 11.327 25.69 17.893c-3.603 11.819-5.958 23.682-8.09 34.32l-1.299 7.931c-6.238 32.721-11 50.688-23.375 61.003c-2.493 1.773-6.057 4.427-11.429 4.612c-2.816.087-3.726-1.85-3.765-2.694c-.067-1.977 1.599-2.883 2.706-3.773c1.654-.902 4.155-2.398 3.985-7.191c-.18-5.664-4.872-10.575-11.654-10.35c-5.08.173-12.823 4.954-12.532 13.705c.303 9.039 8.728 15.813 21.43 15.384c6.79-.233 21.952-2.997 36.895-20.76c17.392-20.362 22.256-43.705 25.915-60.79l4.084-22.556c2.269.272 4.695.453 7.334.516c21.661.457 32.496-10.763 32.657-18.924c.107-4.939-3.241-9.799-7.928-9.689c-3.355.095-7.57 2.328-8.582 6.968c-.988 4.552 6.893 8.66.733 12.65c-4.376 2.832-12.221 4.828-23.269 3.206l2.009-11.103c4.1-21.055 9.157-46.954 28.341-47.584c1.398-.071 6.514.063 6.633 3.446c.035 1.13-.245 1.418-1.568 4.005c-1.347 2.017-1.855 3.734-1.792 5.707c.185 5.376 4.273 8.909 10.185 8.696c7.916-.256 10.193-7.963 10.063-11.921c-.32-9.3-10.122-15.175-23.1-14.75"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-bug-off"><path d="M15 7.13V6a3 3 0 0 0-5.14-2.1L8 2"/><path d="M14.12 3.88 16 2"/><path d="M22 13h-4v-2a4 4 0 0 0-4-4h-1.3"/><path d="M20.97 5c0 2.1-1.6 3.8-3.5 4"/><path d="m2 2 20 20"/><path d="M7.7 7.7A4 4 0 0 0 6 11v3a6 6 0 0 0 11.13 3.13"/><path d="M12 20v-8"/><path d="M6 13H2"/><path d="M3 21c0-2.1 1.7-3.9 3.8-4"/></svg>

Before

Width:  |  Height:  |  Size: 530 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-check"><path d="M20 6 9 17l-5-5"/></svg>

Before

Width:  |  Height:  |  Size: 242 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-coffee"><path d="M10 2v2"/><path d="M14 2v2"/><path d="M16 8a1 1 0 0 1 1 1v8a4 4 0 0 1-4 4H7a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1h14a4 4 0 1 1 0 8h-1"/><path d="M6 2v2"/></svg>

Before

Width:  |  Height:  |  Size: 371 B

-1
View File
@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>

Before

Width:  |  Height:  |  Size: 255 B

+10 -4
View File
@@ -6,17 +6,23 @@ services:
target: frankenphp_dev
volumes:
- ./:/app
- ./frankenphp/Caddyfile:/etc/caddy/Caddyfile:ro
- ./frankenphp/conf.d/app.dev.ini:/usr/local/etc/php/conf.d/app.dev.ini:ro
- ./frankenphp/Caddyfile:/etc/frankenphp/Caddyfile:ro
- ./frankenphp/conf.d/20-app.dev.ini:/usr/local/etc/php/app.conf.d/20-app.dev.ini:ro
# If you develop on Mac or Windows you can remove the vendor/ directory
# from the bind-mount for better performance by enabling the next line:
- /app/vendor
# On dev mount var to localfiles.
- ./var/log:/app/var/log
# App logs can be bind-mount to host folder.
- ./var/log:/somnia_logs
environment:
FRANKENPHP_WORKER_CONFIG: watch
FRANKENPHP_USEWORKER: none # <-- Disable FrankenPHP Workers for development as don't reload on code changes.
MERCURE_EXTRA_DIRECTIVES: demo
# See https://xdebug.org/docs/all_settings#mode
XDEBUG_MODE: "${XDEBUG_MODE:-off}"
XDEBUG_CONFIG: "client_host=${XDEBUG_CLIENT_HOST:-host.docker.internal} discover_client_host=${XDEBUG_DISCOVER_CLIENT_HOST:-off}"
APP_ENV: "${APP_ENV:-dev}"
# Change APP log path.
# SOMNIA_LOGSDIR: "/somnia_logs"
extra_hosts:
# Ensure that host.docker.internal is correctly defined on Linux
- host.docker.internal:host-gateway
+7 -4
View File
@@ -9,17 +9,19 @@ services:
TRUSTED_PROXIES: ${TRUSTED_PROXIES:-127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16}
TRUSTED_HOSTS: ^${SERVER_NAME:-example\.com|localhost}|php$$
# Run "composer require symfony/orm-pack" to install and configure Doctrine ORM
DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-15}&charset=${POSTGRES_CHARSET:-utf8}
DATABASE_URL: postgresql://${POSTGRES_USER:-app}:${POSTGRES_PASSWORD:-!ChangeMe!}@database:5432/${POSTGRES_DB:-app}?serverVersion=${POSTGRES_VERSION:-16}&charset=${POSTGRES_CHARSET:-utf8}
# Run "composer require symfony/mercure-bundle" to install and configure the Mercure integration
MERCURE_URL: ${CADDY_MERCURE_URL:-http://php/.well-known/mercure}
MERCURE_PUBLIC_URL: https://${SERVER_NAME:-localhost}/.well-known/mercure
MERCURE_JWT_SECRET: ${CADDY_MERCURE_JWT_SECRET:-!ChangeThisMercureHubJWTSecretKey!}
FRANKENPHP_USEWORKER: worker # <-- Disable FrankenPHP Workers as don't work well with current EasyAdmin version. This is fixed on newer versions.
# The two next lines can be removed after initial installation
SYMFONY_VERSION: ${SYMFONY_VERSION:-}
STABILITY: ${STABILITY:-stable}
volumes:
- caddy_data:/data
- caddy_config:/config
- app_logs:/somnia_logs # <-- Volume for App logs.
ports:
# HTTP
- target: 80
@@ -48,12 +50,12 @@ services:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
POSTGRES_USER: ${POSTGRES_USER:-app}
healthcheck:
test: ["CMD", "pg_isready"]
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER:-app}"]
timeout: 5s
retries: 5
start_period: 60s
volumes:
- database_data:/var/lib/postgresql/data:rw
- app_database:/var/lib/postgresql/data:rw
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
# - ./docker/db/data:/var/lib/postgresql/data:rw
###< doctrine/doctrine-bundle ###
@@ -61,9 +63,10 @@ services:
volumes:
caddy_data:
caddy_config:
app_logs:
###> symfony/mercure-bundle ###
###< symfony/mercure-bundle ###
###> doctrine/doctrine-bundle ###
database_data:
app_database:
###< doctrine/doctrine-bundle ###
+25 -22
View File
@@ -9,10 +9,10 @@
"php": ">=8.3.6",
"ext-ctype": "*",
"ext-iconv": "*",
"doctrine/dbal": "^3",
"doctrine/doctrine-bundle": "^2.12",
"doctrine/dbal": "^4.2",
"doctrine/doctrine-bundle": "^2.14",
"doctrine/doctrine-migrations-bundle": "^3.3",
"doctrine/orm": "^3.1",
"doctrine/orm": "^3.3",
"easycorp/easyadmin-bundle": "^4.10",
"nelmio/api-doc-bundle": "^4.30",
"nelmio/cors-bundle": "^2.4",
@@ -20,24 +20,27 @@
"phpdocumentor/reflection-docblock": "^5.4",
"phpstan/phpdoc-parser": "^1.28",
"runtime/frankenphp-symfony": "^0.2.0",
"symfony/asset": "7.0.*",
"symfony/asset-mapper": "7.0.*",
"symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*",
"symfony/asset": "7.3.*",
"symfony/asset-mapper": "7.3.*",
"symfony/console": "7.3.*",
"symfony/dotenv": "7.3.*",
"symfony/flex": "^2",
"symfony/form": "7.0.*",
"symfony/framework-bundle": "7.0.*",
"symfony/mime": "7.0.*",
"symfony/form": "7.3.*",
"symfony/framework-bundle": "7.3.*",
"symfony/http-client": "7.3.*",
"symfony/mime": "7.3.*",
"symfony/monolog-bundle": "^3.10",
"symfony/property-access": "7.0.*",
"symfony/property-info": "7.0.*",
"symfony/runtime": "7.0.*",
"symfony/security-bundle": "7.0.*",
"symfony/serializer": "7.0.*",
"symfony/twig-bundle": "7.0.*",
"symfony/validator": "7.0.*",
"symfony/var-exporter": "7.0.4",
"symfony/yaml": "7.0.*",
"symfony/property-access": "7.3.*",
"symfony/property-info": "7.3.*",
"symfony/runtime": "7.3.*",
"symfony/security-bundle": "7.3.*",
"symfony/serializer": "7.3.*",
"symfony/twig-bundle": "7.3.*",
"symfony/ux-icons": "^2.25",
"symfony/ux-twig-component": "^2.25",
"symfony/validator": "7.3.*",
"symfony/var-exporter": "7.3.*",
"symfony/yaml": "7.3.*",
"twig/extra-bundle": "^2.12|^3.0",
"twig/twig": "^2.12|^3.0"
},
@@ -88,13 +91,13 @@
"extra": {
"symfony": {
"allow-contrib": false,
"require": "7.0.*",
"require": "7.3.*",
"docker": true
}
},
"require-dev": {
"symfony/maker-bundle": "^1.58",
"symfony/stopwatch": "7.0.*",
"symfony/web-profiler-bundle": "7.0.*"
"symfony/stopwatch": "7.3.*",
"symfony/web-profiler-bundle": "7.3.*"
}
}
Generated
+1312 -940
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -13,4 +13,6 @@ return [
Twig\Extra\TwigExtraBundle\TwigExtraBundle::class => ['all' => true],
Nelmio\ApiDocBundle\NelmioApiDocBundle::class => ['all' => true],
Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
Symfony\UX\Icons\UXIconsBundle::class => ['all' => true],
];
+11
View File
@@ -0,0 +1,11 @@
# Enable stateless CSRF protection for forms and logins/logouts
framework:
form:
csrf_protection:
token_id: submit
csrf_protection:
stateless_token_ids:
- submit
- authenticate
- logout
+2 -2
View File
@@ -8,13 +8,13 @@ when@dev:
main:
type: rotating_file
path: "%kernel.logs_dir%/%kernel.environment%.log"
level: debug
level: info
channels: ["!event", "!doctrine"]
max_files: 0
doctrine:
type: rotating_file
path: "%kernel.logs_dir%/db_%kernel.environment%.log"
level: debug
level: info
channels: ["doctrine", "request"]
max_files: 10
# uncomment to get logging in your browser
+3
View File
@@ -0,0 +1,3 @@
framework:
property_info:
with_constructor_extractor: true
+5
View File
@@ -0,0 +1,5 @@
twig_component:
anonymous_template_directory: 'components/'
defaults:
# Namespace & directory for components
App\Twig\Components\: 'components/'
+24
View File
@@ -0,0 +1,24 @@
ux_icons:
# The local directory where icons are stored
icon_dir: "%kernel.project_dir%/assets/icons"
# Default attributes to add to all icons
default_icon_attributes:
width: "1.25em"
# Icon aliases (alias => icon name)
aliases:
"tabler:save": "tabler:device-floppy"
# Configuration for the "on demand" icons powered by Iconify.design
iconify:
enabled: true
# Whether to use the "on demand" icons powered by Iconify.design
on_demand: true
# The endpoint for the Iconify API
endpoint: "https://api.iconify.design"
# Whether to ignore errors when an icon is not found
ignore_not_found: true
+4
View File
@@ -0,0 +1,4 @@
# config/routes/easyadmin.yaml
easyadmin:
resource: .
type: easyadmin.routes
+1 -1
View File
@@ -1,4 +1,4 @@
when@dev:
_errors:
resource: '@FrameworkBundle/Resources/config/routing/errors.xml'
resource: '@FrameworkBundle/Resources/config/routing/errors.php'
prefix: /_error
+2 -2
View File
@@ -1,8 +1,8 @@
when@dev:
web_profiler_wdt:
resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml'
resource: '@WebProfilerBundle/Resources/config/routing/wdt.php'
prefix: /_wdt
web_profiler_profiler:
resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml'
resource: '@WebProfilerBundle/Resources/config/routing/profiler.php'
prefix: /_profiler
+5
View File
@@ -4,6 +4,11 @@
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
# Allow custom path for log files.
env(SOMNIA_LOGSDIR): '/somnia_logs'
kernel.logs_dir: '%env(SOMNIA_LOGSDIR)%'
# Set default value for APP_SECRET.
env(APP_SECRET): 'ChangeMySecret'
services:
# default configuration for services in *this* file
+26
View File
@@ -1,6 +1,7 @@
<!-- omit in toc -->
# SomniaRooms - How to deploy.
- [Environment Vars](#environment-vars)
- [Docker compose examples.](#docker-compose-examples)
- [Deploy back-end and front-end on different servers (different ip address).](#deploy-back-end-and-front-end-on-different-servers-different-ip-address)
- [Deploy on the same server.](#deploy-on-the-same-server)
@@ -14,12 +15,37 @@ Production images for Docker Containers are available from Gitea Registry.
Documentation on how to deploy them as containers can be found at [Readme](../README.md).
## Environment Vars
Hare is a list of some "important" or useful environment vars to consider when deploying SomniaRooms
| Var | Required | Default Value | Description |
| :------------------------: | :----------------: | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SERVER_NAME` | :heavy_check_mark: | `-` | Your server hostname to be used by Caddy. Should include "http://". |
| `DATABASE_URL` | :heavy_check_mark: | `-` | Connection string for database. See: https://symfony.com/doc/current/doctrine.html#configuring-the-database |
| `APP_SECRET` | :heavy_check_mark: | `ChangeMySecret` | Symfony App secret. You really should change this. . You can use `openssl rand -base64 36` to generate random strings |
| `TRUSTED_PROXIES` | :heavy_check_mark: | `-` | Allowed proxies address. The proxy IP should be used. Must be set when used behind some proxy/reverse-proxy: traefik (see example below), nginx, .... |
| `TRUSTED_HOSTS` | :heavy_check_mark: | `-` | ¿? You may have to set this to your `SERVER_NAME`, but I don't remember how it worked. |
| `APP_ENV` | :x: | `dev` | Set current app environment. Set to `prod` from production use. |
| `FRANKENPHP_CONFIG` | :x: | `-` | Add extra config for FrankenPHP. See: https://frankenphp.dev/docs/config/ |
| `FRANKENPHP_WORKER_CONFIG` | :x: | `-` | Add extra config for FrankenPHP Workers. See: https://frankenphp.dev/docs/config/ |
| `FRANKENPHP_USEWORKER` | :x: | `-` | Enable/disable FrankenPHP Workers. Valid values: `none` (disabled) /`worker` (enabled) |
| `MERCURE_...` | :x: | `-` | Just copy default values from `compose.yaml`. SomniaRooms don't use Mercure right now and may get removed. |
| `SOMNIA_LOGSDIR` | :x: | `/somnia_logs` | Path for App logs. It's recomended to save them on a volume or host-path (even in production?) just in case, as not all errors may be print to the console. |
For running SomniaRooms behind a proxy, see: https://symfony.com/doc/current/deployment/proxies.html
## Docker compose examples.
Here are some deploy examples using docker compose:
SomniaRooms back-end and front-end can run on the same machine, but some config is needed to work.
> [!WARNING]
> **NOTE (31/05/2025)**: Some examples may be out-of-date. Use only as reference! Check ["dev" `compose.yaml`](../compose.yaml) and [Readme](../README.md) and apply relevant diffs.
### Deploy back-end and front-end on different servers (different ip address).
If your servers have different IP addresses everything is a little bit easier. You can set-up subdomains for each one.
+32 -14
View File
@@ -1,37 +1,44 @@
# Snipets "none"/"worker" can be used to enable-disable frankenphp workers.
(none) {
}
(worker) {
worker {
file ./public/index.php
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
{$FRANKENPHP_WORKER_CONFIG}
}
}
{
skip_install_trust
{$CADDY_GLOBAL_OPTIONS}
frankenphp {
{$FRANKENPHP_CONFIG}
}
# https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm
order mercure after encode
order vulcain after reverse_proxy
order php_server before file_server
import {$FRANKENPHP_USEWORKER:worker}
}
}
{$CADDY_EXTRA_CONFIG}
{$SERVER_NAME:localhost} {
log {
{$CADDY_SERVER_LOG_OPTIONS}
# Redact the authorization query parameter that can be set by Mercure
format filter {
wrap console
fields {
uri query {
replace authorization REDACTED
}
request>uri query {
replace authorization REDACTED
}
}
}
root * /app/public
root /app/public
encode zstd br gzip
mercure {
# Transport to use (default to Bolt)
transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db}
# Publisher JWT key
publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG}
# Subscriber JWT key
@@ -51,5 +58,16 @@
# Disable Topics tracking if not enabled explicitly: https://github.com/jkarlin/topics
header ?Permissions-Policy "browsing-topics=()"
php_server
@phpRoute {
not path /.well-known/mercure*
not file {path}
}
rewrite @phpRoute index.php
@frontController path index.php
php @frontController
file_server {
hide *.php
}
}
+5
View File
@@ -0,0 +1,5 @@
; https://symfony.com/doc/current/performance.html#use-the-opcache-class-preloading
opcache.preload_user = root
opcache.preload = /app/config/preload.php
; https://symfony.com/doc/current/performance.html#don-t-check-php-files-timestamps
opcache.validate_timestamps = 0
-2
View File
@@ -1,2 +0,0 @@
opcache.preload_user = root
opcache.preload = /app/config/preload.php
+11 -5
View File
@@ -17,7 +17,7 @@ if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
composer config --json extra.symfony.docker 'true'
if grep -q ^DATABASE_URL= .env; then
echo "To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build -d --wait"
echo 'To finish the installation please press Ctrl+C to stop Docker Compose and run: docker compose up --build --wait'
sleep infinity
fi
fi
@@ -26,8 +26,12 @@ if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
composer install --prefer-dist --no-progress --no-interaction
fi
# Display information about the current project
# Or about an error in project initialization
php bin/console -V
if grep -q ^DATABASE_URL= .env; then
echo "Waiting for database to be ready..."
echo 'Waiting for database to be ready...'
ATTEMPTS_LEFT_TO_REACH_DATABASE=60
until [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ] || DATABASE_ERROR=$(php bin/console dbal:run-sql -q "SELECT 1" 2>&1); do
if [ $? -eq 255 ]; then
@@ -41,20 +45,22 @@ if [ "$1" = 'frankenphp' ] || [ "$1" = 'php' ] || [ "$1" = 'bin/console' ]; then
done
if [ $ATTEMPTS_LEFT_TO_REACH_DATABASE -eq 0 ]; then
echo "The database is not up or not reachable:"
echo 'The database is not up or not reachable:'
echo "$DATABASE_ERROR"
exit 1
else
echo "The database is now ready and reachable"
echo 'The database is now ready and reachable'
fi
if [ "$( find ./migrations -iname '*.php' -print -quit )" ]; then
php bin/console doctrine:migrations:migrate --no-interaction
php bin/console doctrine:migrations:migrate --no-interaction --all-or-nothing
fi
fi
setfacl -R -m u:www-data:rwX -m u:"$(whoami)":rwX var
setfacl -dR -m u:www-data:rwX -m u:"$(whoami)":rwX var
echo 'PHP app ready!'
fi
exec docker-php-entrypoint "$@"
-4
View File
@@ -1,4 +0,0 @@
worker {
file ./public/index.php
env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
}
+138
View File
@@ -0,0 +1,138 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
* Migrate Postgres SEQUENCE to IDENTITY after updating to DBAL 4.
*/
final class Version20250605182840 extends AbstractMigration
{
public function getDescription(): string
{
return 'Migrate Postgres SEQUENCE to IDENTITY after updating to DBAL 4.';
}
public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE bed_type ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE bed_type ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_customer ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_customer ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_room ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_room ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE fare_table ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE fare_table ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category_photo ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category_photo ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_fare ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_fare ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "user" ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "user" ALTER id ADD GENERATED BY DEFAULT AS IDENTITY
SQL);
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql(<<<'SQL'
ALTER TABLE room_fare ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_fare ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_room ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_room ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_customer ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking_customer ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE bed_type ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE bed_type ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE fare_table ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE fare_table ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "user" ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE "user" ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category_photo ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE room_category_photo ALTER id DROP IDENTITY
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking ALTER id TYPE INT
SQL);
$this->addSql(<<<'SQL'
ALTER TABLE booking ALTER id DROP IDENTITY
SQL);
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
[
{
"name": "Double Bed",
"iconUrl": "icon-bed-double"
"iconUrl": "lucide:bed-double"
},
{
"name": "Single Bed",
"iconUrl": "icon-bed-single"
"iconUrl": "lucide:bed-single"
}
]
+1 -1
View File
@@ -10,7 +10,7 @@ class ApiErrorException extends HttpException
private ApiErrorMessage $apiErrorMsg;
public function __construct(ApiErrorMessage $apiErrorMsg, $detailsData = null, \Exception $previous = null, array $headers = array(), $code = 0)
public function __construct(ApiErrorMessage $apiErrorMsg, $detailsData = null, ?\Exception $previous = null, array $headers = array(), $code = 0)
{
$this->apiErrorMsg = $apiErrorMsg;
@@ -2,6 +2,7 @@
namespace App\Controller\Admin;
use App\Controller\Admin\Field\IconField;
use App\Entity\BedType;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;
use EasyCorp\Bundle\EasyAdminBundle\Field\TextField;
@@ -17,6 +18,6 @@ class BedTypeCrudController extends AbstractCrudController
public function configureFields(string $pageName): iterable
{
yield TextField::new('name');
yield UrlField::new('iconUrl');
yield IconField::new('iconUrl');
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace App\Controller\Admin\Field;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Field\FieldInterface;
use EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait;
use Symfony\Component\Form\Extension\Core\Type\TextType;
final class IconField implements FieldInterface
{
use FieldTrait;
/**
* @param TranslatableInterface|string|false|null $label
*/
public static function new(string $propertyName, $label = null): self
{
return (new self())
->setProperty($propertyName)
->setLabel($label)
// this template is used in 'index' and 'detail' pages
->setTemplatePath('admin/field/icon.html.twig')
// this is used in 'edit' and 'new' pages to edit the field contents
// you can use your own form types too
->setFormType(TextType::class)
->addCssClass('field-icon')
// these methods allow to define the web assets loaded when the
// field is displayed in any CRUD page (index/detail/edit/new)
//->addCssFiles('admin/field/field-icon.css')
//->addJsFiles('admin/field/field-icon.js')
;
}
}
@@ -9,15 +9,16 @@ use App\Entity\FareTable;
use App\Entity\RoomCategory;
use App\Entity\RoomFare;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Attribute\AdminDashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard;
use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
#[AdminDashboard(routePath: '/admin', routeName: 'admin')]
class SomniaDashboardController extends AbstractDashboardController
{
#[Route('/admin', name: 'admin')]
public function index(): Response
{
return $this->render('admin/somniaDashboard.html.twig');
+1 -2
View File
@@ -113,8 +113,7 @@ class AppSetupController extends AbstractController
]);
}
private function GoToResultPage(bool $isError, string $message, string $linkRoute = null, string $linkLabel = null): Response
private function GoToResultPage(bool $isError, string $message, ?string $linkRoute = null, ?string $linkLabel = null): Response
{
return $this->render('app_setup/result.html.twig', [
'isError' => $isError,
+1 -1
View File
@@ -182,7 +182,7 @@ class ExampleDataService
$this->entityManager->persist($fareTable);
for ($i = 1; $i < $room->getMaxGuestNum(); $i++) {
for ($i = 1; $i <= $room->getMaxGuestNum(); $i++) {
$roomFare = new RoomFare();
$roomFare->setGuestNumber($i);
$roomFare->setFareTable($fareTable);
+57
View File
@@ -1,4 +1,13 @@
{
"doctrine/deprecations": {
"version": "1.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "1.0",
"ref": "87424683adc81d7dc305eefec1fced883084aab9"
}
},
"doctrine/doctrine-bundle": {
"version": "2.12",
"recipe": {
@@ -99,6 +108,18 @@
".env"
]
},
"symfony/form": {
"version": "7.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.2",
"ref": "7d86a6723f4a623f59e2bf966b6aad2fc461d36b"
},
"files": [
"./config/packages/csrf.yaml"
]
},
"symfony/framework-bundle": {
"version": "7.0",
"recipe": {
@@ -139,6 +160,18 @@
"./config/packages/monolog.yaml"
]
},
"symfony/property-info": {
"version": "7.3",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "7.3",
"ref": "dae70df71978ae9226ae915ffd5fad817f5ca1f7"
},
"files": [
"./config/packages/property_info.yaml"
]
},
"symfony/routing": {
"version": "7.0",
"recipe": {
@@ -200,6 +233,30 @@
"ref": "0df5844274d871b37fc3816c57a768ffc60a43a5"
}
},
"symfony/ux-icons": {
"version": "2.25",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.17",
"ref": "803a3bbd5893f9584969ab8670290cdfb6a0a5b5"
},
"files": [
"./assets/icons/symfony.svg"
]
},
"symfony/ux-twig-component": {
"version": "2.25",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "2.13",
"ref": "9b627037a510e20eddb559e06050a34854ce51d6"
},
"files": [
"./config/packages/twig_component.yaml"
]
},
"symfony/validator": {
"version": "7.0",
"recipe": {
+18
View File
@@ -0,0 +1,18 @@
{# @var ea \EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext #}
{# @var field \EasyCorp\Bundle\EasyAdminBundle\Dto\FieldDto #}
{# @var entity \EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto #}
{% if ea.crud.currentAction == 'detail' %}
<div class="field-icon-div d-flex gap-3">
<span title="{{ field.value }}">{{ field.formattedValue|raw }}</span>
<div class="badge text-bg-secondary">
<twig:ux:icon name="{{ field.value }}" style="width: 1.25em;" />
</div>
</div>
{% else %}
<div class="field-icon-div d-flex gap-3">
<span title="{{ field.value }}">{{ field.formattedValue|raw }}</span>
<div class="badge text-bg-secondary">
<twig:ux:icon name="{{ field.value }}" style="width: 1.25em;" />
</div>
</div>
{% endif %}
+2 -2
View File
@@ -8,9 +8,9 @@
<h1>SomniaRooms - Set-Up!</h1>
{% if isError %}
<img src="{{ asset('resources/x.svg') }}" />
<twig:ux:icon name="lucide:x" style="width: 3em;" />
{% else %}
<img src="{{ asset('resources/coffee.svg') }}" />
<twig:ux:icon name="lucide:coffee" style="width: 3em;" />
{% endif %}