Docker compose setup (#513)
* fix password reset bug * self hosted mode middleware changes on pages * fix lint * wip: self hosted changes * wip: self hosted frontend changes * wip self hosted mode changes * typo correction * remove commented logic * fix env variable names * fix lint issues * fix minor updates * #445 Switched from single monolithic docker image to a docker-compose orchestrated network of services * Automatically configures shared secret * Working through some issues * Use local file storage * Moved the dockerfiles * Fixed some issues when building from clean * Corrected workflow * Hopefully schedules everything correctly now * Prep storage for worker process as well * .env files are required * Pinned dependency versions * Disable self hosted in the client as well * Removed double defaulting logic * Using regexs is more succinct * Added FRONT_URL environment variable * Merge 236e4-self-hosted-mode-changes * Improve inital user setup * Finalized the new docker-compose setup * Fix back-end formatting issues --------- Co-authored-by: Frank <csskfaves@gmail.com> Co-authored-by: Don Benjamin <don@webhammer.co.uk>
This commit is contained in:
49
docker/Dockerfile.api
Normal file
49
docker/Dockerfile.api
Normal file
@@ -0,0 +1,49 @@
|
||||
FROM php:8.3-fpm
|
||||
|
||||
# syntax=docker/dockerfile:1.3-labs
|
||||
|
||||
RUN apt-get update && apt-get install -y libzip-dev libpng-dev postgresql-client libpq-dev && apt-get clean
|
||||
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||
RUN docker-php-ext-install pdo pgsql pdo_pgsql gd bcmath zip && pecl install redis && docker-php-ext-enable redis
|
||||
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
ADD composer.json composer.lock artisan ./
|
||||
|
||||
# NOTE: The project would build more reliably if all php files were added before running
|
||||
# composer install. This would though introduce a dependency which would cause every
|
||||
# dependency to be re-installed each time any php file is edited. It may be necessary in
|
||||
# future to remove this 'optimisation' by moving the `RUN composer install` line after all
|
||||
# the following ADD commands.
|
||||
|
||||
# Running artisan requires the full php app to be installed so we need to remove the
|
||||
# post-autoload command from the composer file if we want to run composer without
|
||||
# adding a dependency to all the php files.
|
||||
RUN sed 's_@php artisan package:discover_/bin/true_;' -i composer.json
|
||||
ADD app/helpers.php app/helpers.php
|
||||
RUN composer install --ignore-platform-req=php
|
||||
|
||||
ADD app ./app
|
||||
ADD bootstrap ./bootstrap
|
||||
ADD config ./config
|
||||
ADD database ./database
|
||||
ADD public public
|
||||
ADD routes routes
|
||||
ADD tests tests
|
||||
ADD resources resources
|
||||
ADD storage ./storage
|
||||
RUN chmod 777 -R storage
|
||||
|
||||
# Manually run the command we deleted from composer.json earlier
|
||||
RUN php artisan package:discover --ansi
|
||||
|
||||
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
|
||||
COPY docker/generate-api-secret.sh /usr/local/bin/
|
||||
RUN ln -s /secrets/api.env .env
|
||||
|
||||
RUN chmod a+x /usr/local/bin/*
|
||||
|
||||
ENTRYPOINT [ "/usr/local/bin/opnform-entrypoint" ]
|
||||
CMD php-fpm
|
||||
34
docker/Dockerfile.client
Normal file
34
docker/Dockerfile.client
Normal file
@@ -0,0 +1,34 @@
|
||||
FROM node:20-alpine AS javascript-builder
|
||||
WORKDIR /app
|
||||
|
||||
# It's best to add as few files as possible before running the build commands
|
||||
# as they will be re-run everytime one of those files changes.
|
||||
#
|
||||
# It's possible to run npm install with only the package.json and package-lock.json file.
|
||||
|
||||
ADD ./client/package.json ./client/package-lock.json ./
|
||||
|
||||
# Install git and other necessary build tools
|
||||
RUN apk add --no-cache git
|
||||
|
||||
# Clear npm cache, remove existing node_modules, and install dependencies
|
||||
RUN npm cache clean --force && \
|
||||
rm -rf node_modules && \
|
||||
npm install
|
||||
|
||||
# Explicitly install the correct version of esbuild
|
||||
# RUN npm install esbuild@0.21.5
|
||||
|
||||
ADD ./client/ /app/
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY --from=javascript-builder /app/.output/ /app/
|
||||
RUN ls /app/
|
||||
RUN ln -s /secrets/client.env .env
|
||||
ADD ./docker/node-entrypoint /entrypoint.sh
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
CMD [ "node", "./server/index.mjs" ]
|
||||
@@ -1,20 +1,21 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
main() {
|
||||
( flock -n 100 || wait_for_other_instance; generate_api_secrets) 100> /var/lock/api_secret.lock
|
||||
generate_api_secrets
|
||||
}
|
||||
|
||||
generate_api_secrets() {
|
||||
if ! is_configured; then
|
||||
echo "Generating shared secret..."
|
||||
SECRET="$(random_string)"
|
||||
add_secret_to_env_file /app/client/.env NUXT_API_SECRET "$SECRET"
|
||||
add_secret_to_env_file /app/.env FRONT_API_SECRET "$SECRET"
|
||||
add_secret_to_env_file /secrets/client.env NUXT_API_SECRET "$SECRET"
|
||||
add_secret_to_env_file /secrets/api.env FRONT_API_SECRET "$SECRET"
|
||||
fi
|
||||
}
|
||||
|
||||
random_string() {
|
||||
array=()
|
||||
for i in {a..z} {A..Z} {0..9};
|
||||
for i in {a..z} {A..Z} {0..9};
|
||||
do
|
||||
array[$RANDOM]=$i
|
||||
done
|
||||
@@ -27,21 +28,15 @@ add_secret_to_env_file() {
|
||||
VAR=$2
|
||||
VAL=$3
|
||||
|
||||
grep "^$VAR=" "$FILE" || ( echo "$VAR=" >> "$FILE" )
|
||||
grep -q "^$VAR=" "$FILE" 2>/dev/null || ( echo "$VAR=" >> "$FILE" )
|
||||
|
||||
cp $FILE $TEMP_FILE
|
||||
sed "s/^$VAR=.*$/$VAR=$VAL/" -i $TEMP_FILE
|
||||
cat $TEMP_FILE > $FILE
|
||||
}
|
||||
|
||||
wait_for_other_instance() {
|
||||
while ! is_configured; do
|
||||
sleep 1;
|
||||
done
|
||||
}
|
||||
|
||||
is_configured() {
|
||||
grep -q "FRONT_API_SECRET=.\+" /app/.env
|
||||
grep -q "FRONT_API_SECRET=.\+" .env 2>/dev/null
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
@@ -14,32 +14,27 @@ server {
|
||||
index index.html index.htm index.php;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_http_version 1.1;
|
||||
proxy_pass http://ui:3000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Port $server_port;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
set $original_uri $uri;
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location /local/temp/ {
|
||||
set $original_uri $uri;
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location /forms/assets/ {
|
||||
location ~/(api|open|local\/temp|forms\/assets)/ {
|
||||
set $original_uri $uri;
|
||||
try_files $uri $uri/ /index.php$is_args$args;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
||||
fastcgi_pass unix:/var/run/php-fpm-opnform-site.sock;
|
||||
fastcgi_pass api:9000;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi.conf;
|
||||
include fastcgi_params;
|
||||
#fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/public/index.php;
|
||||
fastcgi_param REQUEST_URI $api_uri;
|
||||
}
|
||||
}
|
||||
|
||||
30
docker/node-entrypoint
Normal file
30
docker/node-entrypoint
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
|
||||
main() {
|
||||
if [ "$1" == "bash" ]; then
|
||||
"$@"
|
||||
else
|
||||
wait_for_api_secret
|
||||
if [ ! -f .env ] && [ -f /secrets/client.env ]; then
|
||||
ln -sf /secrets/client.env .env
|
||||
fi
|
||||
if [ -f .env ]; then
|
||||
. .env
|
||||
else
|
||||
echo "Warning: .env file not found"
|
||||
fi
|
||||
run_server "$@"
|
||||
fi
|
||||
}
|
||||
wait_for_api_secret() {
|
||||
until [ -f /secrets/configured ]; do
|
||||
echo "Waiting for api secret..."
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
run_server() {
|
||||
echo "Running " node "$@"
|
||||
"$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
echo + . ~nuxt/.nvm/nvm.sh
|
||||
. ~nuxt/.nvm/nvm.sh
|
||||
|
||||
echo + nvm install --no-progress 20
|
||||
nvm install --no-progress 20
|
||||
echo + nvm use 20
|
||||
nvm use 20
|
||||
|
||||
cd /app/nuxt/server/
|
||||
|
||||
export NUXT_PRIVATE_API_BASE=http://localhost/api
|
||||
|
||||
echo + . /app/client/.env
|
||||
[ -f /app/client/.env ] && . /app/client/.env || echo "Environment file missing!"
|
||||
|
||||
[ "x$NUXT_API_SECRET" != "x" ] || (
|
||||
echo + generate-api-secret.sh
|
||||
generate-api-secret.sh
|
||||
)
|
||||
|
||||
echo + eval \$\(sed 's/^/export /' \< /app/client/.env\)
|
||||
eval $(sed 's/^/export /' < /app/client/.env)
|
||||
|
||||
echo + node index.mjs
|
||||
node index.mjs
|
||||
115
docker/php-fpm-entrypoint
Normal file
115
docker/php-fpm-entrypoint
Normal file
@@ -0,0 +1,115 @@
|
||||
#!/bin/bash
|
||||
|
||||
main() {
|
||||
read_env
|
||||
prep_file_permissions
|
||||
prep_storage
|
||||
if is_master "$@"; then
|
||||
prep_laravel_secrets
|
||||
wait_for_db
|
||||
apply_db_migrations
|
||||
run_init_project
|
||||
mark_ready
|
||||
else
|
||||
wait_for_ready
|
||||
wait_for_db
|
||||
fi
|
||||
read_env
|
||||
run_server "$@"
|
||||
}
|
||||
is_master() {
|
||||
echo "$@" | grep -q php-fpm
|
||||
}
|
||||
|
||||
read_env() {
|
||||
#set +x
|
||||
[ -f .env ] || touch .env
|
||||
. .env
|
||||
#set -x
|
||||
}
|
||||
prep_file_permissions() {
|
||||
chmod a+x ./artisan
|
||||
}
|
||||
|
||||
prep_laravel_secrets() {
|
||||
read_env
|
||||
|
||||
[ "x$APP_KEY" != "x" ] || {
|
||||
echo "Generating Laravel key..."
|
||||
grep -q "APP_KEY=" .env || {
|
||||
echo "APP_KEY=" >> .env
|
||||
}
|
||||
./artisan key:generate
|
||||
read_env
|
||||
}
|
||||
[ "x$JWT_SECRET" != "x" ] || {
|
||||
echo "Generating Laravel Secret..."
|
||||
./artisan jwt:secret -f
|
||||
read_env
|
||||
}
|
||||
|
||||
[ "x$FRONT_API_SECRET" != "x" ] || {
|
||||
echo "Generating Shared Client Secret..."
|
||||
/usr/local/bin/generate-api-secret.sh
|
||||
read_env
|
||||
}
|
||||
echo "Done with secrets"
|
||||
}
|
||||
|
||||
apply_db_migrations() {
|
||||
echo "Running DB Migrations"
|
||||
./artisan migrate
|
||||
}
|
||||
|
||||
run_init_project() {
|
||||
echo "Running app:init-project command"
|
||||
./artisan app:init-project
|
||||
}
|
||||
|
||||
wait_for_ready() {
|
||||
echo "Checking keys have been generated"
|
||||
until [ -f /secrets/configured ]; do
|
||||
sleep 1;
|
||||
echo "Waiting for keys to generate"
|
||||
done
|
||||
}
|
||||
|
||||
mark_ready() {
|
||||
touch /secrets/configured
|
||||
}
|
||||
|
||||
wait_for_db() {
|
||||
until ./artisan migrate:status 2>&1 | grep -q -E "(Migration table not found|Migration name)"; do
|
||||
echo "Waiting for DB to bootup"
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
run_server() {
|
||||
echo "Booting $@"
|
||||
read_env
|
||||
/usr/local/bin/docker-php-entrypoint "$@"
|
||||
}
|
||||
|
||||
prep_storage() {
|
||||
[ -L storage ] || {
|
||||
echo "Backing up initial storage directory"
|
||||
rm -rf /etc/initial-storage
|
||||
mv ./storage /etc/initial-storage
|
||||
}
|
||||
|
||||
[ -d /persist/storage ] || {
|
||||
echo "Initialising blank storage dir"
|
||||
mkdir -p /persist
|
||||
cp -a /etc/initial-storage /persist/storage
|
||||
chmod 777 -R /persist/storage
|
||||
}
|
||||
|
||||
touch /var/log/opnform.log
|
||||
chown www-data /var/log/opnform.log
|
||||
|
||||
echo "Linking persistent storage into app"
|
||||
ln -t . -sf /persist/storage
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
[ -L /app/storage ] || {
|
||||
echo "Backing up initial storage directory"
|
||||
rm -rf /etc/initial-storage
|
||||
mv /app/storage /etc/initial-storage
|
||||
}
|
||||
|
||||
[ -d /persist/storage ] || {
|
||||
echo "Initialising blank storage dir"
|
||||
mkdir -p /persist
|
||||
cp -a /etc/initial-storage /persist/storage
|
||||
chmod 777 -R /persist/storage
|
||||
}
|
||||
|
||||
touch /var/log/opnform.log
|
||||
chown opnform /var/log/opnform.log
|
||||
|
||||
echo "Linking persistent storage into app"
|
||||
ln -t /app -sf /persist/storage
|
||||
|
||||
read_env() {
|
||||
set +x
|
||||
. /app/.env
|
||||
set -x
|
||||
}
|
||||
read_env
|
||||
|
||||
[ "x$APP_KEY" != "x" ] || {
|
||||
artisan key:generate
|
||||
read_env
|
||||
}
|
||||
[ "x$JWT_SECRET" != "x" ] || {
|
||||
artisan jwt:secret -f
|
||||
read_env
|
||||
}
|
||||
|
||||
[ "x$FRONT_API_SECRET" != "x" ] || {
|
||||
generate-api-secret.sh
|
||||
read_env
|
||||
}
|
||||
|
||||
/usr/sbin/php-fpm8.1
|
||||
|
||||
tail -f /var/log/opnform.log
|
||||
@@ -1,18 +0,0 @@
|
||||
[opnform]
|
||||
user = opnform
|
||||
group = opnform
|
||||
listen = /var/run/php-fpm-opnform-site.sock
|
||||
listen.owner = www-data
|
||||
listen.group = www-data
|
||||
php_admin_value[disable_functions] = exec,passthru,shell_exec,system
|
||||
php_admin_flag[allow_url_fopen] = off
|
||||
php_admin_value[error_log] = /var/log/opnform.log
|
||||
; Choose how the process manager will control the number of child processes.
|
||||
pm = dynamic
|
||||
pm.max_children = 75
|
||||
pm.start_servers = 10
|
||||
pm.min_spare_servers = 5
|
||||
pm.max_spare_servers = 20
|
||||
pm.process_idle_timeout = 10s
|
||||
clear_env = no
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
DATA_DIR=/persist/pgsql/data
|
||||
CONFIG_FILE=/etc/postgresql/postgresql.conf
|
||||
PG_BASE=/usr/lib/postgresql/15/
|
||||
|
||||
touch $CONFIG_FILE
|
||||
|
||||
mkdir -p $DATA_DIR
|
||||
chown postgres -R $DATA_DIR
|
||||
chmod 0700 $DATA_DIR
|
||||
|
||||
set +x
|
||||
. /app/.env
|
||||
set -x
|
||||
|
||||
test -f $DATA_DIR/postgresql.conf || NEW_DB=true
|
||||
|
||||
if [ "x$NEW_DB" != "x" ]; then
|
||||
echo "No database files found. Initialising blank database"
|
||||
sudo -u postgres $PG_BASE/bin/initdb -D $DATA_DIR
|
||||
fi
|
||||
sudo -u postgres $PG_BASE/bin/postgres -D $DATA_DIR -c config_file=$CONFIG_FILE &
|
||||
|
||||
wait_for_database_to_be_ready() {
|
||||
while ! (echo "select version()" | psql -U $DB_USERNAME); do
|
||||
echo "Waiting 5 seconds for the database to come up"
|
||||
sleep 5;
|
||||
done
|
||||
}
|
||||
|
||||
if [ "x$NEW_DB" != "x" ]; then
|
||||
echo "Creating database users"
|
||||
wait_for_database_to_be_ready
|
||||
psql -U postgres <<EOF
|
||||
CREATE ROLE $DB_USERNAME LOGIN PASSWORD '$DB_PASSWORD';
|
||||
CREATE DATABASE $DB_DATABASE;
|
||||
\c $DB_DATABASE;
|
||||
GRANT ALL ON DATABASE $DB_DATABASE TO $DB_USERNAME;
|
||||
GRANT ALL ON SCHEMA public TO $DB_USERNAME;
|
||||
EOF
|
||||
|
||||
fi
|
||||
|
||||
wait_for_database_to_be_ready
|
||||
sudo -u opnform artisan migrate --force
|
||||
|
||||
wait
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
sysctl vm.overcommit_memory=1
|
||||
mkdir -p /persist/redis/data
|
||||
chown redis -R /persist/redis/data
|
||||
|
||||
sudo -u redis /usr/bin/redis-server /etc/redis/redis.conf
|
||||
@@ -1,44 +0,0 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/dev/null
|
||||
logfile_maxbytes=0
|
||||
user=root
|
||||
|
||||
[program:nginx]
|
||||
command=/usr/sbin/nginx
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
|
||||
[program:php-fpm]
|
||||
command=/usr/local/bin/php-fpm-wrapper.sh
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
|
||||
[program:php-queue]
|
||||
process_name=%(program_name)s_%(process_num)02d
|
||||
command=/usr/local/bin/artisan queue:work
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
numprocs=5
|
||||
|
||||
[program:postgres]
|
||||
command=/usr/local/bin/postgres-wrapper.sh
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
|
||||
[program:redis]
|
||||
command=/usr/local/bin/redis-wrapper.sh
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
|
||||
[program:nuxt-backend]
|
||||
command=/usr/local/bin/nuxt-wrapper.sh
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
redirect_stderr=true
|
||||
user=nuxt
|
||||
Reference in New Issue
Block a user