From 53dc956c12f8d7a2e4ac8081697574fc2a303ab8 Mon Sep 17 00:00:00 2001
From: "Hahn Axel (hahn)" <axel.hahn@unibe.ch>
Date: Fri, 15 Sep 2023 14:24:29 +0200
Subject: [PATCH] add docker env

---
 docker/.env                                   |  16 +
 docker/containers/db-server/mariadb/my.cnf    |   3 +
 docker/containers/web-server/Dockerfile       |  14 +
 .../apache/sites-enabled/vhost_app.conf       |  30 ++
 .../web-server/php/extra-php-config.ini       |  24 ++
 docker/docker-compose.yml                     |  44 +++
 docker/init.sh                                | 340 ++++++++++++++++++
 docker/init.sh.cfg                            |  68 ++++
 docker/templates/docker-compose.yml           |  71 ++++
 docker/templates/dot_env                      |  28 ++
 docker/templates/extra-php-config.ini         |  25 ++
 docker/templates/my.cnf                       |   4 +
 docker/templates/readme.md                    |   7 +
 docker/templates/vhost_app.conf               |  34 ++
 docker/templates/web-server-Dockerfile        |  15 +
 15 files changed, 723 insertions(+)
 create mode 100644 docker/.env
 create mode 100644 docker/containers/db-server/mariadb/my.cnf
 create mode 100644 docker/containers/web-server/Dockerfile
 create mode 100644 docker/containers/web-server/apache/sites-enabled/vhost_app.conf
 create mode 100644 docker/containers/web-server/php/extra-php-config.ini
 create mode 100644 docker/docker-compose.yml
 create mode 100755 docker/init.sh
 create mode 100644 docker/init.sh.cfg
 create mode 100644 docker/templates/docker-compose.yml
 create mode 100644 docker/templates/dot_env
 create mode 100644 docker/templates/extra-php-config.ini
 create mode 100644 docker/templates/my.cnf
 create mode 100644 docker/templates/readme.md
 create mode 100644 docker/templates/vhost_app.conf
 create mode 100644 docker/templates/web-server-Dockerfile

diff --git a/docker/.env b/docker/.env
new file mode 100644
index 0000000..95e9205
--- /dev/null
+++ b/docker/.env
@@ -0,0 +1,16 @@
+# ======================================================================
+#
+# GENERATED BY init.sh - template: ./templates/dot_env - e2cde05722688ff85d3a93e9cd55787e
+# values to be used in docker-composer.yml
+#
+# ======================================================================
+
+# ----- application
+APP_NAME=ci-pkg
+
+# uid of www-data in the docker container
+DOCKER_USER_UID=33
+
+APP_PORT=8001
+WEBROOT=/var/www/ci-pkg/public_html
+
diff --git a/docker/containers/db-server/mariadb/my.cnf b/docker/containers/db-server/mariadb/my.cnf
new file mode 100644
index 0000000..cc3b80d
--- /dev/null
+++ b/docker/containers/db-server/mariadb/my.cnf
@@ -0,0 +1,3 @@
+[mysqld]
+; collation-server = utf8mb4_unicode_ci
+; character-set-server = utf8mb4
\ No newline at end of file
diff --git a/docker/containers/web-server/Dockerfile b/docker/containers/web-server/Dockerfile
new file mode 100644
index 0000000..6b7b12c
--- /dev/null
+++ b/docker/containers/web-server/Dockerfile
@@ -0,0 +1,14 @@
+#
+# GENERATED BY init.sh - template: ./templates/web-server-Dockerfile - 42dce773c83597a7d05af398bdd66d15
+#
+FROM php:8.2-apache
+
+# install packages
+RUN apt-get update && apt-get install -y git unzip zip libapache2-mod-xsendfile
+
+# enable apache modules
+RUN a2enmod 
+
+# install php packages
+COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
+RUN install-php-extensions 
diff --git a/docker/containers/web-server/apache/sites-enabled/vhost_app.conf b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf
new file mode 100644
index 0000000..34b4d1e
--- /dev/null
+++ b/docker/containers/web-server/apache/sites-enabled/vhost_app.conf
@@ -0,0 +1,30 @@
+#
+# GENERATED BY init.sh - template: ./templates/vhost_app.conf - 5809b7f8d1f4882c4f3569c9ea042045
+#
+<VirtualHost *:80>
+  DocumentRoot /var/www/ci-pkg/public_html
+  <Directory /var/www/ci-pkg/public_html>
+      AllowOverride None
+      Order Allow,Deny
+      Allow from All
+  </Directory>
+
+  # redirect requests to handle packages
+  <Location "/packages">
+
+    RewriteEngine on
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^(.*)$ index.php [QSA,L]
+
+  </Location>
+
+  # download files are outside webroot
+  XSendFile On
+  XSendFilePath "/var/www/${APP_NAME}/example-packages/"
+
+  # example to prevent access with http
+  <Location "/no-access">
+    Require all denied
+  </Location>
+
+</VirtualHost>
\ No newline at end of file
diff --git a/docker/containers/web-server/php/extra-php-config.ini b/docker/containers/web-server/php/extra-php-config.ini
new file mode 100644
index 0000000..aa13bd7
--- /dev/null
+++ b/docker/containers/web-server/php/extra-php-config.ini
@@ -0,0 +1,24 @@
+;
+; GENERATED BY init.sh - template: ./templates/extra-php-config.ini - 9dce36d285d5b21d70e015c074c196c2
+;
+[PHP]
+
+error_reporting=E_ALL
+display_errors=1
+
+; ----------------------------------------------------------------------
+; XDEBUG STUFF BELOW
+; ----------------------------------------------------------------------
+; 
+; error_reporting=E_ALL
+; 
+; [xdebug]
+; xdebug.mode=develop,debug
+; ; xdebug.client_host=localhost
+; xdebug.start_with_request=yes
+; ; xdebug.start_with_request=trigger
+; 
+; xdebug.log=/tmp/xdebug.log
+; xdebug.discover_client_host = 1
+; ; xdebug.client_port=9003
+; xdebug.idekey="netbeans-xdebug"
\ No newline at end of file
diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml
new file mode 100644
index 0000000..39e932e
--- /dev/null
+++ b/docker/docker-compose.yml
@@ -0,0 +1,44 @@
+#
+# GENERATED BY init.sh - template: ./templates/docker-compose.yml - fc2f1d55926abdb9c54f65afd0571d7b
+#
+# ======================================================================
+#
+# (1) see .env for set variables
+# (2) run "docker-compose up" to startup
+# 
+# ======================================================================
+version: '3.9'
+
+networks:
+  ci-pkg-network:
+
+services:
+
+  # ----- apache httpd + php
+  ci-pkg-web-server:
+    build:
+      context: .
+      dockerfile: ./containers/web-server/Dockerfile
+    image: "php:8.2-apache"
+    container_name: 'ci-pkg-server'
+    ports:
+      - '${APP_PORT}:80'
+
+    working_dir: ${WEBROOT}
+    
+    volumes:
+      - ../:/var/www/${APP_NAME}
+      - ./containers/web-server/apache/sites-enabled:/etc/apache2/sites-enabled
+      - ./containers/web-server/php/extra-php-config.ini:/usr/local/etc/php/conf.d/extra-php-config.ini
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost"]
+      interval: 10s
+      timeout: 3s
+      retries: 5
+      # start_period: 40s
+
+    networks:
+      - ci-pkg-network
+
+    user: ${DOCKER_USER_UID}
+
diff --git a/docker/init.sh b/docker/init.sh
new file mode 100755
index 0000000..060db24
--- /dev/null
+++ b/docker/init.sh
@@ -0,0 +1,340 @@
+#!/bin/bash
+# ======================================================================
+#
+# DOCKER PHP DEV ENVIRONMENT :: INIT
+#
+# ----------------------------------------------------------------------
+# 2021-11-nn  v1.0 <axel.hahn@iml.unibe.ch>
+# 2022-07-19  v1.1 <axel.hahn@iml.unibe.ch>  support multiple dirs for setfacl
+# 2022-11-16  v1.2 <www.axel-hahn.de>        use docker-compose -p "$APP_NAME"
+# 2022-12-18  v1.3 <www.axel-hahn.de>        add -p "$APP_NAME" in other docker commands
+# 2022-12-20  v1.4 <axel.hahn@unibe.ch>      replace fgrep with grep -F
+# 2023-03-06  v1.5 <www.axel-hahn.de>        up with and without --build
+# 2023-08-17  v1.6 <www.axel-hahn.de>        menu selection with single key (without return)
+# ======================================================================
+
+cd $( dirname $0 )
+. $( basename $0 ).cfg
+
+# git@git-repo.iml.unibe.ch:iml-open-source/docker-php-starterkit.git
+selfgitrepo="docker-php-starterkit.git"
+
+_version="1.6"
+
+# ----------------------------------------------------------------------
+# FUNCTIONS
+# ----------------------------------------------------------------------
+
+# draw a headline 2
+function h2(){
+    echo
+    echo -e "\e[33m>>>>> $*\e[0m"
+}
+
+# draw a headline 3
+function h3(){
+    echo
+    echo -e "\e[34m----- $*\e[0m"
+}
+
+# function _gitinstall(){
+#     h2 "install/ update app from git repo ${gitrepo} in ${gittarget} ..."
+#     test -d ${gittarget} && ( cd ${gittarget}  && git pull )
+#     test -d ${gittarget} || git clone -b ${gitbranch} ${gitrepo} ${gittarget} 
+# }
+
+# set acl on local directory
+function _setWritepermissions(){
+    h2 "set write permissions on ${gittarget} ..."
+
+    local _user=$( id -gn )
+    typeset -i local _user_uid=0
+    test -f /etc/subuid && _user_uid=$( grep $_user /etc/subuid 2>/dev/null | cut -f 2 -d ':' )-1
+    typeset -i local DOCKER_USER_OUTSIDE=$_user_uid+$DOCKER_USER_UID
+
+    set -vx
+
+    for mywritedir in ${WRITABLEDIR}
+    do 
+
+        echo "--- ${mywritedir}"
+        # remove current acl
+        sudo setfacl -bR "${mywritedir}"
+
+        # default permissions: both the host user and the user with UID 33 (www-data on many systems) are owners with rwx perms
+        sudo setfacl -dRm u:${DOCKER_USER_OUTSIDE}:rwx,${_user}:rwx "${mywritedir}"
+
+        # permissions: make both the host user and the user with UID 33 owner with rwx perms for all existing files/directories
+        sudo setfacl -Rm u:${DOCKER_USER_OUTSIDE}:rwx,${_user}:rwx "${mywritedir}"
+    done
+
+    set +vx
+}
+
+# cleanup starterkit git data
+function _removeGitdata(){
+    h2 "Remove git data of starterkit"
+    echo -n "Current git remote url: "
+    git config --get remote.origin.url
+    git config --get remote.origin.url 2>/dev/null | grep $selfgitrepo >/dev/null
+    if [ $? -eq 0 ]; then
+        echo
+        echo -n "Delete local .git and .gitignore? [y/N] > "
+        read answer
+        test "$answer" = "y" && ( echo "Deleting ... " && rm -rf ../.git ../.gitignore )
+    else
+        echo "It was done already - $selfgitrepo was not found."
+    fi
+
+}
+
+# helper function: cut a text file starting from database start marker
+# see _generateFiles()
+function _fix_no-db(){
+    local _file=$1
+    if [ $DB_ADD = false ]; then
+        typeset -i local iStart=$( cat ${_file} | grep -Fn "$CUTTER_NO_DATABASE" | cut -f 1 -d ':' )-1
+        if [ $iStart -gt 0 ]; then
+            sed -ni "1,${iStart}p" ${_file}
+        fi
+    fi
+}
+
+# loop over all files in templates subdir make replacements and generate
+# a target file.
+# It skips if 
+#   - 1st line is not starting with "# TARGET: filename"
+#   - target file has no updated lines
+function _generateFiles(){
+
+    # re-read config vars
+    . $( basename $0 ).cfg
+
+    local _tmpfile=/tmp/newfilecontent$$.tmp
+    h2 "generate files from templates..."
+    for mytpl in $( ls -1 ./templates/* )
+    do
+        # h3 $mytpl
+        local _doReplace=1
+
+        # fetch traget file from first line
+        target=$( head -1 $mytpl | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' )
+
+        if [ -z "$target" ]; then
+            echo SKIP: $mytpl - target was not found in 1st line
+            _doReplace=0
+        fi
+
+        # write generated files to target
+        if [ $_doReplace -eq 1 ]; then
+
+            # write file from line 2 to a tmp file
+            sed -n '2,$p' $mytpl >$_tmpfile
+
+            # add generator
+            # sed -i "s#{{generator}}#generated by $0 - template: $mytpl - $( date )#g" $_tmpfile
+            local _md5=$( md5sum $_tmpfile | awk '{ print $1 }' )
+            sed -i "s#{{generator}}#GENERATED BY $( basename $0 ) - template: $mytpl - $_md5#g" $_tmpfile
+
+            # loop over vars to make the replacement
+            grep "^[a-zA-Z]" $( basename $0 ).cfg | while read line
+            do
+                # echo replacement: $line
+                mykey=$( echo $line | cut -f 1 -d '=' )
+                myvalue="$( eval echo \"\${$mykey}\" )"
+                # grep "{{$mykey}}" $_tmpfile
+
+                # TODO: multiline values fail here in replacement with sed 
+                sed -i "s#{{$mykey}}#${myvalue}#g" $_tmpfile
+            done
+            _fix_no-db $_tmpfile
+
+            # echo "changes for $target:"
+            diff  "../$target"  "$_tmpfile" | grep -v "$_md5" | grep -v "^---" | grep .
+            if [ $? -eq 0 -o ! -f "../$target" ]; then
+                echo -n "$mytpl - changes detected - writing [$target] ... "
+                mkdir -p $( dirname  "../$target" ) || exit 2
+                mv "$_tmpfile" "../$target" || exit 2
+                echo OK
+            else
+                rm -f $_tmpfile
+                echo "SKIP: $mytpl - Nothing to do."
+            fi
+        fi
+        echo
+    done
+
+}
+
+# loop over all files in templates subdir make replacements and generate
+# a traget file.
+function _removeGeneratedFiles(){
+    h2 "remove generated files..."
+    for mytpl in $( ls -1 ./templates/* )
+    do
+        h3 $mytpl
+
+        # fetch traget file from first line
+        target=$( head -1 $mytpl | grep "^# TARGET:" | cut -f 2- -d ":" | awk '{ print $1 }' )
+
+        if [ ! -z "$target" -a -f "../$target" ]; then
+            echo -n "REMOVING "
+            ls -l "../$target" || exit 2
+            rm -f "../$target" || exit 2
+            echo OK
+        else
+            echo SKIP: $target
+        fi
+        
+    done
+}
+
+function _showContainers(){
+    local bLong=$1
+    h2 CONTAINERS
+    if [ -z "$bLong" ]; then
+        docker-compose -p "$APP_NAME" ps
+    else
+        docker ps | grep $APP_NAME
+    fi
+}
+
+
+# a bit stupid ... i think I need to delete it.
+function _showInfos(){
+    _showContainers long
+    h2 INFO
+
+    h3 "processes"
+    docker-compose top
+
+    h3 "Check app port"
+    >/dev/tcp/localhost/${APP_PORT} 2>/dev/null && (
+        echo "OK, app port ${APP_PORT} is reachable"
+        echo
+        echo "In a web browser open:"
+        echo "  $frontendurl"
+    )
+    h3 "Check database port"
+    >/dev/tcp/localhost/${DB_PORT} 2>/dev/null && (
+        echo "OK, db port ${DB_PORT} is reachable"
+        echo
+        echo "In a local DB admin tool:"
+        echo "  host    : localhost"
+        echo "  port    : ${DB_PORT}"
+        echo "  user    : root"
+        echo "  password: ${MYSQL_ROOT_PASS}"
+    )
+    echo
+}
+
+# helper for menu: print an inverted key
+function  _key(){
+    printf "\e[4;7m ${1} \e[0m"
+}
+
+# helper: wait for a return key
+function _wait(){
+    echo -n "... press RETURN > "; read -r
+}
+
+# ----------------------------------------------------------------------
+# MAIN
+# ----------------------------------------------------------------------
+
+action=$1
+
+while true; do
+    echo
+    echo -e "\e[32m===== INITIALIZER FOR DOCKER APP [$APP_NAME] v$_version ===== \e[0m\n\r"
+
+    if [ -z "$action" ]; then
+
+        _showContainers
+
+        h2 MENU
+        echo "  $( _key g ) - remove git data of starterkit"
+        echo
+        echo "  $( _key i ) - init application: set permissions"
+        echo "  $( _key t ) - generate files from templates"
+        echo "  $( _key T ) - remove generated files"
+        echo
+        echo "  $( _key u ) - startup containers    docker-compose ... up -d"
+        echo "  $( _key U ) - startup containers    docker-compose ... up -d --build"
+        echo "  $( _key s ) - shutdown containers   docker-compose stop"
+        echo "  $( _key r ) - remove containers     docker-compose rm -f"
+        echo
+        echo "  $( _key m ) - more infos"
+        echo "  $( _key c ) - console (bash)"
+        echo
+        echo "  $( _key q ) - quit"
+        echo
+        echo -n "  select >"
+        read -rn 1 action 
+        echo
+    fi
+
+    case "$action" in
+        g)
+            _removeGitdata
+            ;;
+        i)
+            # _gitinstall
+            _setWritepermissions
+            ;;
+        t)
+            _generateFiles
+            ;;
+        T)
+            _removeGeneratedFiles
+            rm -rf containers
+            ;;
+        # not in the menu
+        # f)
+        #     _removeGeneratedFiles
+        #     _generateFiles
+        #     _wait
+        #     ;;
+        m)
+            _showInfos
+            _wait
+            ;;
+        u|U)
+            dockerUp="docker-compose -p "$APP_NAME" --verbose up -d --remove-orphans"
+            if [ "$action" = "U" ]; then
+                dockerUp+=" --build"
+            fi
+            if $dockerUp; then
+                echo "In a web browser:"
+                echo "  $frontendurl"
+            else
+                echo "ERROR: docker-compose up failed :-/"
+                docker-compose -p "$APP_NAME" logs | tail
+            fi
+            echo
+
+            _wait
+            ;;
+        s)
+            docker-compose -p "$APP_NAME" stop
+            ;;
+        r)
+            docker-compose -p "$APP_NAME" rm -f
+            ;;
+        c)
+            docker ps
+            echo -n "id or name >"
+            read dockerid
+            test -z "$dockerid" || docker exec -it $dockerid /bin/bash
+            ;;
+        q)
+            exit 0;
+            ;;
+        *) 
+            test -n "$action" && ( echo "  ACTION FOR [$action] NOT IMPLEMENTED."; sleep 1 )
+    esac
+    action=
+done
+
+
+# ----------------------------------------------------------------------
diff --git a/docker/init.sh.cfg b/docker/init.sh.cfg
new file mode 100644
index 0000000..6202716
--- /dev/null
+++ b/docker/init.sh.cfg
@@ -0,0 +1,68 @@
+# ======================================================================
+#
+# settings for init.sh and base values for replacements in template files
+# This script is sourced by init.sh ... this file is bash syntax
+# 
+# ----------------------------------------------------------------------
+# 2021-12-17  <axel.hahn@iml.unibe.ch>
+# ======================================================================
+
+APP_NAME=ci-pkg
+
+# web port 80 in container is seen on localhost as ...
+APP_PORT=8001
+
+APP_APT_PACKAGES="git unzip zip libapache2-mod-xsendfile"
+
+#APP_APACHE_MODULES="rewrite"
+APP_APACHE_MODULES=""
+
+APP_PHP_VERSION=8.2
+# APP_PHP_MODULES="curl pdo_mysql mbstring xml zip xdebug"
+APP_PHP_MODULES=""
+
+# optional exec command after container was started with init.sh script
+# APP_ONSTARTUP="php /var/www/${APP_NAME}/public_html/myservice.php"
+APP_ONSTARTUP=""
+
+# ----------------------------------------------------------------------
+
+# add a container with database?
+DB_ADD=false
+
+# ----------------------------------------------------------------------
+# for an optional database server
+
+DB_PORT=13306
+
+# ----- database settings
+MYSQL_IMAGE=mariadb:10.5.9
+MYSQL_RANDOM_ROOT_PASSWORD=0
+MYSQL_ALLOW_EMPTY_PASSWORD=0
+MYSQL_ROOT_PASS=12345678
+MYSQL_USER=${APP_NAME}
+MYSQL_PASS=mypassword
+MYSQL_DB=${APP_NAME}
+
+
+
+# ======================================================================
+# ignore things below
+
+
+# where to set acl where local user and web user in container
+# can write simultanously
+WRITABLEDIR=../public_html
+
+
+# web service user in container
+DOCKER_USER_UID=33
+
+# document root inside web-server container 
+WEBROOT=/var/www/${APP_NAME}/public_html
+
+CUTTER_NO_DATABASE="CUT-HERE-FOR-NO-DATABASE"
+
+frontendurl=http://localhost:${APP_PORT}/
+
+# ----------------------------------------------------------------------
diff --git a/docker/templates/docker-compose.yml b/docker/templates/docker-compose.yml
new file mode 100644
index 0000000..3e039a7
--- /dev/null
+++ b/docker/templates/docker-compose.yml
@@ -0,0 +1,71 @@
+# TARGET: docker/docker-compose.yml
+#
+# {{generator}}
+#
+# ======================================================================
+#
+# (1) see .env for set variables
+# (2) run "docker-compose up" to startup
+# 
+# ======================================================================
+version: '3.9'
+
+networks:
+  {{APP_NAME}}-network:
+
+services:
+
+  # ----- apache httpd + php
+  {{APP_NAME}}-web-server:
+    build:
+      context: .
+      dockerfile: ./containers/web-server/Dockerfile
+    image: "php:{{APP_PHP_VERSION}}-apache"
+    container_name: '{{APP_NAME}}-server'
+    ports:
+      - '${APP_PORT}:80'
+
+    working_dir: ${WEBROOT}
+    
+    volumes:
+      - ../:/var/www/${APP_NAME}
+      - ./containers/web-server/apache/sites-enabled:/etc/apache2/sites-enabled
+      - ./containers/web-server/php/extra-php-config.ini:/usr/local/etc/php/conf.d/extra-php-config.ini
+    healthcheck:
+      test: ["CMD", "curl", "-f", "http://localhost"]
+      interval: 10s
+      timeout: 3s
+      retries: 5
+      # start_period: 40s
+
+    networks:
+      - {{APP_NAME}}-network
+
+    user: ${DOCKER_USER_UID}
+
+    # --- 8< --- {{CUTTER_NO_DATABASE}} --- 8< ---
+
+    depends_on:
+      - {{APP_NAME}}-db-server
+
+  # ----- mariadb
+  {{APP_NAME}}-db-server:
+    image: {{MYSQL_IMAGE}}
+    container_name: '${APP_NAME}-db'
+    # restart: always
+    ports:
+      - '${DB_PORT}:3306'
+    environment:
+      MYSQL_ROOT_PASSWORD: '${MYSQL_ROOT_PASS}'
+      MYSQL_USER: '${MYSQL_USER}'
+      MYSQL_PASSWORD: '${MYSQL_PASS}'
+      MYSQL_DATABASE: '${MYSQL_DB}'
+    volumes:
+      # - ./containers/db-server/db_data:/var/lib/mysql
+      - ./containers/db-server/mariadb/my.cnf:/etc/mysql/conf.d/my.cnf
+    healthcheck:
+      test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD
+      interval: 5s
+      retries: 5
+    networks:
+      - {{APP_NAME}}-network
diff --git a/docker/templates/dot_env b/docker/templates/dot_env
new file mode 100644
index 0000000..bc8af1d
--- /dev/null
+++ b/docker/templates/dot_env
@@ -0,0 +1,28 @@
+# TARGET: docker/.env
+# ======================================================================
+#
+# {{generator}}
+# values to be used in docker-composer.yml
+#
+# ======================================================================
+
+# ----- application
+APP_NAME={{APP_NAME}}
+
+# uid of www-data in the docker container
+DOCKER_USER_UID={{DOCKER_USER_UID}}
+
+APP_PORT={{APP_PORT}}
+WEBROOT={{WEBROOT}}
+
+# --- 8< --- {{CUTTER_NO_DATABASE}} --- 8< ---
+
+DB_PORT={{DB_PORT}}
+
+# ----- database settings
+MYSQL_RANDOM_ROOT_PASSWORD={{MYSQL_RANDOM_ROOT_PASSWORD}}
+MYSQL_ALLOW_EMPTY_PASSWORD={{MYSQL_ALLOW_EMPTY_PASSWORD}}
+MYSQL_ROOT_PASS={{MYSQL_ROOT_PASS}}
+MYSQL_USER={{APP_NAME}}
+MYSQL_PASS={{MYSQL_PASS}}
+MYSQL_DB={{APP_NAME}}
diff --git a/docker/templates/extra-php-config.ini b/docker/templates/extra-php-config.ini
new file mode 100644
index 0000000..d3f5327
--- /dev/null
+++ b/docker/templates/extra-php-config.ini
@@ -0,0 +1,25 @@
+# TARGET: docker/containers/web-server/php/extra-php-config.ini
+;
+; {{generator}}
+;
+[PHP]
+
+error_reporting=E_ALL
+display_errors=1
+
+; ----------------------------------------------------------------------
+; XDEBUG STUFF BELOW
+; ----------------------------------------------------------------------
+; 
+; error_reporting=E_ALL
+; 
+; [xdebug]
+; xdebug.mode=develop,debug
+; ; xdebug.client_host=localhost
+; xdebug.start_with_request=yes
+; ; xdebug.start_with_request=trigger
+; 
+; xdebug.log=/tmp/xdebug.log
+; xdebug.discover_client_host = 1
+; ; xdebug.client_port=9003
+; xdebug.idekey="netbeans-xdebug"
\ No newline at end of file
diff --git a/docker/templates/my.cnf b/docker/templates/my.cnf
new file mode 100644
index 0000000..3692f17
--- /dev/null
+++ b/docker/templates/my.cnf
@@ -0,0 +1,4 @@
+# TARGET: docker/containers/db-server/mariadb/my.cnf
+[mysqld]
+; collation-server = utf8mb4_unicode_ci
+; character-set-server = utf8mb4
\ No newline at end of file
diff --git a/docker/templates/readme.md b/docker/templates/readme.md
new file mode 100644
index 0000000..34c2c57
--- /dev/null
+++ b/docker/templates/readme.md
@@ -0,0 +1,7 @@
+# Templates
+
+## Rules
+
+* in the first line must be a line `# TARGET: [name of target file]` to define the target file
+* Placeholdrs have the syntax variable in double brackets, i.e. `{{VARNAME}}`
+* variables to be replaced are those in docker/init.sh.cfg and `{{genrator}}`
diff --git a/docker/templates/vhost_app.conf b/docker/templates/vhost_app.conf
new file mode 100644
index 0000000..56ce042
--- /dev/null
+++ b/docker/templates/vhost_app.conf
@@ -0,0 +1,34 @@
+# TARGET: docker/containers/web-server/apache/sites-enabled/vhost_app.conf
+#
+# {{generator}}
+#
+<VirtualHost *:80>
+  DocumentRoot {{WEBROOT}}
+  <Directory {{WEBROOT}}>
+      AllowOverride None
+      Order Allow,Deny
+      Allow from All
+  </Directory>
+
+  # redirect requests to handle packages
+  <Location "/packages">
+
+    # for Php as php-fpm service:
+    # SetEnvIf Authorization "(.*)" HTTP_AUTHORIZATION=$1
+
+    RewriteEngine on
+    RewriteCond %{REQUEST_FILENAME} !-f
+    RewriteRule ^(.*)$ index.php [QSA,L]
+
+  </Location>
+
+  # download files are outside webroot
+  XSendFile On
+  XSendFilePath "/var/www/${APP_NAME}/example-packages/"
+
+  # example to prevent access with http
+  <Location "/no-access">
+    Require all denied
+  </Location>
+
+</VirtualHost>
\ No newline at end of file
diff --git a/docker/templates/web-server-Dockerfile b/docker/templates/web-server-Dockerfile
new file mode 100644
index 0000000..d2443af
--- /dev/null
+++ b/docker/templates/web-server-Dockerfile
@@ -0,0 +1,15 @@
+# TARGET: docker/containers/web-server/Dockerfile
+#
+# {{generator}}
+#
+FROM php:{{APP_PHP_VERSION}}-apache
+
+# install packages
+RUN apt-get update && apt-get install -y {{APP_APT_PACKAGES}}
+
+# enable apache modules
+RUN a2enmod {{APP_APACHE_MODULES}}
+
+# install php packages
+COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/
+RUN install-php-extensions {{APP_PHP_MODULES}}
-- 
GitLab