From 53a5d87cd34521d68931b4838c221609547cd9b1 Mon Sep 17 00:00:00 2001 From: Thomas Hucke Date: Tue, 3 Mar 2026 18:28:58 +0100 Subject: [PATCH 1/5] feat: initialize typo3 template --- src/hello/.devcontainer/Dockerfile | 4 - src/hello/.devcontainer/devcontainer.json | 21 - src/hello/devcontainer-template.json | 34 -- src/typo3/.devcontainer/.env | 34 ++ src/typo3/.devcontainer/README.md | 56 ++ src/typo3/.devcontainer/devcontainer.json | 78 +++ src/typo3/.devcontainer/docker/TYPO3.env.tmpl | 31 ++ .../.devcontainer/docker/apache/.htaccess | 396 ++++++++++++++ .../.devcontainer/docker/apache/Dockerfile | 99 ++++ .../.devcontainer/docker/apache/preStop.sh | 14 + src/typo3/.devcontainer/docker/db/initdb/.env | 4 + .../docker/db/initdb/2_create_procedure.sql | 55 ++ .../docker/db/mariadb/Dockerfile | 3 + .../.devcontainer/docker/db/mariadb/mysql.cnf | 6 + .../.devcontainer/docker/db/mysql/Dockerfile | 4 + .../.devcontainer/docker/db/mysql/mysql.cnf | 6 + .../docker/docker-compose.backend.yaml | 82 +++ .../.devcontainer/docker/igniteEnvironment.sh | 59 +++ .../.devcontainer/docker/nginx/Dockerfile | 15 + .../docker/nginx/includes/cache-header.conf | 4 + .../docker/nginx/includes/compression.conf | 31 ++ .../docker/nginx/includes/typo3.conf | 161 ++++++ .../nginx/templates/default.conf.template | 42 ++ src/typo3/.devcontainer/docker/parseDotEnv.sh | 32 ++ src/typo3/.devcontainer/docker/php/Dockerfile | 89 ++++ .../docker/php/php-fpm-healthcheck.sh | 139 +++++ src/typo3/.devcontainer/docker/php/www.conf | 490 ++++++++++++++++++ src/typo3/.devcontainer/docker/postStart.sh | 36 ++ .../.devcontainer/docker/typo3/additional.php | 17 + src/typo3/.devcontainer/docker/typo3/php.ini | 21 + .../docker/typo3/typo3.caddyfile | 79 +++ .../.devcontainer/onCreateCommandScript.sh | 19 + .../.devcontainer/postCreateCommandScript.sh | 28 + src/{hello => typo3}/README.md | 0 src/typo3/devcontainer-template.json | 32 ++ 35 files changed, 2162 insertions(+), 59 deletions(-) delete mode 100644 src/hello/.devcontainer/Dockerfile delete mode 100644 src/hello/.devcontainer/devcontainer.json delete mode 100644 src/hello/devcontainer-template.json create mode 100644 src/typo3/.devcontainer/.env create mode 100644 src/typo3/.devcontainer/README.md create mode 100644 src/typo3/.devcontainer/devcontainer.json create mode 100644 src/typo3/.devcontainer/docker/TYPO3.env.tmpl create mode 100644 src/typo3/.devcontainer/docker/apache/.htaccess create mode 100644 src/typo3/.devcontainer/docker/apache/Dockerfile create mode 100644 src/typo3/.devcontainer/docker/apache/preStop.sh create mode 100644 src/typo3/.devcontainer/docker/db/initdb/.env create mode 100644 src/typo3/.devcontainer/docker/db/initdb/2_create_procedure.sql create mode 100644 src/typo3/.devcontainer/docker/db/mariadb/Dockerfile create mode 100644 src/typo3/.devcontainer/docker/db/mariadb/mysql.cnf create mode 100644 src/typo3/.devcontainer/docker/db/mysql/Dockerfile create mode 100644 src/typo3/.devcontainer/docker/db/mysql/mysql.cnf create mode 100644 src/typo3/.devcontainer/docker/docker-compose.backend.yaml create mode 100644 src/typo3/.devcontainer/docker/igniteEnvironment.sh create mode 100644 src/typo3/.devcontainer/docker/nginx/Dockerfile create mode 100644 src/typo3/.devcontainer/docker/nginx/includes/cache-header.conf create mode 100644 src/typo3/.devcontainer/docker/nginx/includes/compression.conf create mode 100644 src/typo3/.devcontainer/docker/nginx/includes/typo3.conf create mode 100644 src/typo3/.devcontainer/docker/nginx/templates/default.conf.template create mode 100644 src/typo3/.devcontainer/docker/parseDotEnv.sh create mode 100644 src/typo3/.devcontainer/docker/php/Dockerfile create mode 100644 src/typo3/.devcontainer/docker/php/php-fpm-healthcheck.sh create mode 100644 src/typo3/.devcontainer/docker/php/www.conf create mode 100644 src/typo3/.devcontainer/docker/postStart.sh create mode 100644 src/typo3/.devcontainer/docker/typo3/additional.php create mode 100644 src/typo3/.devcontainer/docker/typo3/php.ini create mode 100644 src/typo3/.devcontainer/docker/typo3/typo3.caddyfile create mode 100644 src/typo3/.devcontainer/onCreateCommandScript.sh create mode 100644 src/typo3/.devcontainer/postCreateCommandScript.sh rename src/{hello => typo3}/README.md (100%) create mode 100644 src/typo3/devcontainer-template.json diff --git a/src/hello/.devcontainer/Dockerfile b/src/hello/.devcontainer/Dockerfile deleted file mode 100644 index 9c45c4c..0000000 --- a/src/hello/.devcontainer/Dockerfile +++ /dev/null @@ -1,4 +0,0 @@ -FROM mcr.microsoft.com/devcontainers/base:${templateOption:imageVariant} - -RUN mkdir -p /usr/local/etc \ - && echo "${templateOption:greeting}" > /usr/local/etc/greeting.txt \ No newline at end of file diff --git a/src/hello/.devcontainer/devcontainer.json b/src/hello/.devcontainer/devcontainer.json deleted file mode 100644 index fc33f83..0000000 --- a/src/hello/.devcontainer/devcontainer.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "Hello, World", - "build": { - "dockerfile": "Dockerfile" - }, - - // 👇 Features to add to the Dev Container. More info: https://containers.dev/implementors/features. - // "features": {}, - - // 👇 Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // 👇 Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "cat /usr/local/etc/greeting.txt" - - // 👇 Configure tool-specific properties. - // "customizations": {}, - - // 👇 Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" -} diff --git a/src/hello/devcontainer-template.json b/src/hello/devcontainer-template.json deleted file mode 100644 index 342c75e..0000000 --- a/src/hello/devcontainer-template.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id": "hello", - "version": "1.0.0", - "name": "Hello, World", - "description": "A hello world Template", - "documentationURL": "https://github.com/devcontainers/template-starter/tree/main/src/hello", - "licenseURL": "https://github.com/devcontainers/template-starter/blob/main/LICENSE", - "options": { - "imageVariant": { - "type": "string", - "description": "Ubuntu version (use ubuntu-22.04 or ubuntu-18.04 on local arm64/Apple Silicon):", - "proposals": [ - "jammy", - "focal", - "bionic" - ], - "default": "jammy" - }, - "greeting": { - "type": "string", - "description": "Select a pre-made greeting, or enter your own", - "proposals": [ - "hey", - "hello", - "hi", - "howdy" - ], - "default": "hey" - } - }, - "platforms": [ - "Any" - ] -} diff --git a/src/typo3/.devcontainer/.env b/src/typo3/.devcontainer/.env new file mode 100644 index 0000000..c41f7d2 --- /dev/null +++ b/src/typo3/.devcontainer/.env @@ -0,0 +1,34 @@ +TZ=Europe/Berlin +COMPOSE_PROJECT_NAME=typo3-devcontainer +DB_CONNECTION_ROOT_PASSWORD=dbroot + +# +# TYPO3 Settings +# ---------------------------------------- +# +# The following settings are used during the installation of TYPO3 +# and are also available in the running container as environment variables. +# In a containerless environment they should be set in a file named TYPO3.env +# placed in the root of the TYPO3 project. +#---------------------------------------- +TYPO3_CONTEXT=Development +#TYPO3_CONTEXT=Production + +TYPO3_INSTALL_DB_DRIVER=mysqli +TYPO3_INSTALL_DB_USER=db +TYPO3_INSTALL_DB_PASSWORD=db +TYPO3_INSTALL_DB_HOST=127.0.0.1 +TYPO3_INSTALL_DB_PORT=3306 +TYPO3_INSTALL_DB_USE_EXISTING=true +TYPO3_INSTALL_DB_DBNAME=db +TYPO3_INSTALL_ADMIN_USER=admin +TYPO3_INSTALL_ADMIN_PASSWORD=Password!1 +TYPO3_INSTALL_SITE_NAME=TYPO3 Devcontainer Package +TYPO3_INSTALL_SITE_SETUP_TYPE=no +TYPO3_CONFIG_SYS_TRUSTED_HOST_PATTERN="localhost|127.0.0.1" + +#https://www.php.net/manual/en/errorfunc.constants.php - CAUTION - wrong setting could break Image Processing tests in BE +#this includes E_RECOVERABLE_ERROR (4096), E_ERROR (1) +TYPO3_CONFIG_SYS_EXCEPTIONALERRORS=4097 +# this includes E_DEPRECATED (8192), E_RECOVERABLE_ERROR (4096), E_WARNING (2) +#TYPO3_CONFIG_SYS_EXCEPTIONALERRORS=12290 diff --git a/src/typo3/.devcontainer/README.md b/src/typo3/.devcontainer/README.md new file mode 100644 index 0000000..1d5c3f2 --- /dev/null +++ b/src/typo3/.devcontainer/README.md @@ -0,0 +1,56 @@ +# TYPO3 Dev Container Configuration + +Normally a TYPO3 setup consists of the following main parts + +* PHP version as required by TYPO3 with the needed extensions enabled + * XDebug support enabled for development needs +* a webserver that + * is enabled to process the *.php files in the TYPO3 folders + * properly configured with secure filters and rewrite rules +* a database backed supported by TYPO3 version + +This Devcontainer blueprint is based upon the following ruleset: + +* we make use of Docker-In-Docker (DIND) within the core devcontainer to provide the best "local-lookalike-feeling" for developers who could thereby launch any additional docker service they want +* the core devcontainer environment contains the PHP environment needed for the designated TYPO3 version in addtion to the souce code checked out into the working directory +* a webserver could be startet either within the core devcontainer (apache) or as a supporting docker container +* a database backend (with the exclusion of SqLite) is started as a separate docker service + +## Configurations provided + +### Apache, PHP 8.4, MariaDB + +The `apache` subdirectory contains Devcontainer configuration for running Apache web server along PHP 8.4 basen on Debian Trixie. + +![Architecture overview](doc/Devcontainer_Apache.drawio.png) + +### PHP 8.4-FPM, Nginx, MariaDB + +The `php-fpm` subdirectory contains Docker configuration for PHP 8.4 FastCGI Process Manager in conjunction with Nginx webserver running as a DIND docker service: + +![Architecture overview](doc/Devcontainer_PHP-FPM.drawio.png) + +### FrankenPHP 8.4 Classic, MariaDB (Default) + +The `frankenphp` subdirectory contains Devcontainer configuration for running FrankenPHP 8.4 based on Debian Trixie. + +![Architecture overview](doc/Devcontainer_FrankenPHP.drawio.png) + +FrankenPHP currently *only* runs in classic mode. Unfortunately worker mode is not supported by TYPO3 at this point of time. + +## Getting started + +* Login to Github WebUI and select the desired branch +* select the dropdown field `<> Code` / Tab `Codespaces` +* in the rown `Codespaces` select the three dots
+ (choosing `+` will launch the default configuration) +* select `+ New with options...` + * verify that the correct branch is selected + * choose the desired devcontainer configuration + * verify the region + * choose a machine type (2 cores should be fine to start) +* select `Create codespace`
+ Now a VS Code UI opens in the browser window. + * please be VERY patient now because all containers have to be build initially including the whole TYPO3 environment. You may follow the progress by pressing `Building codespace...` link in the bottom right corner:
+ ![Building codespace](doc/Github_VsCodeBuildingCodespace.drawio.png)
+ If you later start the codespace again it will come up quickly. diff --git a/src/typo3/.devcontainer/devcontainer.json b/src/typo3/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c52040e --- /dev/null +++ b/src/typo3/.devcontainer/devcontainer.json @@ -0,0 +1,78 @@ +{ + "name": "TYPO3 Dev Container", + "image": "ghcr.io/thucke/devcontainers/typo3-${templateOption:webserver}:1-php${templateOption:phpVersion}-trixie", + "runArgs": [ + "--env-file=.devcontainer/.env" + ], + "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", + "mounts": [ + "source=${localWorkspaceFolderBasename}-build,target=${containerWorkspaceFolder}/.build,type=volume" + ], + "features": { + "ghcr.io/devcontainers/features/sshd:1": {}, + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": false + } + }, + "containerEnv": { + "WORKSPACE_ROOT": "${containerWorkspaceFolder}", + "TYPO3_INSTALL_WEB_SERVER_CONFIG": "other", + "SERVER_NAME": "127.0.0.1", + "COMPOSE_PROFILES": "${templateOption:webserver}" + }, + "forwardPorts": [ + 80, + 2222, + 3306, + 8080 + ], + "portsAttributes": { + "80": { + "label": "Webserver" + }, + "2222": { + "label": "SSH daemon Port" + }, + "3306": { + "label": "Mysql/MariaDB database" + }, + "8080": { + "label": "Mysql Adminer" + } + }, + "onCreateCommand": "chmod -c +x ${WORKSPACE_ROOT}/.devcontainer/onCreateCommandScript.sh && ${WORKSPACE_ROOT}/.devcontainer/onCreateCommandScript.sh", + "postCreateCommand": "${WORKSPACE_ROOT}/.devcontainer/postCreateCommandScript.sh", + "postAttachCommand": "cd ${WORKSPACE_ROOT} && .devcontainer/docker/frankenphp/server.sh && git pull && exit 0", + "customizations": { + "vscode": { + "settings": { + "extensions.autoUpdate": false, + "editor.codeActionsOnSave": { + "source.fixAll.markdownlint": "explicit" + }, + "files.autoSave": "afterDelay", + "files.autoSaveDelay": 1000, + "git.postCommitCommand": "push", + "git.path": "/usr/bin/git" + }, + "extensions": [ + "cweijan.vscode-database-client2", + "ms-vscode-remote.remote-containers", + "benjaminkott.typo3-typoscript", + "ralffreit.typo3snippets", + "ms-azuretools.vscode-containers", + "ms-azuretools.vscode-docker", + "docker.docker", + "donjayamanne.githistory", + "ecmel.vscode-html-css", + "xdebug.php-debug", + "redhat.vscode-yaml", + "DotJoshJohnson.xml", + "SanderRonde.phpstan-vscode", + "eamodio.gitlens", + "DavidAnson.vscode-markdownlint", + "EditorConfig.EditorConfig" + ] + } + } +} diff --git a/src/typo3/.devcontainer/docker/TYPO3.env.tmpl b/src/typo3/.devcontainer/docker/TYPO3.env.tmpl new file mode 100644 index 0000000..427aefa --- /dev/null +++ b/src/typo3/.devcontainer/docker/TYPO3.env.tmpl @@ -0,0 +1,31 @@ +TYPO3_CONTEXT=Development +#TYPO3_CONTEXT=Production + +TYPO3_INSTALL_DB_DRIVER=${TYPO3_INSTALL_DB_DRIVER} +TYPO3_DB_DRIVER=${TYPO3_INSTALL_DB_DRIVER} +TYPO3_INSTALL_DB_USER=${TYPO3_INSTALL_DB_USER} +TYPO3_DB_USERNAME=${TYPO3_INSTALL_DB_USER} +TYPO3_INSTALL_DB_PASSWORD=${TYPO3_INSTALL_DB_PASSWORD} +TYPO3_DB_PASSWORD=${TYPO3_INSTALL_DB_PASSWORD} +TYPO3_INSTALL_DB_HOST=${TYPO3_INSTALL_DB_HOST} +TYPO3_DB_HOST=${TYPO3_INSTALL_DB_HOST} +TYPO3_INSTALL_DB_PORT=${TYPO3_INSTALL_DB_PORT} +TYPO3_DB_PORT=${TYPO3_INSTALL_DB_PORT} +TYPO3_INSTALL_DB_USE_EXISTING=${TYPO3_INSTALL_DB_USE_EXISTING} +TYPO3_INSTALL_DB_DBNAME=${TYPO3_INSTALL_DB_DBNAME} +TYPO3_DB_DBNAME=${TYPO3_INSTALL_DB_DBNAME} +TYPO3_INSTALL_ADMIN_USER=${TYPO3_INSTALL_ADMIN_USER} +TYPO3_SETUP_ADMIN_USERNAME=${TYPO3_INSTALL_ADMIN_USER} +TYPO3_INSTALL_ADMIN_PASSWORD=${TYPO3_INSTALL_ADMIN_PASSWORD} +TYPO3_SETUP_ADMIN_PASSWORD=${TYPO3_INSTALL_ADMIN_PASSWORD} +TYPO3_INSTALL_SITE_NAME=${TYPO3_INSTALL_SITE_NAME} +TYPO3_PROJECT_NAME=${TYPO3_INSTALL_SITE_NAME} +TYPO3_INSTALL_SITE_SETUP_TYPE=${TYPO3_INSTALL_SITE_SETUP_TYPE} +TYPO3_INSTALL_WEB_SERVER_CONFIG=${TYPO3_INSTALL_WEB_SERVER_CONFIG} +TYPO3_SERVER_TYPE=${TYPO3_INSTALL_WEB_SERVER_CONFIG} +TYPO3_CONFIG_SYS_TRUSTED_HOST_PATTERN=${TYPO3_CONFIG_SYS_TRUSTED_HOST_PATTERN} + +#https://www.php.net/manual/en/errorfunc.constants.php - CAUTION - wrong setting could break Image Processing tests in BE +TYPO3_CONFIG_SYS_EXCEPTIONALERRORS=${TYPO3_CONFIG_SYS_EXCEPTIONALERRORS} +#TYPO3_CONFIG_SYS_EXCEPTIONALERRORS=4097 #E_RECOVERABLE_ERROR (4096), E_ERROR (1) +#TYPO3_CONFIG_SYS_EXCEPTIONALERRORS=12290, # this includes E_DEPRECATED (8192), E_RECOVERABLE_ERROR (4096), E_WARNING (2) diff --git a/src/typo3/.devcontainer/docker/apache/.htaccess b/src/typo3/.devcontainer/docker/apache/.htaccess new file mode 100644 index 0000000..767bf21 --- /dev/null +++ b/src/typo3/.devcontainer/docker/apache/.htaccess @@ -0,0 +1,396 @@ +##### +# +# Example .htaccess file for TYPO3 CMS - for use with Apache Webserver +# +# This file includes settings for the following configuration options: +# +# - Compression +# - Caching +# - MIME types +# - Cross Origin requests +# - Rewriting and Access +# - Miscellaneous +# - PHP optimisation +# +# If you want to use it, you have to copy it to the root folder of your TYPO3 installation (if its +# not there already) and rename it to '.htaccess'. To make .htaccess files work, you might need to +# adjust the 'AllowOverride' directive in your Apache configuration file. +# +# IMPORTANT: You may need to change this file depending on your TYPO3 installation! +# Consider adding this file's content to your webserver's configuration directly for speed improvement +# +# Lots of the options are taken from https://github.com/h5bp/html5-boilerplate/blob/master/dist/.htaccess +# +#### + +### Begin: Compression ### + +# Compressing resource files will save bandwidth and so improve loading speed especially for users +# with slower internet connections. TYPO3 can compress the .js and .css files for you. +# *) Uncomment the following lines and +# *) Set $GLOBALS['TYPO3_CONF_VARS']['BE']['compressionLevel'] = 9 for the Backend +# *) Set $GLOBALS['TYPO3_CONF_VARS']['FE']['compressionLevel'] = 9 together with the TypoScript properties +# config.compressJs and config.compressCss for GZIP compression of Frontend JS and CSS files. + +# +# AddType "text/javascript" .gzip +# +# +# AddType "text/css" .gzip +# +#AddEncoding gzip .gzip + + + # Force compression for mangled `Accept-Encoding` request headers + + + SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding + RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding + + + + # Compress all output labeled with one of the following media types. + # + # (!) For Apache versions below version 2.3.7 you don't need to + # enable `mod_filter` and can remove the `` + # and `` lines as `AddOutputFilterByType` is still in + # the core directives. + # + # https://httpd.apache.org/docs/current/mod/mod_filter.html#addoutputfilterbytype + + + AddOutputFilterByType DEFLATE application/atom+xml \ + application/javascript \ + application/json \ + application/ld+json \ + application/manifest+json \ + application/rdf+xml \ + application/rss+xml \ + application/schema+json \ + application/vnd.geo+json \ + application/geo+json \ + application/vnd.ms-fontobject \ + application/x-font-ttf \ + application/x-javascript \ + application/x-web-app-manifest+json \ + application/xhtml+xml \ + application/xml \ + font/eot \ + font/opentype \ + font/otf \ + font/ttf \ + image/bmp \ + image/svg+xml \ + image/vnd.microsoft.icon \ + image/x-icon \ + text/cache-manifest \ + text/css \ + text/html \ + text/javascript \ + text/plain \ + text/vcard \ + text/vnd.rim.location.xloc \ + text/vtt \ + text/x-component \ + text/x-cross-domain-policy \ + text/xml + + + + AddEncoding gzip svgz + + + +### End: Compression ### + + + +### Begin: Browser caching of resource files ### + +# This affects Frontend and Backend and increases performance. + + + ExpiresActive On + ExpiresDefault "access plus 1 month" + + ExpiresByType text/css "access plus 1 year" + + ExpiresByType application/json "access plus 0 seconds" + ExpiresByType application/ld+json "access plus 0 seconds" + ExpiresByType application/schema+json "access plus 0 seconds" + ExpiresByType application/vnd.geo+json "access plus 0 seconds" + ExpiresByType application/geo+json "access plus 0 seconds" + ExpiresByType application/xml "access plus 0 seconds" + ExpiresByType text/xml "access plus 0 seconds" + + ExpiresByType image/vnd.microsoft.icon "access plus 1 week" + ExpiresByType image/x-icon "access plus 1 week" + + ExpiresByType text/x-component "access plus 1 month" + + ExpiresByType text/html "access plus 0 seconds" + + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType application/x-javascript "access plus 1 year" + ExpiresByType text/javascript "access plus 1 year" + + ExpiresByType application/manifest+json "access plus 1 week" + ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" + ExpiresByType text/cache-manifest "access plus 0 seconds" + + ExpiresByType audio/ogg "access plus 1 month" + ExpiresByType image/apng "access plus 1 month" + ExpiresByType image/avif "access plus 1 month" + ExpiresByType image/avif-sequence "access plus 1 month" + ExpiresByType image/bmp "access plus 1 month" + ExpiresByType image/gif "access plus 1 month" + ExpiresByType image/jpeg "access plus 1 month" + ExpiresByType image/jxl "access plus 1 month" + ExpiresByType image/png "access plus 1 month" + ExpiresByType image/svg+xml "access plus 1 month" + ExpiresByType image/webp "access plus 1 month" + ExpiresByType video/mp4 "access plus 1 month" + ExpiresByType video/ogg "access plus 1 month" + ExpiresByType video/webm "access plus 1 month" + + ExpiresByType application/atom+xml "access plus 1 hour" + ExpiresByType application/rdf+xml "access plus 1 hour" + ExpiresByType application/rss+xml "access plus 1 hour" + + ExpiresByType font/collection "access plus 1 month" + ExpiresByType application/vnd.ms-fontobject "access plus 1 month" + ExpiresByType font/eot "access plus 1 month" + ExpiresByType font/opentype "access plus 1 month" + ExpiresByType font/otf "access plus 1 month" + ExpiresByType application/x-font-ttf "access plus 1 month" + ExpiresByType font/ttf "access plus 1 month" + ExpiresByType application/font-woff "access plus 1 month" + ExpiresByType application/x-font-woff "access plus 1 month" + ExpiresByType font/woff "access plus 1 month" + ExpiresByType application/font-woff2 "access plus 1 month" + ExpiresByType font/woff2 "access plus 1 month" + + ExpiresByType text/x-cross-domain-policy "access plus 1 week" + + + +### End: Browser caching of resource files ### + + +### Begin: MIME types ### + +# Proper MIME types for all files + + # Security configuration + RemoveType .html .htm + + AddType text/html .html .htm + + + RemoveType .svg .svgz + + AddType image/svg+xml .svg .svgz + + + # Data interchange + AddType application/atom+xml atom + AddType application/json json map topojson + AddType application/ld+json jsonld + AddType application/rss+xml rss + AddType application/vnd.geo+json geojson + AddType application/xml rdf xml + + # JavaScript + AddType application/javascript js + + # Manifest files + AddType application/manifest+json webmanifest + AddType application/x-web-app-manifest+json webapp + AddType text/cache-manifest appcache + + # Media files + + AddType audio/mp4 f4a f4b m4a + AddType audio/ogg oga ogg opus + AddType image/avif avif + AddType image/avif-sequence avifs + AddType image/bmp bmp + AddType image/jxl jxl + AddType image/webp webp + AddType video/mp4 f4v f4p m4v mp4 + AddType video/ogg ogv + AddType video/webm webm + AddType video/x-flv flv + AddType image/x-icon cur ico + + # Web fonts + AddType font/woff woff + AddType font/woff2 woff2 + AddType application/vnd.ms-fontobject eot + AddType font/ttf ttc ttf + AddType font/otf otf + + # Other + AddType application/octet-stream safariextz + AddType application/x-bb-appworld bbaw + AddType application/x-chrome-extension crx + AddType application/x-opera-extension oex + AddType application/x-xpinstall xpi + AddType text/vcard vcard vcf + AddType text/vnd.rim.location.xloc xloc + AddType text/vtt vtt + AddType text/x-component htc + + + +# UTF-8 encoding +AddDefaultCharset utf-8 + + AddCharset utf-8 .atom .css .js .json .manifest .rdf .rss .vtt .webapp .webmanifest .xml + + +### End: MIME types ### + + + +### Begin: Cross Origin ### + +# Send the CORS header for images when browsers request it. + + + + SetEnvIf Origin ":" IS_CORS + Header set Access-Control-Allow-Origin "*" env=IS_CORS + + + + +# Allow cross-origin access to web fonts. + + + Header set Access-Control-Allow-Origin "*" + + + +### End: Cross Origin ### + + + +### Begin: Rewriting and Access ### + +# You need rewriting, if you use a URL-Rewriting extension (RealURL, CoolUri). + + + + # Enable URL rewriting + RewriteEngine On + + # Store the current location in an environment variable CWD to use + # mod_rewrite in .htaccess files without knowing the RewriteBase + RewriteCond $0#%{REQUEST_URI} ([^#]*)#(.*)\1$ + RewriteRule ^.*$ - [E=CWD:%2] + + # Rules to set ApplicationContext based on hostname + #RewriteCond %{HTTP_HOST} ^dev\.example\.com$ + #RewriteRule .? - [E=TYPO3_CONTEXT:Development] + #RewriteCond %{HTTP_HOST} ^staging\.example\.com$ + #RewriteRule .? - [E=TYPO3_CONTEXT:Production/Staging] + #RewriteCond %{HTTP_HOST} ^www\.example\.com$ + #RewriteRule .? - [E=TYPO3_CONTEXT:Production] + + # Rule for versioned static files, configured through: + # - $GLOBALS['TYPO3_CONF_VARS']['BE']['versionNumberInFilename'] + # - $GLOBALS['TYPO3_CONF_VARS']['FE']['versionNumberInFilename'] + # IMPORTANT: This rule has to be the very first RewriteCond in order to work! + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule ^(.+)\.(\d+)\.(php|js|css|png|jpg|gif|gzip)$ %{ENV:CWD}$1.$3 [L] + + # Access block for folders + RewriteRule _(?:recycler|temp)_/ - [F] + RewriteRule fileadmin/templates/.*\.(?:txt|ts)$ - [F] + RewriteRule ^(?:vendor|typo3_src|typo3temp/var) - [F] + RewriteRule (?:typo3conf/ext|typo3/sysext|typo3/ext)/[^/]+/(?:Configuration|Resources/Private|Tests?|Documentation|docs?)/ - [F] + + # Block access to all hidden files and directories with the exception of + # the visible content from within the `/.well-known/` hidden directory (RFC 5785). + RewriteCond %{REQUEST_URI} "!(^|/)\.well-known/([^./]+./?)+$" [NC] + RewriteCond %{SCRIPT_FILENAME} -d [OR] + RewriteCond %{SCRIPT_FILENAME} -f + RewriteRule (?:^|/)\. - [F] + + # Stop rewrite processing, if we are in any other known directory + # NOTE: Add your additional local storages here + RewriteRule ^(?:fileadmin/|typo3conf/|typo3temp/|uploads/) - [L] + + # If the file/symlink/directory does not exist but is below /typo3/, redirect to the TYPO3 Backend entry point. + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-l + RewriteRule ^typo3/(.*)$ %{ENV:CWD}typo3/index.php [QSA,L] + + # If the file/symlink/directory does not exist => Redirect to index.php. + # For httpd.conf, you need to prefix each '%{REQUEST_FILENAME}' with '%{DOCUMENT_ROOT}'. + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-l + RewriteRule ^.*$ %{ENV:CWD}index.php [QSA,L] + + + +# Access block for files +# Apache < 2.3 + + + Order allow,deny + Deny from all + Satisfy All + + +# Apache ≥ 2.3 + + + Require all denied + + + +# Block access to vcs directories + + RedirectMatch 404 /\.(?:git|svn|hg)/ + + +### End: Rewriting and Access ### + + + +### Begin: Miscellaneous ### + +# 404 error prevention for non-existing redirected folders +Options -MultiViews + +# Make sure that directory listings are disabled. + + Options -Indexes + + + + # Force IE to render pages in the highest available mode + Header set X-UA-Compatible "IE=edge" + + Header unset X-UA-Compatible + + + # Reducing MIME type security risks + Header set X-Content-Type-Options "nosniff" + + +# ETag removal + + Header unset ETag + +FileETag None + +### End: Miscellaneous ### + + +# Add your own rules here. diff --git a/src/typo3/.devcontainer/docker/apache/Dockerfile b/src/typo3/.devcontainer/docker/apache/Dockerfile new file mode 100644 index 0000000..aa1b142 --- /dev/null +++ b/src/typo3/.devcontainer/docker/apache/Dockerfile @@ -0,0 +1,99 @@ +# stick to bookworm until mysqldump supports trixie (Debian 13) +FROM php:8.4-apache-trixie + +# https://github.com/xdebug/xdebug/releases +ARG XDEBUG_VERSION=3.5.0 + +ARG DEVCONTAINER_SERVICE_ID +ARG DEVCONTAINER_SERVICE_NAME +ARG COMPOSE_PROFILES +ARG APACHE_DOCUMENT_ROOT + +ENV DEVCONTAINER_SERVICE_ID=${DEVCONTAINER_SERVICE_ID} +ENV DEVCONTAINER_SERVICE_NAME=${DEVCONTAINER_SERVICE_NAME} +ENV APACHE_RUN_USER=${DEVCONTAINER_SERVICE_NAME} +ENV APACHE_RUN_GROUP=${DEVCONTAINER_SERVICE_NAME} +ENV COMPOSE_PROFILES=${COMPOSE_PROFILES} +ENV APACHE_DOCUMENT_ROOT=${APACHE_DOCUMENT_ROOT} + +EXPOSE 80 443 +USER root + +COPY docker/typo3/php.ini /usr/local/etc/php/php.ini + +RUN addgroup --gid ${DEVCONTAINER_SERVICE_ID} ${DEVCONTAINER_SERVICE_NAME} && \ + adduser --gid ${DEVCONTAINER_SERVICE_ID} --no-create-home --home /nonexistent --comment "devcontainer service user" \ + --disabled-password --shell /bin/false --uid ${DEVCONTAINER_SERVICE_ID} $DEVCONTAINER_SERVICE_NAME + +RUN apt-get update && \ + apt-get install -y \ + libssl-dev libicu-dev \ + libzip-dev zlib1g-dev \ + libgmp-dev \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libmagickwand-dev \ + locales locales-all \ + openssh-client \ + vim \ + procps \ + unzip \ + libfcgi-bin \ + imagemagick \ + ghostscript \ + mariadb-client && \ + #apt-get install -y iproute2 iputils-ping && \ + apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ + sed -i 's/^# *\(en_US.UTF-8\)/\1/' /etc/locale.gen && \ + locale-gen en_US.UTF-8 && \ + update-locale LANG=en_US.UTF-8 &&\ + sed -ri -e "s!/var/www/html!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/sites-available/*.conf && \ + sed -ri -e "s!/var/www/!${APACHE_DOCUMENT_ROOT}!g" /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf && \ + echo "UseCanonicalName On" >>../apache2.conf && \ + ln -sfr /etc/apache2/mods-available/expires.load /etc/apache2/mods-enabled/expires.load && \ + ln -sfr /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/headers.load + +RUN docker-php-ext-install calendar bcmath exif ftp gmp intl zip mysqli pdo pdo_mysql && \ + docker-php-ext-enable pdo_mysql && \ + docker-php-ext-configure gd --with-freetype --with-jpeg && \ + docker-php-ext-install -j$(nproc) gd && \ + echo 'yes' | pecl install imagick && docker-php-ext-enable imagick && \ + pecl install xdebug-${XDEBUG_VERSION} && docker-php-ext-enable xdebug + + +#RUN docker-php-ext-enable opcache +#RUN docker-php-ext-install tokenizer +#RUN docker-php-ext-install json +#RUN apt-get install -y \ +# libonig-dev \ +# && docker-php-ext-install iconv mbstring + +#RUN apt-get install -y \ +# libcurl4-openssl-dev \ +# && docker-php-ext-install curl + +#RUN docker-php-ext-install phar + +#RUN apt-get install -y \ +# libmcrypt-dev \ +# && docker-php-ext-install session + +#RUN apt-get install -y \ +# libxml2-dev \ +# && docker-php-ext-install simplexml xml + #&& docker-php-ext-install xmlrpc https://php.watch/versions/8.0/xmlrpc#alternatives + +ENV COMPOSER_BINARY=/usr/local/bin/composer \ + COMPOSER_HOME=/usr/local/composer +ENV PATH=$PATH:$COMPOSER_HOME + +RUN curl -sS https://getcomposer.org/installer | php && \ + mv composer.phar "$COMPOSER_BINARY" && \ + chmod +x "$COMPOSER_BINARY" + +################################################################################## +# configuration according to https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Administration/Installation/SystemRequirements/Index.html#using-typo3-with-docker-based-environments +################################################################################## +# Enable Apache mod_rewrite +RUN a2enmod rewrite diff --git a/src/typo3/.devcontainer/docker/apache/preStop.sh b/src/typo3/.devcontainer/docker/apache/preStop.sh new file mode 100644 index 0000000..2fd1f4c --- /dev/null +++ b/src/typo3/.devcontainer/docker/apache/preStop.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# post start script +echo "BEGIN: Apache preStop.sh" + +pushd ${WORKSPACE_ROOT} +trap "popd" EXIT + +# needed as directory check moans about wrong permissions +chmod +x .build/typo3/dumpData.sh +.build/typo3/dumpData.sh + +popd +echo "END: Apache preStop.sh" diff --git a/src/typo3/.devcontainer/docker/db/initdb/.env b/src/typo3/.devcontainer/docker/db/initdb/.env new file mode 100644 index 0000000..26e325f --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/initdb/.env @@ -0,0 +1,4 @@ +MYSQL_DATABASE=${TYPO3_INSTALL_DB_DBNAME} +MYSQL_USER=${TYPO3_INSTALL_DB_USER} +MYSQL_PASSWORD=${TYPO3_INSTALL_DB_PASSWORD} +MYSQL_ROOT_PASSWORD=${DB_CONNECTION_ROOT_PASSWORD} diff --git a/src/typo3/.devcontainer/docker/db/initdb/2_create_procedure.sql b/src/typo3/.devcontainer/docker/db/initdb/2_create_procedure.sql new file mode 100644 index 0000000..42b81c8 --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/initdb/2_create_procedure.sql @@ -0,0 +1,55 @@ +/*!40101 SET COLLATION_CONNECTION=@@COLLATION_DATABASE */; +delimiter // + +CREATE PROCEDURE IF NOT EXISTS fixImgInTtContent() +BEGIN + DECLARE checkuid INT; + DECLARE checkbodytext LONGTEXT; + DECLARE checkconfiguration LONGTEXT; + DECLARE checkidentifier TEXT; + DECLARE cur1_list_isdone BOOLEAN DEFAULT FALSE; + DECLARE imgCount INT; + + # first fetch all tt_content having suspicious img elements + DECLARE cur1 CURSOR FOR SELECT tt_content.uid, tt_content.bodytext + FROM tt_content + WHERE ExtractValue(bodytext,'count(//img)')>0 and bodytext like "%data-htmlarea-file-uid%" and bodytext like '%data-htmlarea-file-table="sys_file"%' + ORDER BY tt_content.uid; + DECLARE CONTINUE HANDLER FOR NOT FOUND SET cur1_list_isdone = TRUE; + + + # process all suspicous tt_content + OPEN cur1; + content_loop: LOOP + FETCH FROM cur1 INTO checkuid, checkbodytext; + IF cur1_list_isdone THEN LEAVE content_loop; END IF; + SELECT ExtractValue(checkbodytext,'count(//img)') INTO imgCount FROM dual; + REPEAT + # process all img elements in this bodytext + BEGIN + SELECT ExtractValue(checkbodytext, concat('//img[', imgCount, ']/@data-htmlarea-file-uid' )), + ExtractValue(sys_file_storage.configuration,'//field[@index="basePath"]/value'), + sys_file.identifier + # fetch original file uid, storage basePath and file identifier + into @file_uid, @file_src_fixed, @sys_file_identifier + FROM sys_file, sys_file_storage + # only select the current img element and join to sys_file and sys_file_storage + WHERE ExtractValue(checkbodytext, concat('//img[', imgCount, ']/@data-htmlarea-file-table'))='sys_file' + AND ExtractValue(checkbodytext,concat('//img[', imgCount, ']/@data-htmlarea-file-uid'))=sys_file.uid + AND sys_file.storage=sys_file_storage.uid; + + # build the correct src value + SET @file_src_update = concat("/", @file_src_fixed, @sys_file_identifier); + + # update the current img element in the bodytext + SET checkbodytext = UpdateXML(checkbodytext, concat('//img[' COLLATE utf8mb4_0900_ai_ci, imgCount, ']/@src'), concat('src="', @file_src_update, '"')); + SET imgCount = imgCount - 1; + END; + UNTIL imgCount = 0 END REPEAT; + + # store the updated bodytext back to the record + update tt_content set bodytext = checkbodytext where uid=checkuid; + commit; + END LOOP; + CLOSE cur1; +END; diff --git a/src/typo3/.devcontainer/docker/db/mariadb/Dockerfile b/src/typo3/.devcontainer/docker/db/mariadb/Dockerfile new file mode 100644 index 0000000..abc3f48 --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/mariadb/Dockerfile @@ -0,0 +1,3 @@ +FROM mysql:10.11 + + diff --git a/src/typo3/.devcontainer/docker/db/mariadb/mysql.cnf b/src/typo3/.devcontainer/docker/db/mariadb/mysql.cnf new file mode 100644 index 0000000..820eb42 --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/mariadb/mysql.cnf @@ -0,0 +1,6 @@ +[mysqld] +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci +skip-character-set-client-handshake + +bind-address = 0.0.0.0 diff --git a/src/typo3/.devcontainer/docker/db/mysql/Dockerfile b/src/typo3/.devcontainer/docker/db/mysql/Dockerfile new file mode 100644 index 0000000..bee009f --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/mysql/Dockerfile @@ -0,0 +1,4 @@ +FROM mysql:8.0 + +#COPY docker/db/mysql/mysql.cnf /etc/mysql/conf.d/custom.cnf +#COPY docker/db/initdb/*.sql /docker-entrypoint-initdb.d diff --git a/src/typo3/.devcontainer/docker/db/mysql/mysql.cnf b/src/typo3/.devcontainer/docker/db/mysql/mysql.cnf new file mode 100644 index 0000000..820eb42 --- /dev/null +++ b/src/typo3/.devcontainer/docker/db/mysql/mysql.cnf @@ -0,0 +1,6 @@ +[mysqld] +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci +skip-character-set-client-handshake + +bind-address = 0.0.0.0 diff --git a/src/typo3/.devcontainer/docker/docker-compose.backend.yaml b/src/typo3/.devcontainer/docker/docker-compose.backend.yaml new file mode 100644 index 0000000..08cf560 --- /dev/null +++ b/src/typo3/.devcontainer/docker/docker-compose.backend.yaml @@ -0,0 +1,82 @@ +# https://www.rutschmann.biz/de-de/typo3-blog/detail/typo3-mit-solr-und-nginx-mittels-docker-compose-auf-auf-amazon-ec2#c542 +# https://medium.com/@tech_18484/deploying-a-php-web-app-with-docker-compose-nginx-and-mariadb-d61a84239c0d +# https://blog.hdnet.de/blog/setting-up-typo3-9-locally +services: + nginx: + build: + context: ${WORKSPACE_ROOT}/.devcontainer/docker/nginx/. + args: + DEVCONTAINER_SERVICE_NAME: + DEVCONTAINER_SERVICE_ID: + depends_on: + db: + condition: service_healthy + required: false + #php: + # condition: service_started + # required: true + #- solr + restart: unless-stopped + ports: + - '80:80' + volumes: + - ${WORKSPACE_ROOT}:/workspaces/TYPO3.introduction:rw,cached + #- /etc/letsencrypt/live/:/etc/letsencrypt/live + #- /etc/letsencrypt/archive:/etc/letsencrypt/archive + #- /etc/ssl/:/etc/ssl + - ${WORKSPACE_ROOT}/.devcontainer/docker/nginx/templates:/etc/nginx/templates:rw,cached + - ${WORKSPACE_ROOT}/.devcontainer/docker/nginx/includes:/etc/nginx/includes:rw,cached + networks: + - devcontainer + profiles: [php-fpm] + db: + build: + context: .. + dockerfile: ${WORKSPACE_ROOT}/.devcontainer/docker/db/mysql/Dockerfile + command: mysqld + restart: unless-stopped + volumes: + #- db-data:/var/lib/mysql + - type: bind + source: ${WORKSPACE_ROOT}/.devcontainer/docker/db/mysql/mysql.cnf + target: /etc/mysql/conf.d/custom.cnf + - type: bind + source: ${WORKSPACE_ROOT}/.devcontainer/docker/db/initdb + target: /docker-entrypoint-initdb.d + networks: + - devcontainer + ports: + - 3306:3306 + env_file: + - path: ${WORKSPACE_ROOT}/.devcontainer/.env + required: "true" + - path: ${WORKSPACE_ROOT}/.devcontainer/docker/db/initdb/.env + required: "true" + healthcheck: + test: ["CMD-SHELL", "mysql -uroot -p$${MYSQL_ROOT_PASSWORD} --execute \"SHOW DATABASES;\" || exit 1"] + interval: 20s + timeout: 5s + retries: 5 + #start_period: 10s + #start_interval: 5s + adminer: + image: adminer:latest + restart: unless-stopped + environment: + TZ: "Europe/Berlin" + networks: + - devcontainer + ports: + - 8080:8080 + depends_on: + db: + condition: service_healthy + required: false + +networks: + devcontainer: + # Specify driver options + driver: bridge + enable_ipv6: false + driver_opts: + com.docker.network.bridge.host_binding_ipv4: "127.0.0.1" diff --git a/src/typo3/.devcontainer/docker/igniteEnvironment.sh b/src/typo3/.devcontainer/docker/igniteEnvironment.sh new file mode 100644 index 0000000..c550acc --- /dev/null +++ b/src/typo3/.devcontainer/docker/igniteEnvironment.sh @@ -0,0 +1,59 @@ +#!/bin/bash +set -u + +echo "BEGIN: igniteEnvironment.sh" + +script=$(readlink -f "$0") +scriptdir=$(dirname "$script") + +source $scriptdir/parseDotEnv.sh + +pushd ${WORKSPACE_ROOT} + +echo "Step 1/6: Reset environment" +rm -rf config +rm -rf .build/bin +rm -rf .build/public +rm -rf .build/vendor +rm -rf var + +mkdir -vp .build/public +mkdir -vp var/log/ + + +if [ "${TYPO3_INSTALL_DB_DRIVER}" == "mysqli" ]; then + echo "Using MySQL/MariaDB as database - resetting database" + # Drop all database tables + mysql -h${TYPO3_INSTALL_DB_HOST} -P${TYPO3_INSTALL_DB_PORT} -u${TYPO3_INSTALL_DB_USER} -p${TYPO3_INSTALL_DB_PASSWORD} --silent --skip-column-names -e "SHOW TABLES" ${TYPO3_INSTALL_DB_DBNAME} | + xargs -L1 -I% echo 'SET FOREIGN_KEY_CHECKS = 0; DROP TABLE %; SET FOREIGN_KEY_CHECKS = 1;' | + mysql -h${TYPO3_INSTALL_DB_HOST} -P${TYPO3_INSTALL_DB_PORT} -u${TYPO3_INSTALL_DB_USER} -p${TYPO3_INSTALL_DB_PASSWORD} -v ${TYPO3_INSTALL_DB_DBNAME} + + mysql -h${TYPO3_INSTALL_DB_HOST} -P${TYPO3_INSTALL_DB_PORT} -u${TYPO3_INSTALL_DB_USER} -p${TYPO3_INSTALL_DB_PASSWORD} --init-command='USE '${TYPO3_INSTALL_DB_DBNAME} < .devcontainer/docker/db/initdb/2_create_procedure.sql +fi + +echo "Step 2/6: postStart.sh" +chmod +x .devcontainer/docker/postStart.sh +.devcontainer/docker/postStart.sh + +echo "Step 3/6: Composer up" +composer up + +echo "Step 4/6: Add additional.php if not exists" +[ ! -f config/system/additional.php ] && cp .devcontainer/docker/typo3/additional.php config/system/additional.php + +echo "Step 5/6: Fix TYPO3 image references" +mysql -h127.0.0.1 -P3306 -u${TYPO3_INSTALL_DB_USER} -p${TYPO3_INSTALL_DB_PASSWORD} -e 'use '${TYPO3_INSTALL_DB_DBNAME}'; call fixImgInTtContent();' + +if [[ "${COMPOSE_PROFILES}" =~ "php-fpm" ]]; then + echo "Step 6/6: Checking for running php-fpm" + .devcontainer/php-fpm/server.sh restart +elif [[ "${COMPOSE_PROFILES}" =~ "apache" ]]; then + echo "Step 6/6: Restarting Apache" + .devcontainer/php-fpm/server.sh +elif [[ "${COMPOSE_PROFILES}" =~ "frankenphp" ]]; then + echo "Step 6/6: Restarting FrankenPHP" + .devcontainer/docker/frankenphp/server.sh restart +fi + +echo "END: igniteEnvironment.sh" +popd diff --git a/src/typo3/.devcontainer/docker/nginx/Dockerfile b/src/typo3/.devcontainer/docker/nginx/Dockerfile new file mode 100644 index 0000000..3ede3c6 --- /dev/null +++ b/src/typo3/.devcontainer/docker/nginx/Dockerfile @@ -0,0 +1,15 @@ +FROM nginx:1.29-alpine-slim + +ARG DEVCONTAINER_SERVICE_ID +ARG DEVCONTAINER_SERVICE_NAME + +RUN addgroup -g $DEVCONTAINER_SERVICE_ID -S $DEVCONTAINER_SERVICE_NAME && \ + adduser -S -D -H -u $DEVCONTAINER_SERVICE_ID -h /home/$DEVCONTAINER_SERVICE_NAME -s /sbin/nologin \ + -G $DEVCONTAINER_SERVICE_NAME -g $DEVCONTAINER_SERVICE_NAME $DEVCONTAINER_SERVICE_NAME && \ + sed -i "s/user\s*nginx/user\ $DEVCONTAINER_SERVICE_NAME/" /etc/nginx/nginx.conf && \ + apk add --no-cache fcgi + +VOLUME /workspaces/TYPO3.introduction +EXPOSE 80 + +WORKDIR /workspaces/TYPO3.introduction diff --git a/src/typo3/.devcontainer/docker/nginx/includes/cache-header.conf b/src/typo3/.devcontainer/docker/nginx/includes/cache-header.conf new file mode 100644 index 0000000..6f1f89f --- /dev/null +++ b/src/typo3/.devcontainer/docker/nginx/includes/cache-header.conf @@ -0,0 +1,4 @@ +location ~* \.(js|css|png|jpg|jpeg|gif|svg|ico|webp|woff|woff2|otf|eot)$ { + expires 180d; + add_header Cache-Control "public, no-transform"; +} diff --git a/src/typo3/.devcontainer/docker/nginx/includes/compression.conf b/src/typo3/.devcontainer/docker/nginx/includes/compression.conf new file mode 100644 index 0000000..2d831e4 --- /dev/null +++ b/src/typo3/.devcontainer/docker/nginx/includes/compression.conf @@ -0,0 +1,31 @@ +gzip on; +gzip_vary on; +gzip_proxied any; +gzip_comp_level 8; +gzip_buffers 16 8k; +gzip_http_version 1.1; +gzip_min_length 256; +gzip_types + application/atom+xml + application/geo+json + application/javascript + application/x-javascript + application/json + application/ld+json + application/manifest+json + application/rdf+xml + application/rss+xml + application/xhtml+xml + application/xml + font/eot + font/otf + font/ttf + font/woff + font/woff2 + image/svg+xml + image/png + image/webp + text/css + text/javascript + text/plain + text/xml; diff --git a/src/typo3/.devcontainer/docker/nginx/includes/typo3.conf b/src/typo3/.devcontainer/docker/nginx/includes/typo3.conf new file mode 100644 index 0000000..a67c06a --- /dev/null +++ b/src/typo3/.devcontainer/docker/nginx/includes/typo3.conf @@ -0,0 +1,161 @@ +# source: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/Security/GuidelinesAdministrators/RestrictAccessToFiles.html#nginx-web-servers-configuration-both-installation-modes +# Disable sendfile as per https://docs.vagrantup.com/v2/synced-folders/virtualbox.html +sendfile off; +error_log /dev/stdout info; +access_log /var/log/nginx/access.log; + +# Security: Content-Security-Policy +# ================================= +# +# Add CSP header for possible vulnerable files stored in fileadmin see: +# * https://typo3.org/security/advisory/typo3-psa-2019-010 +# * https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/Security/GuidelinesAdministrators/ContentSecurityPolicy.html +# * https://github.com/TYPO3/TYPO3.CMS/blob/master/typo3/sysext/install/Resources/Private/FolderStructureTemplateFiles/resources-root-htaccess + +# Add strict CSP header depending on mapping (fileadmin only) +add_header Content-Security-Policy $csp_header; + +# matching requested *.pdf files only (strict rules block Safari showing PDF documents) +location ~ \/(?:fileadmin|uploads)\/.*\.pdf$ { + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline'; script-src 'none'; object-src 'self'; require-trusted-types-for 'script'; plugin-types application/pdf;"; +} + +# matching anything else, using negative lookbehind pattern +location ~ \/(?:fileadmin|uploads)\/.*(? https://github.com/renatomefi +# The original code lives in https://github.com/renatomefi/php-fpm-healthcheck +# +# A POSIX compliant shell script to healthcheck PHP fpm status, can be used only for pinging the status page +# or check for specific metrics +# +# i.e.: ./php-fpm-healthcheck --verbose --active-processes=6 +# The script will fail in case the 'active processes' is bigger than 6. +# +# You can combine multiple options as well, the first one to fail will fail the healthcheck +# i.e.: ./php-fpm-healthcheck --listen-queue-len=10 --active-processes=6 +# +# Ping mode (exit 0 if php-fpm returned data): ./php-fpm-healthcheck +# +# Ping mode with data (outputs php-fpm status text): ./php-fpm-healthcheck -v +# +# Exit status codes: +# 2,9,111 - Couldn't connect to PHP fpm, is it running? +# 8 - Couldn't reach PHP fpm status page, have you configured it with `pm.status_path = /status`? +# 1 - A healthcheck condition has failed +# 3 - Invalid option given +# 4 - One or more required softwares are missing +# +# Available options: +# -v|--verbose +# +# Metric options, fails in case the CURRENT VALUE is bigger than the GIVEN VALUE +# --accepted-conn=n +# --listen-queue=n +# --max-listen-queue=n +# --idle-processes=n +# --active-processes=n +# --total-processes=n +# --max-active-processes=n +# --max-children-reached=n +# --slow-requests=n +# + +set -eu + +OPTIND=1 # Reset getopt in case it has been used previously in the shell + +# Required software +FCGI_CMD_PATH=$(command -v cgi-fcgi) || { >&2 echo "Make sure fcgi is installed (i.e. apk add --no-cache fcgi). Aborting."; exit 4; } +command -v sed 1> /dev/null || { >&2 echo "Make sure sed is installed (i.e. apk add --no-cache busybox). Aborting."; exit 4; } +command -v tail 1> /dev/null || { >&2 echo "Make sure tail is installed (i.e. apk add --no-cache busybox). Aborting."; exit 4; } +command -v grep 1> /dev/null || { >&2 echo "Make sure grep is installed (i.e. apk add --no-cache grep). Aborting."; exit 4; } + +# Get status from fastcgi connection +# $1 - cgi-fcgi connect argument +get_fpm_status() { + if test "$VERBOSE" = 1; then printf "Trying to connect to php-fpm via: %s%s\\n" "$1" "$SCRIPT_NAME"; fi; + + # Since I cannot use pipefail I'll just split these in two commands + FPM_STATUS=$(env -i REQUEST_METHOD="$REQUEST_METHOD" SCRIPT_NAME="$SCRIPT_NAME" SCRIPT_FILENAME="$SCRIPT_FILENAME" "$FCGI_CMD_PATH" -bind -connect "$1" 2> /dev/null) + FPM_STATUS=$(echo "$FPM_STATUS" | tail -n +5) + + if test "$VERBOSE" = 1; then printf "php-fpm status output:\\n%s\\n" "$FPM_STATUS"; fi; + + if test "$FPM_STATUS" = "File not found."; then + >&2 printf "php-fpm status page non reachable\\n"; + exit 8; + fi; +} + +# $1 - fpm option +# $2 - expected value threshold +check_fpm_health_by() { + OPTION=$(echo "$1" | sed 's/--//g; s/-/ /g;') + VALUE_EXPECTED="$2"; + VALUE_ACTUAL=$(echo "$FPM_STATUS" | grep "^$OPTION:" | cut -d: -f2 | sed 's/ //g') + + if test "$VERBOSE" = 1; then printf "'%s' value '%s' and expected is less than '%s'\\n" "$OPTION" "$VALUE_ACTUAL" "$VALUE_EXPECTED"; fi; + + if test "$VALUE_ACTUAL" -gt "$VALUE_EXPECTED"; then + >&2 printf "'%s' value '%s' is greater than expected '%s'\\n" "$OPTION" "$VALUE_ACTUAL" "$VALUE_EXPECTED"; + exit 1; + fi; +} + +PARAM_AMOUNT=0 + +# $1 - fpm option +# $2 - expected value threshold +check_later() { + # The POSIX sh way to check if it's an integer, also the output is supressed since it's polution + if ! test "$2" -eq "$2" 2> /dev/null; then + >&2 printf "'%s' option value must be an integer, '%s' given\\n" "$1" "$2"; exit 3; + fi + + PARAM_AMOUNT=$(( PARAM_AMOUNT + 1 )) + + eval "PARAM_TO_CHECK$PARAM_AMOUNT=$1" + eval "VALUE_TO_CHECK$PARAM_AMOUNT=$2" +} + +# From the PARAM_TO_CHECK/VALUE_TO_CHECK magic variables, do all the checks +check_fpm_health() { + j=1 + while [ $j -le $PARAM_AMOUNT ]; do + eval "CURRENT_PARAM=\$PARAM_TO_CHECK$j" + eval "CURRENT_VALUE=\$VALUE_TO_CHECK$j" + check_fpm_health_by "$CURRENT_PARAM" "$CURRENT_VALUE" + j=$(( j + 1 )) + done +} + +if ! GETOPT=$(getopt -o v --long verbose,accepted-conn:,listen-queue:,max-listen-queue:,listen-queue-len:,idle-processes:,active-processes:,total-processes:,max-active-processes:,max-children-reached:,slow-requests: -n 'php-fpm-healthcheck' -- "$@"); then + >&2 echo "Invalid options, terminating." ; exit 3 +fi; + +eval set -- "$GETOPT" + +# FastCGI variables +FCGI_CONNECT_DEFAULT="php-fpm:9000" +FCGI_STATUS_PATH_DEFAULT="/status" + +export REQUEST_METHOD="GET" +export SCRIPT_NAME="${FCGI_STATUS_PATH:-$FCGI_STATUS_PATH_DEFAULT}" +export SCRIPT_FILENAME="${FCGI_STATUS_PATH:-$FCGI_STATUS_PATH_DEFAULT}" +FCGI_CONNECT="${FCGI_CONNECT:-$FCGI_CONNECT_DEFAULT}" + +VERBOSE=0 + +while test "$1"; do + case "$1" in + -v|--verbose ) VERBOSE=1; shift ;; + --) shift ; break ;; + * ) check_later "$1" "$2"; shift 2 ;; + esac +done + +FPM_STATUS=false + +get_fpm_status "$FCGI_CONNECT" +check_fpm_health diff --git a/src/typo3/.devcontainer/docker/php/www.conf b/src/typo3/.devcontainer/docker/php/www.conf new file mode 100644 index 0000000..3d0b47b --- /dev/null +++ b/src/typo3/.devcontainer/docker/php/www.conf @@ -0,0 +1,490 @@ +; Start a new pool named 'www'. +; the variable $pool can be used in any directive and will be replaced by the +; pool name ('www' here) +[www] + +; Per pool prefix +; It only applies on the following directives: +; - 'access.log' +; - 'slowlog' +; - 'listen' (unixsocket) +; - 'chroot' +; - 'chdir' +; - 'php_values' +; - 'php_admin_values' +; When not set, the global prefix (or NONE) applies instead. +; Note: This directive can also be relative to the global prefix. +; Default Value: none +;prefix = /path/to/pools/$pool + +; Unix user/group of the child processes. This can be used only if the master +; process running user is root. It is set after the child process is created. +; The user and group can be specified either by their name or by their numeric +; IDs. +; Note: If the user is root, the executable needs to be started with +; --allow-to-run-as-root option to work. +; Default Values: The user is set to master process running user by default. +; If the group is not set, the user's group is used. +user = devcontainer +group = devcontainer + +; The address on which to accept FastCGI requests. +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Note: This value is mandatory. +listen = 0.0.0.0:9000 + +; Set listen(2) backlog. +; Default Value: 511 (-1 on Linux, FreeBSD and OpenBSD) +;listen.backlog = 511 + +; Set permissions for unix socket, if one is used. In Linux, read/write +; permissions must be set in order to allow connections from a web server. Many +; BSD-derived systems allow connections regardless of permissions. The owner +; and group can be specified either by name or by their numeric IDs. +; Default Values: Owner is set to the master process running user. If the group +; is not set, the owner's group is used. Mode is set to 0660. +;listen.owner = www-data +;listen.group = devcontainer +;listen.mode = 0660 + +; When POSIX Access Control Lists are supported you can set them using +; these options, value is a comma separated list of user/group names. +; When set, listen.owner and listen.group are ignored +;listen.acl_users = +;listen.acl_groups = + +; List of addresses (IPv4/IPv6) of FastCGI clients which are allowed to connect. +; Equivalent to the FCGI_WEB_SERVER_ADDRS environment variable in the original +; PHP FCGI (5.2.2+). Makes sense only with a tcp listening socket. Each address +; must be separated by a comma. If this value is left blank, connections will be +; accepted from any ip address. +; Default Value: any +;listen.allowed_clients = 127.0.0.1 + +; Set the associated the route table (FIB). FreeBSD only +; Default Value: -1 +;listen.setfib = 1 + +; Specify the nice(2) priority to apply to the pool processes (only if set) +; The value can vary from -19 (highest priority) to 20 (lower priority) +; Note: - It will only work if the FPM master process is launched as root +; - The pool processes will inherit the master process priority +; unless it specified otherwise +; Default Value: no set +; process.priority = -19 + +; Set the process dumpable flag (PR_SET_DUMPABLE prctl for Linux or +; PROC_TRACE_CTL procctl for FreeBSD) even if the process user +; or group is different than the master process user. It allows to create process +; core dump and ptrace the process for the pool user. +; Default Value: no +; process.dumpable = yes + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; pm.max_spawn_rate - the maximum number of rate to spawn child +; processes at once. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = dynamic + +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. +pm.max_children = 5 + +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: (min_spare_servers + max_spare_servers) / 2 +pm.start_servers = 2 + +; The desired minimum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.min_spare_servers = 1 + +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = 3 + +; The number of rate to spawn child processes at once. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +; Default Value: 32 +;pm.max_spawn_rate = 32 + +; The number of seconds after which an idle process will be killed. +; Note: Used only when pm is set to 'ondemand' +; Default Value: 10s +;pm.process_idle_timeout = 10s; + +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +;pm.max_requests = 500 + +; The URI to view the FPM status page. If this value is not set, no URI will be +; recognized as a status page. It shows the following information: +; pool - the name of the pool; +; process manager - static, dynamic or ondemand; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; +; idle processes - the number of idle processes; +; active processes - the number of active processes; +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; +; max children reached - number of times, the process limit has been reached, +; when pm tries to start more children (works only for +; pm 'dynamic' and 'ondemand'); +; Value are updated in real time. +; Example output: +; pool: www +; process manager: static +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; +; By default the status page output is formatted as text/plain. Passing either +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: +; http://www.foo.bar/status +; http://www.foo.bar/status?json +; http://www.foo.bar/status?html +; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then information is related to the +; last request the process has served. Otherwise information is related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: /usr/local/share/php/fpm/status.html +; +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +pm.status_path = /status + +; The address on which to accept FastCGI status request. This creates a new +; invisible pool that can handle requests independently. This is useful +; if the main pool is busy with long running requests because it is still possible +; to get the status before finishing the long running requests. +; +; Valid syntaxes are: +; 'ip.add.re.ss:port' - to listen on a TCP socket to a specific IPv4 address on +; a specific port; +; '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6 address on +; a specific port; +; 'port' - to listen on a TCP socket to all addresses +; (IPv6 and IPv4-mapped) on a specific port; +; '/path/to/unix/socket' - to listen on a unix socket. +; Default Value: value of the listen option +;pm.status_listen = 127.0.0.1:9001 + +; The ping URI to call the monitoring page of FPM. If this value is not set, no +; URI will be recognized as a ping page. This could be used to test from outside +; that FPM is alive and responding, or to +; - create a graph of FPM availability (rrd or such); +; - remove a server from a group if it is not responding (load balancing); +; - trigger alerts for the operating team (24/7). +; Note: The value must start with a leading slash (/). The value can be +; anything, but it may not be a good idea to use the .php extension or it +; may conflict with a real PHP file. +; Default Value: not set +ping.path = /ping + +; This directive may be used to customize the response of a ping request. The +; response is formatted as text/plain with a 200 response code. +; Default Value: pong +;ping.response = pong + +; The access log file +; Default: not set +;access.log = log/$pool.access.log + +; The access log format. +; The following syntax is allowed +; %%: the '%' character +; %C: %CPU used by the request +; it can accept the following format: +; - %{user}C for user CPU only +; - %{system}C for system CPU only +; - %{total}C for user + system CPU (default) +; %d: time taken to serve the request +; it can accept the following format: +; - %{seconds}d (default) +; - %{milliseconds}d +; - %{milli}d +; - %{microseconds}d +; - %{micro}d +; %e: an environment variable (same as $_ENV or $_SERVER) +; it must be associated with embraces to specify the name of the env +; variable. Some examples: +; - server specifics like: %{REQUEST_METHOD}e or %{SERVER_PROTOCOL}e +; - HTTP headers like: %{HTTP_HOST}e or %{HTTP_USER_AGENT}e +; %f: script filename +; %l: content-length of the request (for POST request only) +; %m: request method +; %M: peak of memory allocated by PHP +; it can accept the following format: +; - %{bytes}M (default) +; - %{kilobytes}M +; - %{kilo}M +; - %{megabytes}M +; - %{mega}M +; %n: pool name +; %o: output header +; it must be associated with embraces to specify the name of the header: +; - %{Content-Type}o +; - %{X-Powered-By}o +; - %{Transfert-Encoding}o +; - .... +; %p: PID of the child that serviced the request +; %P: PID of the parent of the child that serviced the request +; %q: the query string +; %Q: the '?' character if query string exists +; %r: the request URI (without the query string, see %q and %Q) +; %R: remote IP address +; %s: status (response code) +; %t: server time the request was received +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsulated in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %T: time the log has been written (the request has finished) +; it can accept a strftime(3) format: +; %d/%b/%Y:%H:%M:%S %z (default) +; The strftime(3) format must be encapsulated in a %{}t tag +; e.g. for a ISO8601 formatted timestring, use: %{%Y-%m-%dT%H:%M:%S%z}t +; %u: basic auth user if specified in Authorization header +; +; Default: "%R - %u %t \"%m %r\" %s" +;access.format = "%R - %u %t \"%m %r%Q%q\" %s %f %{milli}d %{kilo}M %C%%" + +; A list of request_uri values which should be filtered from the access log. +; +; As a security precaution, this setting will be ignored if: +; - the request method is not GET or HEAD; or +; - there is a request body; or +; - there are query parameters; or +; - the response code is outwith the successful range of 200 to 299 +; +; Note: The paths are matched against the output of the access.format tag "%r". +; On common configurations, this may look more like SCRIPT_NAME than the +; expected pre-rewrite URI. +; +; Default Value: not set +;access.suppress_path[] = /ping +;access.suppress_path[] = /health_check.php + +; The log file for slow requests +; Default Value: not set +; Note: slowlog is mandatory if request_slowlog_timeout is set +;slowlog = log/$pool.log.slow + +; The timeout for serving a single request after which a PHP backtrace will be +; dumped to the 'slowlog' file. A value of '0s' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_slowlog_timeout = 0 + +; Depth of slow log stack trace. +; Default Value: 20 +;request_slowlog_trace_depth = 20 + +; The timeout for serving a single request after which the worker process will +; be killed. This option should be used when the 'max_execution_time' ini option +; does not stop script execution for some reason. A value of '0' means 'off'. +; Available units: s(econds)(default), m(inutes), h(ours), or d(ays) +; Default Value: 0 +;request_terminate_timeout = 0 + +; The timeout set by 'request_terminate_timeout' ini option is not engaged after +; application calls 'fastcgi_finish_request' or when application has finished and +; shutdown functions are being called (registered via register_shutdown_function). +; This option will enable timeout limit to be applied unconditionally +; even in such cases. +; Default Value: no +;request_terminate_timeout_track_finished = no + +; Set open file descriptor rlimit. +; Default Value: system defined value +;rlimit_files = 1024 + +; Set max core size rlimit. +; Possible Values: 'unlimited' or an integer greater or equal to 0 +; Default Value: system defined value +;rlimit_core = 0 + +; Chroot to this directory at the start. This value must be defined as an +; absolute path. When this value is not set, chroot is not used. +; Note: you can prefix with '$prefix' to chroot to the pool prefix or one +; of its subdirectories. If the pool prefix is not set, the global prefix +; will be used instead. +; Note: chrooting is a great security feature and should be used whenever +; possible. However, all PHP paths will be relative to the chroot +; (error_log, sessions.save_path, ...). +; Default Value: not set +;chroot = + +; Chdir to this directory at the start. +; Note: relative path can be used. +; Default Value: current directory or / when chroot +;chdir = /var/www + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environment, this can cause some delay in the page +; process time (several ms). +; Default Value: no +;catch_workers_output = yes + +; Decorate worker output with prefix and suffix containing information about +; the child that writes to the log and if stdout or stderr is used as well as +; log level and time. This options is used only if catch_workers_output is yes. +; Settings to "no" will output data as written to the stdout or stderr. +; Default value: yes +;decorate_workers_output = no + +; Clear environment in FPM workers +; Prevents arbitrary environment variables from reaching FPM worker processes +; by clearing the environment in workers before env vars specified in this +; pool configuration are added. +; Setting to "no" will make all environment variables available to PHP code +; via getenv(), $_ENV and $_SERVER. +; Default Value: yes +;clear_env = no + +; Limits the extensions of the main script FPM will allow to parse. This can +; prevent configuration mistakes on the web server side. You should only limit +; FPM to .php extensions to prevent malicious users to use other extensions to +; execute php code. +; Note: set an empty value to allow all extensions. +; Default Value: .php +;security.limit_extensions = .php .php3 .php4 .php5 .php7 + +; Pass environment variables like LD_LIBRARY_PATH. All $VARIABLEs are taken from +; the current environment. +; Default Value: clean env +;env[HOSTNAME] = $HOSTNAME +;env[PATH] = /usr/local/bin:/usr/bin:/bin +;env[TMP] = /tmp +;env[TMPDIR] = /tmp +;env[TEMP] = /tmp + +; Additional php.ini defines, specific to this pool of workers. These settings +; overwrite the values previously defined in the php.ini. The directives are the +; same as the PHP SAPI: +; php_value/php_flag - you can set classic ini defines which can +; be overwritten from PHP call 'ini_set'. +; php_admin_value/php_admin_flag - these directives won't be overwritten by +; PHP call 'ini_set' +; For php_*flag, valid values are on, off, 1, 0, true, false, yes or no. + +; Defining 'extension' will load the corresponding shared extension from +; extension_dir. Defining 'disable_functions' or 'disable_classes' will not +; overwrite previously defined php.ini values, but will append the new value +; instead. + +; Note: path INI options can be relative and will be expanded with the prefix +; (pool, global or /usr/local) + +; Default Value: nothing is defined by default except the values in php.ini and +; specified at startup with the -d argument +;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com +;php_flag[display_errors] = off +;php_admin_value[error_log] = /var/log/fpm-php.www.log +;php_admin_flag[log_errors] = on +;php_admin_value[memory_limit] = 32M diff --git a/src/typo3/.devcontainer/docker/postStart.sh b/src/typo3/.devcontainer/docker/postStart.sh new file mode 100644 index 0000000..bf4629e --- /dev/null +++ b/src/typo3/.devcontainer/docker/postStart.sh @@ -0,0 +1,36 @@ +#!/bin/bash +set -eu + +# post start script +echo "BEGIN: poststart.sh" + +echo "postStart: Checking if site already has been initialized" +if test ! -d .build/vendor; then + echo "postStart: Initialize TYPO3" + composer install + # Add .build/bin to PATH + [[ ":$PATH:" != *":${WORKSPACE_ROOT}/.build/bin:"* ]] && export PATH="${WORKSPACE_ROOT}/.build/bin:${PATH}" +fi + +typo3_major_version=$(typo3 -V|grep "TYPO3 CMS"|perl -n -e '/\ ([\d]+)/ && print $1') +echo "postStart: Running on TYPO3 major version ${typo3_major_version}" + +echo "postStart: DB compare" +typo3 database:updateschema --no-interaction --no-ansi || true + +echo "postStart: Update reference index" +typo3 referenceindex:update --no-interaction --no-ansi || true + +#echo "postStart: Create new backend user" +#$typo3_cli backend:createadmin ${TYPO3_INSTALL_ADMIN_USER} ${TYPO3_INSTALL_ADMIN_PASSWORD} --no-interaction --no-ansi || true + +# take care of file ownership esp. to support cross container functionality +echo "postStart: Update file and directory ownership" +chown -R ${DEVCONTAINER_SERVICE_NAME}:${DEVCONTAINER_SERVICE_NAME} config .build var +chgrp ${DEVCONTAINER_SERVICE_NAME} ${WORKSPACE_ROOT} + +# needed as directory check moans about wrong permissions +echo "postStart: Update file permissions" +chmod -R 2770 .build config var + +echo "END: poststart.sh" diff --git a/src/typo3/.devcontainer/docker/typo3/additional.php b/src/typo3/.devcontainer/docker/typo3/additional.php new file mode 100644 index 0000000..005b954 --- /dev/null +++ b/src/typo3/.devcontainer/docker/typo3/additional.php @@ -0,0 +1,17 @@ +isDevelopment()) { + $GLOBALS['TYPO3_CONF_VARS'] = array_replace_recursive( + $GLOBALS['TYPO3_CONF_VARS'], + [ + 'SYS' => [ + 'exceptionalErrors' => getenv('TYPO3_CONFIG_SYS_EXCEPTIONALERRORS'), + 'systemLocale' => 'en_US' + ], + ] + ); +} diff --git a/src/typo3/.devcontainer/docker/typo3/php.ini b/src/typo3/.devcontainer/docker/typo3/php.ini new file mode 100644 index 0000000..6cf111d --- /dev/null +++ b/src/typo3/.devcontainer/docker/typo3/php.ini @@ -0,0 +1,21 @@ +upload_max_filesize=24M +post_max_size=24M +always_populate_raw_post_data=1 +max_execution_time=240 +max_input_vars=4500 +memory_limit=256M +pcre.jit = 1 +#extension=gd.so +display_errors = 1 +error_reporting=E_ALL +log_errors = 1 +error_log = /workspaces/TYPO3.introduction/var/log/php_error.log + +[xdebug] +#zend_extension=/usr/local/lib/php/extensions/no-debug-non-zts-20230831/xdebug.so +xdebug.mode=develop,debug +xdebug.client_host=host.docker.internal +xdebug.start_with_request=yes +xdebug.log = /workspaces/TYPO3.introduction/var/log/xdebug.log +xdebug.client_port=9003 +xdebug.idekey=vscode diff --git a/src/typo3/.devcontainer/docker/typo3/typo3.caddyfile b/src/typo3/.devcontainer/docker/typo3/typo3.caddyfile new file mode 100644 index 0000000..03f9bb5 --- /dev/null +++ b/src/typo3/.devcontainer/docker/typo3/typo3.caddyfile @@ -0,0 +1,79 @@ +{ + debug + admin off + auto_https off + + # https://caddyserver.com/docs/caddyfile/directives#sorting-algorithm + order php_server before file_server + order php before file_server +} + +127.0.0.1:80, localhost:80 { + root * {$WORKSPACE_ROOT}/.build/public + + php_server { + root {$WORKSPACE_ROOT}/.build/public + } + + @canonicalPath { + file {path}/index.php + not path */ + } + redir @canonicalPath {path}/ 308 + + # TYPO3 paths for content security policy + @fileadmin { + path /fileadmin/* + } + header @fileadmin ?Content-Security-Policy "default-src 'self'; script-src 'none'; style-src 'none'; object-src 'none'; require-trusted-types-for 'script';" + + # TYPO3 Frontend + @frontend { + not path /typo3/* + file { + try_files {path} {path}/index.php /index.php + split_path .php + } + } + rewrite @frontend {http.matchers.file.relative} + request_header @frontend +TYPO3_FRANKENPHP_MODE frontend + + # TYPO3 Backend - if not set, backend routing will not work! + @backend { + path /typo3/* + not path /typo3/install.php + file { + try_files {path} {path}/index.php /typo3/index.php /index.php + split_path .php + } + } + rewrite @backend {http.matchers.file.relative} + request_header @backend +TYPO3_FRANKENPHP_MODE backend + + encode zstd gzip + + handle @backend { + php_server + # #redir @backend /phpinfo.php + } + + handle @frontend { + php_server { + #worker index.php 1 + } + } + + log { + level debug + # Redact the authorization query parameter that can be set by Mercure + format filter { + wrap console + fields { + uri query { + replace authorization REDACTED + } + } + } + } + log_append HTTP_TYPO3_FRANKENPHP_MODE {http.request.header.TYPO3_FRANKENPHP_MODE} +} diff --git a/src/typo3/.devcontainer/onCreateCommandScript.sh b/src/typo3/.devcontainer/onCreateCommandScript.sh new file mode 100644 index 0000000..bccc38d --- /dev/null +++ b/src/typo3/.devcontainer/onCreateCommandScript.sh @@ -0,0 +1,19 @@ +#!/bin/bash +set -eu + +echo "BEGIN: onCreateCommandScript.sh" + +# initialize TYPO3.env if not exists +echo "onCreateCommandScript: Initializing TYPO3.env if not exists" +[ ! -f ${WORKSPACE_ROOT}/TYPO3.env ] && cp ${WORKSPACE_ROOT}/.devcontainer/docker/TYPO3.env.tmpl ${WORKSPACE_ROOT}/TYPO3.env + +# configure git safe directories +echo "onCreateCommandScript: Configuring git safe directories" +git config --global --add safe.directory /var/www +git config --global --add safe.directory ${WORKSPACE_ROOT} + +# make scripts executable +echo "onCreateCommandScript: Making scripts executable" +find ${WORKSPACE_ROOT}/.devcontainer -name *.sh -type f -exec chmod -c +x {} \; + +echo "END: onCreateCommandScript.sh" diff --git a/src/typo3/.devcontainer/postCreateCommandScript.sh b/src/typo3/.devcontainer/postCreateCommandScript.sh new file mode 100644 index 0000000..def4bc0 --- /dev/null +++ b/src/typo3/.devcontainer/postCreateCommandScript.sh @@ -0,0 +1,28 @@ +#!/bin/bash +set -eu + +# post start script +echo "BEGIN: postCreateCommandScript.sh" + +#echo "postCreateCommandScript: Fetching IP address of docker0 gateway" +#export DOCKER0_GATEWAY=$(docker network inspect -f json bridge|jq ".[] | select( .Name == \"bridge\").IPAM.Config[0].Gateway"| tr -d '"') +#echo "postCreateCommandScript: => DOCKER0_GATEWAY=${DOCKER0_GATEWAY}" + +# check if docker containers are already running only if docker cli is installed +if [[ `which docker` ]]; then + echo "postCreateCommandScript: Checking if docker containers are already running" + if [ `docker compose ls -q --filter "name=^${COMPOSE_PROJECT_NAME}$" | wc -l` -eq 0 ]; then + compose_filename="${WORKSPACE_ROOT}/.devcontainer/docker/docker-compose.backend.yaml" + # start docker containers for initialization + echo "postCreateCommandScript: Starting docker containers for ${COMPOSE_PROJECT_NAME}" + docker compose -f ${compose_filename} up -d --wait + else + echo "Docker containers for ${COMPOSE_PROJECT_NAME} are already running." + fi +fi + +# ignite TYPO3 environment for the first time +echo "postCreateCommandScript: Ignite TYPO3 environment for the first time" +${WORKSPACE_ROOT}/.devcontainer/docker/igniteEnvironment.sh + +echo "END: postCreateCommandScript.sh" diff --git a/src/hello/README.md b/src/typo3/README.md similarity index 100% rename from src/hello/README.md rename to src/typo3/README.md diff --git a/src/typo3/devcontainer-template.json b/src/typo3/devcontainer-template.json new file mode 100644 index 0000000..b5c68b4 --- /dev/null +++ b/src/typo3/devcontainer-template.json @@ -0,0 +1,32 @@ +{ + "id": "typo3", + "version": "1.0.0", + "name": "Typo3 ", + "description": "TYPO3 development container template", + "documentationURL": "https://github.com/thucke/devcontainer-templates/tree/main/src/typo3", + "licenseURL": "https://github.com/thucke/devcontainer-templates/blob/main/LICENSE", + "options": { + "phpVersion": { + "type": "string", + "description": "PHP version to use:", + "proposals": [ + "8.2", + "8.3", + "8.4", + "8.5" + ], + "default": "8.4" + }, + "webserver": { + "type": "string", + "description": "Which webserver do you prefer?", + "proposals": [ + "frankenphp" + ], + "default": "frankenphp" + } + }, + "platforms": [ + "Any" + ] +} From 285e2e10aee75dbd8c8263d794ae42f155faf735 Mon Sep 17 00:00:00 2001 From: Thomas Hucke Date: Tue, 3 Mar 2026 18:31:28 +0100 Subject: [PATCH 2/5] chore: added file --- .../.devcontainer/docker/frankenphp/server.sh | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/typo3/.devcontainer/docker/frankenphp/server.sh diff --git a/src/typo3/.devcontainer/docker/frankenphp/server.sh b/src/typo3/.devcontainer/docker/frankenphp/server.sh new file mode 100644 index 0000000..e3cceb1 --- /dev/null +++ b/src/typo3/.devcontainer/docker/frankenphp/server.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +echo "BEGIN: server.sh (frankenphp)" + +if [[ ! ${COMPOSE_PROFILES} =~ "frankenphp" ]]; then + echo "server.sh: No frankenphp environment" + exit 0 +fi + +if [[ "$1" == "restart" ]]; then + echo "server.sh: Checking for running frankenphp" + if [ $(pidof frankenphp| wc -w) -ne 0 ]; then + echo "restartFrankenphp: Stopping running frankenphp" + pidof frankenphp | xargs kill -9 + sleep 1 + fi +fi + +if [ $(pidof frankenphp| wc -w) -eq 0 ]; then + echo "server.sh: Starting frankenphp in daemon mode" + nohup frankenphp run --config ${WORKSPACE_ROOT}/.devcontainer/docker/typo3/typo3.caddyfile >/dev/null 2>&1 & + #nohup frankenphp run --config ${WORKSPACE_ROOT}/.devcontainer/docker/typo3/typo3.caddyfile >>${WORKSPACE_ROOT}/frankenphp.log 2>&1 & + echo "Devcontainer: frankenphp server started (PID: $!))" +else + echo "Devcontainer: frankenphp server already running (PID: $(pidof frankenphp))" +fi + +echo "END: server.sh (frankenphp)" +exit 0 From 46a1cb7984368708b6e2ad97daf2eae27484f0de Mon Sep 17 00:00:00 2001 From: Thomas HUCKE Date: Wed, 11 Mar 2026 18:39:08 +0100 Subject: [PATCH 3/5] feat: added typo3 template --- README.md | 146 +++++++----------- .../doc/Devcontainer_Apache.drawio.png | Bin 0 -> 72486 bytes .../doc/Devcontainer_FrankenPHP.drawio.png | Bin 0 -> 72223 bytes .../doc/Devcontainer_PHP-FPM.drawio.png | Bin 0 -> 83983 bytes .../Github_VsCodeBuildingCodespace.drawio.png | Bin 0 -> 4319 bytes .../docker/docker-compose.backend.yaml | 2 +- .../.devcontainer/docker/igniteEnvironment.sh | 57 ++++--- .../{postStart.sh => initializeTYPO3.sh} | 20 +-- .../.devcontainer/onCreateCommandScript.sh | 1 - .../.devcontainer/postCreateCommandScript.sh | 4 - src/typo3/NOTES.md | 5 + src/typo3/README.md | 14 +- src/typo3/devcontainer-template.json | 2 +- test/{hello => typo3}/test.sh | 3 +- 14 files changed, 110 insertions(+), 144 deletions(-) create mode 100644 src/typo3/.devcontainer/doc/Devcontainer_Apache.drawio.png create mode 100644 src/typo3/.devcontainer/doc/Devcontainer_FrankenPHP.drawio.png create mode 100644 src/typo3/.devcontainer/doc/Devcontainer_PHP-FPM.drawio.png create mode 100644 src/typo3/.devcontainer/doc/Github_VsCodeBuildingCodespace.drawio.png rename src/typo3/.devcontainer/docker/{postStart.sh => initializeTYPO3.sh} (64%) create mode 100644 src/typo3/NOTES.md rename test/{hello => typo3}/test.sh (54%) diff --git a/README.md b/README.md index dacf5d7..6330628 100644 --- a/README.md +++ b/README.md @@ -1,122 +1,80 @@ -# Dev Container Templates: Self Authoring Guide +# Development Container Templates -> This repo provides a starting point and example for creating your own custom [Dev Container Templates](https://containers.dev/implementors/templates), hosted for free on GitHub Container Registry. The example in this repository follows the [Dev Container Template distribution specification](https://containers.dev/implementors/templates-distribution/). -> -> To provide feedback on the distribution spec, please leave a comment [on spec issue #71](https://github.com/devcontainers/spec/issues/71). + + + +
devcontainers organization logo +Development Container Templates
+A simple set of dev container 'templates' to help get you up and running with a containerized environment. +
-## Repo and Template Structure -This repository contains a _collection_ of two Templates - `hello` and `color`. These Templates serve as simple template implementations which helps containerize the project. Similar to the [`devcontainers/templates`](https://github.com/devcontainers/templates) repo, this repository has a `src` folder. Each Template has its own sub-folder, containing at least a `devcontainer-template.json` and `.devcontainer/devcontainer.json`. +A **development container** is a running [Docker](https://www.docker.com) container with a well-defined tool/runtime stack and its prerequisites. It allows you to use a container as a full-featured development environment which can be used to run an application, to separate tools, libraries, or runtimes needed for working with a codebase, and to aid in continuous integration and testing. -``` -├── src -│ ├── color -│ │ ├── devcontainer-template.json -│ │ └──| .devcontainer -│ │ └── devcontainer.json -│ ├── hello -│ │ ├── devcontainer-template.json -│ │ └──| .devcontainer -│ │ ├── devcontainer.json -│ │ └── Dockerfile -| ├── ... -│ │ ├── devcontainer-template.json -│ │ └──| .devcontainer -│ │ └── devcontainer.json -├── test -│ ├── color -│ │ └── test.sh -│ ├── hello -│ │ └── test.sh -│ └──test-utils -│ └── test-utils.sh -... -``` - -### Options - -All available options for a Template should be declared in the `devcontainer-template.json`. The syntax for the `options` property can be found in the [devcontainer Template json properties reference](https://containers.dev/implementors/templates#devcontainer-templatejson-properties). - -For example, the `color` Template provides three possible options (`red`, `gold`, `green`), where the default value is set to "red". - -```jsonc -{ - // ... - "options": { - "favorite": { - "type": "string", - "description": "Choose your favorite color.", - "proposals": [ - "red", - "gold", - "green" - ], - "default": "red" - } - } -} -``` - -An [implementing tool](https://containers.dev/supporting#tools) will use the `options` property from [the documented Dev Container Template properties](https://containers.dev/implementors/templates#devcontainer-templatejson-properties) for customizing the Template. See [option resolution example](https://containers.dev/implementors/templates#option-resolution-example) for details. - -## Distributing Templates +This repository contains a set of **Dev Container Templates** which are source files packaged together that encode configuration for a complete development environment. A Template can be used in a new or existing project, and a [supporting tool](https://containers.dev/supporting) will use the configuration from the template to build a development container. -**Note**: *Allow GitHub Actions to create and approve pull requests* should be enabled in the repository's `Settings > Actions > General > Workflow permissions` for auto generation of `src/