2
0

Compare commits

..

No commits in common. "48faad59c7f1675b5d8ae6dfe2117b40c8e82a63" and "39c0f07fca09aa5b79e18c2397e6eb0fcce4ced4" have entirely different histories.

36 changed files with 115 additions and 5091 deletions

BIN
.metadata

Binary file not shown.

View File

@ -1,5 +1,6 @@
# MediaWiki with MariaDB (optionally with extensions). # MediaWiki with MariaDB (optionally with extensions).
# #
#version: '3'
services: services:
# https://hub.docker.com/_/mediawiki # https://hub.docker.com/_/mediawiki
mediawiki: mediawiki:
@ -16,7 +17,8 @@ services:
# Needs R/W UID:GID 33:33 (www-data:www-data). # Needs R/W UID:GID 33:33 (www-data:www-data).
- ./storage/volumes/mediawiki_images:/var/www/html/images - ./storage/volumes/mediawiki_images:/var/www/html/images
# After initial setup, download LocalSettings.php # After initial setup, download LocalSettings.php
# populate the following line and restart the mediawiki service. # populate the following line and
# use compose to restart the mediawiki service.
# Needs read UID or GID 33 (www-data). # Needs read UID or GID 33 (www-data).
# - ./configs/LocalSettings.php:/var/www/html/LocalSettings.php:ro # - ./configs/LocalSettings.php:/var/www/html/LocalSettings.php:ro
extra_hosts: extra_hosts:

View File

@ -1,31 +1,28 @@
#!/bin/bash #!/bin/bash
# #
# A service script to backup the docker-composed MySQL/MariaDB database. # A service script to backup the docker-composed Mediawiki instance.
# Dumps database to the $BASE_DIR/storage/backups/dumps folder (by default). # Dumps the MySQL/MariaDB database to the $BASE_DIR/storage/backups/dumps
# An optional parameter may change the target folder. # folder (by default). An optional parameter may change the target folder.
# #
# This script gets the database credentials from the docker-compose.yml file # This script gets the database credentials from MW's LocalSettings.php
# and calls the mysql_dumpdb worker script which should be installed in # and calls the mysql_dumpdb worker script which should be installed in
# the same folder or somewhere in the path. # the same folder or somewhere in the path.
# #
# Call as a Docker manager user (member of the docker Linux group) via cron. # Call as a Docker manager user (member of the docker Linux group) via cron.
# Uses the mysql_dumpdb utility which must be available on path.
# #
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu> # Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html # License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-02-26 v0.3
# mod: doesn't tied to a particular composition (Mediawiki, Wordpress, etc).
# 2024-12-01 v0.2.1 # 2024-12-01 v0.2.1
# fix: typo in docker-compose version detection. # fix: typo in docker-compose version detection.
# 2024-08-25 v0.2 # 2024-08-24 v0.2
# new: docker-compose v2 compatibility - tested with Ubuntu 24.04 LTS. # new: docker-compose v2 compatibility - tested with Ubuntu 24.04 LTS.
# 2021-10-19 v0.1 Initial version. # 2021-08-27 v0.1 Initial version.
# Accepted environment variables and their defaults. # Accepted environment variables and their defaults.
# #
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_DUMPDIR=${PAR_DUMPDIR:-""} # Folder to dump within PAR_DUMPDIR=${PAR_DUMPDIR:-""} # Folder to dump within
PAR_SERVICE=${PAR_SERVICE:-"database"} # Service's name in composition
# Messages (maybe overridden by configuration). # Messages (maybe overridden by configuration).
# #
@ -36,11 +33,11 @@ MSG_MISSINGCONF="Fatal: missing config file"
MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file" MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file"
MSG_NONWRITE="The target directory isn't writable" MSG_NONWRITE="The target directory isn't writable"
MSG_NOLOCATE="Cannot locate the database container." MSG_NOLOCATE="Cannot locate the database container."
MSG_NOPARAM="Missing environment parameter" MSG_NOPARAM="Missing PHP parameter"
# Other initialisations. # Other initialisations.
# #
CONFFILE="docker-compose.yml" # Configuration file CONFFILE="configs/LocalSettings.php" # MW's configuration file
DUMPDIR="storage/backups/dumps" # Folder to dump within DUMPDIR="storage/backups/dumps" # Folder to dump within
USER=${USER:-LOGNAME} # Fix for cron enviroment only USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml" YMLFILE="docker-compose.yml"
@ -115,12 +112,12 @@ DUMPDIR="${PAR_DUMPDIR:-$BASE_DIR/$DUMPDIR}"
[[ ! -w "$DUMPDIR" ]] \ [[ ! -w "$DUMPDIR" ]] \
&& echo "$MSG_NONWRITE: $DUMPDIR" >&2 && exit 1 && echo "$MSG_NONWRITE: $DUMPDIR" >&2 && exit 1
# The composition must be running - silently gives up here if not. # The service must be running - silently gives up here if not.
# #
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \ [[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1 && exit 1
# Searches and parses the config file. # Searches and parses the MW's LocalSettings.php file.
# #
if [ ! -r "$CONFFILE" ]; then if [ ! -r "$CONFFILE" ]; then
echo "$MSG_MISSINGCONF $CONFFILE" >&2; exit 1 echo "$MSG_MISSINGCONF $CONFFILE" >&2; exit 1
@ -129,29 +126,31 @@ fi
function parse { [[ -z "$1" ]] && return function parse { [[ -z "$1" ]] && return
# Gets the live lines containing the parameter. # Gets the live lines containing the parameter.
value=$("$CAT" "$CONFFILE" | "$GREP" -ve '^#' | \ value=$("$CAT" "$CONFFILE" | "$GREP" -ve '^#' | \
"$GREP" -e "$1" | "$TR" -d '\r') "$GREP" -e "^$1" | "$TR" -d '\r')
# If multiple the last one to consider. # If multiple the last one to consider.
value=$(echo -e "$value" | "$TAIL" -n1) value=$(echo -e "$value" | "$TAIL" -n1)
# Right side of the colon W/O leading and trailing spaces and quotes. # Right side of the equal sign W/O leading and trailing spaces and quotes.
value=$(echo -ne "$value" | "$CUT" -d':' -f2 | "$XARGS") value=$(echo -ne "$value" | "$CUT" -d'=' -f2 | "$XARGS")
# Removes the trailing semicolon (if any). # Removes the trailing semicolon (if any).
value=${value%;*} value=${value%;*}
echo -e "$value"; return echo -e "$value"; return
} }
# Gives up here silently if the type of the database isn't MySQL.
[[ "$(parse "\$wgDBtype")" != 'mysql' ]] && exit 1
# All parameters are mandatories. # All parameters are mandatories.
MYCONTAINER="$PAR_SERVICE" # TODO: guess from the yml MYCONTAINER="$(parse "\$wgDBserver")"
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM PAR_SERVICE" >&2; exit 1; fi1; fi if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM \$wgDBserver" >&2; exit 1; fi
MYDATABASE="$(parse "MYSQL_DATABASE")" MYDATABASE="$(parse "\$wgDBname")"
if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM MYSQL_DATABASE" >&2; exit 1; fi if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM \$wgDBname" >&2; exit 1; fi
MYUSER="$(parse "MYSQL_USER")" MYUSER="$(parse "\$wgDBuser")"
if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM MYSQL_USER" >&2; exit 1; fi if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM \$wgDBuser" >&2; exit 1; fi
MYPASSWORD="$(parse "MYSQL_PASSWORD")" MYPASSWORD="$(parse "\$wgDBpassword")"
if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM MYSQL_PASSWORD" >&2; exit 1; fi if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM \$wgDBpassword" >&2; exit 1; fi
# We've the configuration parsed. # We've the configuration parsed.
# Converts the database service name to an actual running container's name. # Converts the database service name to an actual running container's name.
# #
MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps -q "$MYCONTAINER") | "$CUT" -c2-)" MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandtring ps -q "$MYCONTAINER") | "$CUT" -c2-)"
# Gives up here if failed. # Gives up here if failed.
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi

View File

@ -1,195 +0,0 @@
#!/bin/bash
#
# Restores a composed MySQL/MariaDB database from a dump file.
# Gets all necessary data from the docker-compose.yml file.
#
# This is a wrapper script to the system-wide mysql_restoredb tool.
# Database recovey with the necessary user management and grants
# requires superuser privileges in MySQL, but simple data recovery
# is possible if the user and privileges are already set.
#
# You have to call this script as a Docker manager user (member of the
# 'docker' Linux group). The worker tool must be available somewhere
# in PATH. At least 5.7.6 MySQL or at least 10.1.3 MariaDB is required.
#
# Usage:
# $0 path_to_the_dumpfile [ path_to_the_service's_base ]
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html)
#
# 2025-02-26 v0.1 Forked from the Smartfront repository and rewritten.
# Accepted environment variables and their defaults.
#
PAR_SERVICE=${SERVICE:-"database"} # Database container's name
# Other initialisations.
#
BACKUPFOLDER="storage/backups/dumps" # Skeleton's default dump folder
PROP_DBAPASS="MYSQL_ROOT_PASSWORD" # DB admin password property
PROP_DBNAME="MYSQL_DATABASE" # DB name property
PROP_DBPASS="MYSQL_PASSWORD" # DB password property
PROP_DBUSER="MYSQL_USER" # DB username property
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml"
# Basic environment settings.
#
LANG=C
LC_ALL=C
# Messages.
#
MSG_BADDUMP="Fatal: doesn't exist or doesn't a dumpfile:"
MSG_DOCKERGRPNEED="You must be a member of the docker group."
MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGCONF="Fatal: missing config file"
MSG_MISSINGYML="Fatal: didn't find the $YMLFILE file"
MSG_NOLOCATE="Cannot locate the database container."
MSG_NOPARAM="Missing environment parameter"
MSG_USAGE="Usage: $0 dump_pathname [ composition_base_pathname ]\n"
MSG_USAGE+="ENVVAR:\n"
MSG_USAGE+="SERVICE \tDatabase service's name in composition\n"
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname docker \
grep id mysql_restoredb readlink tail xargs
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
#
# Let's find which version of docker-compose is installed.
if [ $($DOCKER compose version >/dev/null 2>&1; echo $?) -eq 0 ]; then
# We'll use v2 if it is available.
DOCKER_COMPOSE="$DOCKER"
commandstring="compose"
else
# Otherwise falling back to v1.
DOCKER_COMPOSE="$(which docker-compose)"
commandstring=""
fi
# One of the two is mandatory.
if [ -z "$DOCKER_COMPOSE" ];then echo "$MSG_MISSINGDEP docker-compose" >&2; exit 1; fi
# Below docker-compose should be called as "$DOCKER_COMPOSE" $commandstring sequence.
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Need to be root or a Docker manager user.
#
[[ "$USER" != 'root' ]] \
&& [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \
&& echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #"
# Gets the command line parameters.
#
# DUMPFILE is mandatory
if [ -n "$1" ]; then DUMPFILE="$1"; shift
else echo -e "$MSG_USAGE" >&2; exit 1; fi
# SERVICE_BASE is optional
if [ -n "$1" ]; then SERVICE_BASE="$1"; shift; fi
# We've read the unchecked command line parameters.
# Searches the base folder, containing the YMLFILE.
#
if [ -z "$SERVICE_BASE" ]; then
# Called from the base folder (./)?
TEST_DIR="$SCRPATH"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
fi
# On failure gives it up here.
if [ -z "$SERVICE_BASE" -o ! -r "$SERVICE_BASE/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute path.
YMLFILE="$SERVICE_BASE/$YMLFILE"
# We've the YMLFILE.
# Finds the DUMPFILE to use.
#
# The DUMPFILE must point to a readable file.
# If doesn't it tries the skeleton's standard backup folder as well.
if [ ! -r "$DUMPFILE" ]
then DUMPFILE="$("$DIRNAME" "$SERVICE_BASE")/$BACKUPFOLDER/$DUMPFILE"; fi
# If it is an existing symlink dereferences it to ensure, it points to a file.
if [ -h "$DUMPFILE" ]; then
if [[ "$("$READLINK" "$DUMPFILE")" != /* ]]
# relative path in symlink
then DUMPFILE="$("$DIRNAME" "$DUMPFILE")/$("$READLINK" "$DUMPFILE")"
# absolute path in symlink
else DUMPFILE="$("$READLINK" "$DUMPFILE")"; fi
fi
# Let's check it!
if [ ! -r "$DUMPFILE" -o ! -f "$DUMPFILE" ]
then echo -e "$MSG_BADDUMP $DUMPFILE"; exit 1; fi
# We've an existing dumpfile.
# The composition must be running - silently gives up here if not.
#
[[ -z "$(cd "$SERVICE_BASE"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1
# Parses the YMLFILE for parameters to use.
#
function parse { [[ -z "$1" ]] && return
# Gets the live lines containing the parameter.
value=$("$CAT" "$YMLFILE" | "$GREP" -ve '^#' | \
"$GREP" -e "^ *$1" | "$TR" -d '\r')
# If multiple the last one to consider.
value=$(echo -e "$value" | "$TAIL" -n1)
# Right side of the colon W/O leading and trailing spaces and quotes.
value=$(echo -ne "$value" | "$CUT" -d':' -f2 | "$XARGS")
# Removes the trailing semicolon (if any).
value=${value%;*}
echo -e "$value"; return
}
# These parameters are mandatory.
MYCONTAINER="$PAR_SERVICE" # TODO: guess from the yml
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM PAR_SERVICE" >&2; exit 1; fi1; fi
MYDATABASE="$(parse "$PROP_DBNAME")"
if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM $PROP_DBNAME" >&2; exit 1; fi
MYUSER="$(parse "$PROP_DBUSER")"
if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM $PROP_DBUSER" >&2; exit 1; fi
MYPASSWORD="$(parse "$PROP_DBPASS")"
if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM $PROP_DBPASS" >&2; exit 1; fi
# These are optional.
MYDBAUSER="root"
MYDBAPASSWORD="$(parse "$PROP_DBAPASS")"
# We've the configuration parsed.
# Converts the database service name to an actual running container's name.
#
MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$SERVICE_BASE"; "$DOCKER_COMPOSE" $commandstring ps -q "$MYCONTAINER") | "$CUT" -c2-)"
# Gives up here if failed.
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Calls the worker script to make the job.
#
export MYDBAUSER MYDBAPASSWORD MYPASSWORD
"$MYSQL_RESTOREDB" -C "$MYCONTAINER" -U "$MYUSER" "$MYDATABASE" "$DUMPFILE"
# That's all, Folks! :)

View File

@ -1,7 +0,0 @@
# Ignore everything else in this directory.
*
!storage
!tools
!.gitignore
!README.md
!docker-compose.yml

View File

@ -1,113 +0,0 @@
# Node.js with MongoDB and Mongo-Express tool.
#
# Provides a JavaScript runtime environment with MongoDB backend.
# Assumes a suitable JS application in nodejs-apps volume.
#
services:
#
# https://hub.docker.com/_/node
# https://github.com/nodejs/docker-node
#
nodejs:
image: node:latest
restart: unless-stopped
# Choose a suitable Linux account here.
# Must have R/W access to the nodejs-apps volume.
user: "1001"
# The application defines the port(s) to expose.
# Take a look the possible public port collision.
ports:
- 8201:8080
links:
- mongodb
volumes:
- /etc/localtime:/etc/localtime:ro
- ./storage/volumes/nodejs-apps:/home/node/app
environment:
TZ: Europe/Budapest
NODE_ENV: production
NPM_CONFIG_CACHE: /home/node/app/node_modules/.cache
NPM_CONFIG_LOGLEVEL: info
#
# Environment variables to control the docker-skeleton's
# external backup. The Node.JS image doesn't interpret them.
# You may specify the relevant folders for the backup utility.
# By default it backups the entire nodejs-apps folder.
DS_BACKUP_FOLDERS: ''
# These folders below will be excluded from the backup.
DS_BACKUP_EXCLUDES: ''
# You may specify the relevant MongoDB database(s) as well.
DS_BACKUP_DATABASES: ''
#
# Starting the application via npm and package.json:
#command: sh -c "cd /home/node/app && npm install && npm start"
# Starting a single file application (testing only):
command: node /home/node/app/helloworld.js
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
com.centurylinklabs.watchtower.enable: true
#
# https://hub.docker.com/_/mongo
# https://github.com/docker-library/mongo
#
mongodb:
image: mongo:latest
restart: unless-stopped
# Choose a suitable Linux account here.
# Must have R/W access to the mongodb-data volume.
user: "1001"
volumes:
- ./storage/volumes/mongodb-data:/data/db
environment:
MONGO_INITDB_DATABASE: admin
# Sets the DBA (root) credentials below.
MONGO_INITDB_ROOT_USERNAME: admin
# It is highly recommended to change this to a strong random password.
# https://passwordsgenerator.net/
MONGO_INITDB_ROOT_PASSWORD: secret-1
# Sets the DBA (root) credentials below.
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
com.centurylinklabs.watchtower.enable: true
#
# https://hub.docker.com/_/mongo-express
# https://github.com/mongo-express/mongo-express
# https://github.com/mongo-express/mongo-express-docker
#
mongoxp:
image: mongo-express
restart: unless-stopped
# Take a look the possible public port collision.
ports:
- 8202:8081
links:
- mongodb
environment:
# Override the default value set in the docker-entrypoint.sh:
# https://github.com/mongo-express/mongo-express-docker/issues/21
ME_CONFIG_MONGODB_URL: fake,fake
ME_CONFIG_MONGODB_SERVER: mongodb
ME_CONFIG_MONGODB_PORT: 27017
ME_CONFIG_SITE_BASEURL: /mongoxp/
# We don't use SSL behind a local reverse proxy.
ME_CONFIG_SITE_SSL_ENABLED: false
ME_CONFIG_SITE_SSL_CRT_PATH: ''
ME_CONFIG_SITE_SSL_KEY_PATH: ''
# We use the root account here.
ME_CONFIG_MONGODB_ENABLE_ADMIN: true
# Must match MONGO_INITDB_ROOT_* credentials.
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: secret-1
# It is recommended to use at least a basic authentication.
ME_CONFIG_BASICAUTH: true
ME_CONFIG_BASICAUTH_USERNAME: admin
# It is highly recommended to change this to a strong random password.
# https://passwordsgenerator.net/
ME_CONFIG_BASICAUTH_PASSWORD: secret-2
ME_CONFIG_OPTIONS_EDITORTHEME: ambiance
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
com.centurylinklabs.watchtower.enable: true

View File

@ -1,4 +0,0 @@
# Ignore everything in this directory except this folders.
*
!.gitignore
!volumes

View File

@ -1,5 +0,0 @@
# Ignore everything in this directory except this folders.
*
!.gitignore
!nodejs-apps
!mongodb-data

View File

@ -1,3 +0,0 @@
# Ignore everything in this directory except this folders.
*
!.gitignore

View File

@ -1,4 +0,0 @@
# Ignore everything in this directory except this folders.
*
!.gitignore
!helloworld.js

View File

@ -1,11 +0,0 @@
/*
A humble test web application.
https://www.w3schools.com/nodejs/nodejs_get_started.asp
*/
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Hello World!');
}).listen(8080);

View File

@ -1,4 +0,0 @@
# Ignore everything else in this directory.
*
!*.d
!.gitignore

View File

@ -1,169 +0,0 @@
#!/bin/bash
#
# A service script to dump the relevant MongoDB database(s)
# of a docker-composed MongoDB instance. Creates a tarball in
# $BASE_DIR/storage/backups/tarballs folder (by default).
# The relevant databases must be specified within the
# docker-compose.yml in a BACKUP_DATABASES environment variable.
# An optional parameter may change the target folder.
#
# Call as a Docker manager user (member of the docker Linux group) via cron.
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-03-05 v0.1.1
# mod: minimally rewrited the description.
# 2024-09-23 v0.1 Initial version.
# Accepted environment variables and their defaults.
#
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_BACKUPDIR=${PAR_BACKUPDIR:-""} # Folder to dump within
# Messages (maybe overridden by configuration).
#
MSG_DOCKERGRPNEED="You must be a member of the docker group."
MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file"
MSG_NONWRITE="The target directory isn't writable"
MSG_NOLOCATE="Cannot locate the MongoDB container."
MSG_NOPARAM="Missing environment parameter"
# Other initialisations.
#
BACKUPDIR="storage/backups/tarballs" # Folder to dump within
PAR_DATABASES="DS_BACKUP_DATABASES" # List of DB(s) in YMLFILE
SERVICENAME="mongodb" # The composed MongoDB service
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml" # Gets the parameters from here
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname docker \
grep gzip hostname id pwd tail xargs
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
#
# Let's find which version of docker-compose is installed.
if [ $($DOCKER compose version 2>&1 >/dev/null; echo $?) -eq 0 ]; then
# We'll use v2 if it is available.
DOCKER_COMPOSE="$DOCKER"
commandstring="compose"
else
# Otherwise falling back to v1.
DOCKER_COMPOSE="$(which docker-compose)"
commandstring=""
fi
# One of the two is mandatory.
if [ -z "$DOCKER_COMPOSE" ];then echo "$MSG_MISSINGDEP docker-compose" >&2; exit 1; fi
# Below docker-compose should be called as "$DOCKER_COMPOSE" $commandstring sequence.
#
# An additional bugfix (use "$(which gzip)" instead of "$GZIP"):
# https://www.gnu.org/software/gzip/manual/html_node/Environment.html
GZIP=""
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Need to be root or a Docker manager user.
#
[[ "$USER" != 'root' ]] \
&& [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \
&& echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #"
# Searches the base folder, containing a docker-compose.yml file.
#
# Called from the base folder (./)?
BASE_DIR="$PAR_BASEDIR"
TEST_DIR="$SCRPATH"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# On failure gives it up here.
if [ -z "$BASE_DIR" -o ! -r "$BASE_DIR/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute paths.
BACKUPDIR="${PAR_BACKUPDIR:-$BASE_DIR/$BACKUPDIR}"
# The dump target folder must be writable.
#
[[ ! -w "$BACKUPDIR" ]] \
&& echo "$MSG_NONWRITE: $BACKUPDIR" >&2 && exit 1
# The service must be running - silently gives up here if not.
#
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1
# Converts the MongoDB service name to an actual running container's name.
#
MDBCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps -q "$SERVICENAME") | "$CUT" -c2-)"
# Gives up here if failed.
if [ -z "$MDBCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Checks and parses the config file for database names to dump
# and DBA (root) credentials for MongoDB.
#
function parse { [[ -z "$1" ]] && return
# Gets the live lines containing the parameter.
value=$("$CAT" "$CONFFILE" 2>/dev/null | "$GREP" -ve '^#' | \
"$GREP" -e " $1:" | "$TR" -d '\r')
# If multiple the last one to consider.
value=$(echo -e "$value" | "$TAIL" -n1)
# Right side of the equal sign W/O leading and trailing spaces and quotes.
value=$(echo -ne "$value" | "$CUT" -d':' -f2 | "$XARGS")
echo -e "$value"; return
}
# Examines the YMLFILE.
CONFFILE="$BASE_DIR/$YMLFILE"
# Gets the list of the databases to dump. Silently exits if it is empty.
DATABASES="$(parse "$PAR_DATABASES")"
if [ -z "$DATABASES" ]; then exit; fi
# All parameters below are mandatories.
DBAUTH="$(parse "MONGO_INITDB_DATABASE")"
if [ -z "$DBAUTH" ]; then echo "$MSG_NOPARAM MONGO_INITDB_DATABASE" >&2; exit 1; fi
DBUSER="$(parse "MONGO_INITDB_ROOT_USERNAME")"
if [ -z "$DBAUTH" ]; then echo "$MSG_NOPARAM MONGO_INITDB_ROOT_USERNAME" >&2; exit 1; fi
DBPASS="$(parse "MONGO_INITDB_ROOT_PASSWORD")"
if [ -z "$DBAUTH" ]; then echo "$MSG_NOPARAM MONGO_INITDB_ROOT_PASSWORD" >&2; exit 1; fi
# We've the configuration parsed.
# Attempts the dump(s) using the mongodump utility existing within the container.
# Uses the DBA (root) credentials parsed from the YMLFILE above.
#
if [ -w "$BACKUPDIR" ]; then
# Enumerates the relevant databases (if any).
for DBNAME in $DATABASES ''
do
# Dumps the actual database as a DBA.
if [ -n "$DBNAME" ]; then
BACKUP_NAME=$SERVICENAME-$DBNAME.$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME")
"$DOCKER" exec $MDBCONTAINER sh -c "exec mongodump -u $DBUSER -p $DBPASS --authenticationDatabase $DBAUTH -d $DBNAME --quiet --archive" | \
"$(which gzip)" > "$BACKUPDIR/$BACKUP_NAME.archive.gz" 2>>"$BACKUPDIR/$BACKUP_NAME.log"
fi
done
fi
# That's all, Folks! :)

View File

@ -1,152 +0,0 @@
#!/bin/bash
#
# A service script to backup the application's storage (with exceptions)
# of a docker-composed Node.JS instance. Creates a tarball in
# $BASE_DIR/storage/backups/tarballs folder (by default). An optional
# parameter may change the target folder.
#
# The contents of the tarball can be refined by setting the SD_BACKUP_*
# environment variables in the docker-compose.yml file.
#
# Call as a Docker manager user (member of the docker Linux group) via cron.
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2024-09-23 v0.1 Initial version.
# Accepted environment variables and their defaults.
#
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_BACKUPDIR=${PAR_BACKUPDIR:-""} # Folder to dump within
# Messages (maybe overridden by configuration).
#
MSG_DOCKERGRPNEED="You must be a member of the docker group."
MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file"
MSG_NONWRITE="The target directory isn't writable"
MSG_NOLOCATE="Cannot locate the Node.JS container."
# Other initialisations.
#
APPSDIR="/home/node/app" # Base folder of storage to dump
BACKUPDIR="storage/backups/tarballs" # Folder to dump within
SERVICENAME="nodejs" # The composed Node.JS service
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml"
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname docker \
find grep hostname id pwd tail xargs
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
#
# Let's find which version of docker-compose is installed.
if [ $($DOCKER compose version 2>&1 >/dev/null; echo $?) -eq 0 ]; then
# We'll use v2 if it is available.
DOCKER_COMPOSE="$DOCKER"
commandstring="compose"
else
# Otherwise falling back to v1.
DOCKER_COMPOSE="$(which docker-compose)"
commandstring=""
fi
# One of the two is mandatory.
if [ -z "$DOCKER_COMPOSE" ];then echo "$MSG_MISSINGDEP docker-compose" >&2; exit 1; fi
# Below docker-compose should be called as "$DOCKER_COMPOSE" $commandstring sequence.
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Need to be root or a Docker manager user.
#
[[ "$USER" != 'root' ]] \
&& [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \
&& echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #"
# Searches the base folder, containing a docker-compose.yml file.
#
# Called from the base folder (./)?
BASE_DIR="$PAR_BASEDIR"
TEST_DIR="$SCRPATH"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# On failure gives it up here.
if [ -z "$BASE_DIR" -o ! -r "$BASE_DIR/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute paths.
BACKUPDIR="${PAR_BACKUPDIR:-$BASE_DIR/$BACKUPDIR}"
# The dump target folder must be writable.
#
[[ ! -w "$BACKUPDIR" ]] \
&& echo "$MSG_NONWRITE: $BACKUPDIR" >&2 && exit 1
# The service must be running - silently gives up here if not.
#
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1
# Converts the Node.JS service name to an actual running container's name.
#
NDCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps -q "$SERVICENAME") | "$CUT" -c2-)"
# Gives up here if failed.
if [ -z "$NDCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Checks and parses the config file for the folder (path)names
# to dump and to exclude.
#
function parse { [[ -z "$1" ]] && return
# Gets the live lines containing the parameter.
value=$("$CAT" "$CONFFILE" 2>/dev/null | "$GREP" -ve '^#' | \
"$GREP" -e " $1:" | "$TR" -d '\r')
# If multiple the last one to consider.
value=$(echo -e "$value" | "$TAIL" -n1)
# Right side of the equal sign W/O leading and trailing spaces and quotes.
value=$(echo -ne "$value" | "$CUT" -d':' -f2 | "$XARGS")
echo -e "$value"; return
}
# Examines the YMLFILE.
CONFFILE="$BASE_DIR/$YMLFILE"
# Gets the list of folders to dump and makes the path relative (some sanitization).
# Sets the app's root if no folders given.
FOLDERS=""
for folder in $(parse "DS_BACKUP_FOLDERS") ''; do [[ -n "$folder" ]] && FOLDERS+=" ./$folder"; done
[[ -z "$FOLDERS" ]] && FOLDERS="."
# Gets the list of excludes as well. Converts them to tar parameters.
EXCLUDES=""
for exclude in $(parse "DS_BACKUP_EXCLUDES") ''; do [[ -n "$exclude" ]] && EXCLUDES+="--exclude='./$exclude' "; done
# We've folders and excludes prepared.
# Tries the FS backup.
if [ -w "$BACKUPDIR" ]; then
BACKUP_NAME="storage.$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME")"
"$DOCKER" exec $NDCONTAINER sh \
-c "cd $APPSDIR; tar $EXCLUDES -cz $FOLDERS" \
> "$BACKUPDIR/$BACKUP_NAME.tgz" 2>>"$BACKUPDIR/$BACKUP_NAME.log"
fi
# That's all, Folks! :)

View File

@ -1 +0,0 @@
../storage/volumes/filebrowser_data

View File

@ -1,28 +0,0 @@
# Static website with Filebrowser as an admin tool.
#
services:
# https://github.com/filebrowser/filebrowser
# https://hub.docker.com/r/filebrowser/filebrowser
filebrowser:
image: filebrowser/filebrowser:latest
restart: unless-stopped
# Take care a possible public port collision.
ports:
- 8201:80
# The same Linux user running the reverse proxy webserver.
user: 33:1001
environment:
TZ: Europe/Budapest
# Default credentials: admin/admin
# Note, FB_NOAUTH only matters if the database is still empty.
#FB_NOAUTH: true
FB_BASEURL: "/"
volumes:
# The Linux user defined above must have R/W access here.
- ./storage/volumes/staticweb:/srv
- ./storage/volumes/filebrowser_data/filebrowser.json:/.filebrowser.json
- ./storage/volumes/filebrowser_data/database.db:/.database.db
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
com.centurylinklabs.watchtower.enable: true

View File

@ -1,8 +0,0 @@
{
"port": 80,
"baseURL": "/",
"address": "",
"log": "stdout",
"database": "/.database.db",
"root": "/srv"
}

View File

@ -1,10 +0,0 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test page</title>
</head>
<body>
<h1>It works!</h1>
</body>
</html>

View File

@ -1,93 +0,0 @@
#!/bin/bash
#
# A service script to backup the web storage of a static website.
# Creates a tarball in $BASE_DIR/storage/backups/tarballs folder
# (by default). Optional parameters may change the source and/or
# target folder.
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-01-22 Initial version.
# Accepted environment variables and their defaults.
#
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_BACKUPDIR=${PAR_BACKUPDIR:-""} # Folder to dump within
PAR_SOURCEDIR=${PAR_SOURCEDIR:-""} # Folder to save
# Messages (maybe overridden by configuration).
#
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_NONREAD="The source directory isn't readable"
MSG_NONWRITE="The target directory isn't writable"
# Other initialisations.
#
BACKUPDIR="storage/backups/tarballs" # Folder to dump within
SOURCEDIR="storage/volumes/staticweb" # Folder to backup
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml"
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname hostname pwd tar
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Searches the base folder, containing a docker-compose.yml file.
#
# Called from the base folder (./)?
BASE_DIR="$PAR_BASEDIR"
TEST_DIR="$SCRPATH"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# On failure gives it up here.
if [ -z "$BASE_DIR" -o ! -r "$BASE_DIR/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute paths.
BACKUPDIR="${PAR_BACKUPDIR:-$BASE_DIR/$BACKUPDIR}"
SOURCEDIR="${PAR_SOURCEDIR:-$BASE_DIR/$SOURCEDIR}"
# The dump target folder must be writable.
#
[[ ! -w "$BACKUPDIR" ]] \
&& echo "$MSG_NONWRITE: $BACKUPDIR" >&2 && exit 1
# The source folder must be readable.
#
[[ ! -r "$SOURCEDIR" ]] \
&& echo "$MSG_NONREAD: $SOURCEDIR" >&2 && exit 1
# Tries the FS backup.
#
if [ -w "$BACKUPDIR" ]; then
BACKUP_NAME=$("$BASENAME" "$SOURCEDIR").$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME")
(cd $SOURCEDIR; "$TAR" cz . \
> "$BACKUPDIR/$BACKUP_NAME.tgz" 2>>"$BACKUPDIR/$BACKUP_NAME.log")
fi
# That's all, Folks! :)

View File

@ -1,46 +0,0 @@
# WonderCMS Flat File application with official Apache 2.4.x and PHP 8.x.
#
# This recipe doesn't extend the official image, it keeps all necessary
# modifications in the container. The application itself must be added
# with a persistent volume.
#
# Based on https://github.com/robiso/docker-wondercms/
#
services:
# https://github.com/WonderCMS/wondercms
# https://github.com/robiso/docker-wondercms/
# https://hub.docker.com/_/php
wondercms:
image: php:8-apache
restart: unless-stopped
# Take a look the possible public port collision.
ports:
- 8201:80
volumes:
- /etc/localtime:/etc/localtime:ro
# Needs R/W for UID 33 (www-data).
- ./storage/volumes/wonder_html/:/var/www/html/
environment:
TZ: Europe/Budapest
# We don't want to extend the official image to maintain
# watchtower's monitoring for updates. So we use CMD to
# make all the necessary changes. Unfortunately this will
# slightly prolong the start of the service.
command:
- /bin/bash
- -c
- |
DEBIAN_FRONTEND=noninteractive apt update
apt install -y libzip-dev zip
apt clean
rm -rf /var/lib/apt/lists/*
docker-php-ext-configure zip
docker-php-ext-install zip
a2enmod rewrite
cp -p /usr/local/etc/php/php.ini-production /usr/local/etc/php/conf.d/php.ini
apache2-foreground
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
com.centurylinklabs.watchtower.enable: true

View File

@ -1,8 +0,0 @@
Options -Indexes
ServerSignature Off
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ index.php?page=$1 [QSA,L]
RewriteRule database.js - [F]
RewriteRule cache.json - [F]

File diff suppressed because it is too large Load Diff

View File

@ -1,557 +0,0 @@
@font-face {
font-family: 'Catamaran';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url('fonts/catamaran-v7-latin-ext_latin-regular.woff2') format('woff2')
}
@font-face {
font-family: 'Catamaran';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url('fonts/catamaran-v7-latin-ext_latin-700.woff2') format('woff2')
}
@font-face {
font-family: 'Catamaran';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url('fonts/catamaran-v7-latin-ext_latin-900.woff2') format('woff2')
}
html, body, div, span, applet, object,
iframe, h1, h2, h3, h4, h5, h6, p, blockquote,
pre, a, abbr, acronym, address, big, cite,
code, del, dfn, em, img, ins, kbd, q, s, samp, strike, strong, sub, sup, tt, var, b,
u, i, center, dl, dt, dd, li, fieldset,
form, label, legend, caption,
tfoot, article, aside,
canvas, details, embed, figure, figcaption,
footer, header, hgroup, menu, nav, output, ruby,
section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline
}
html, body {
box-shadow: 0 0 200px rgba(0, 0, 0, 0.27) inset;
background-image: -webkit-linear-gradient(45deg, rgb(102, 95, 238) 0%, rgb(39, 194, 222) 100%);
min-height: 100%;
font-family: 'Catamaran';
color: #fff !important
}
.actions li {
list-style: none
}
input::-moz-focus-inner {
border: 0;
padding: 0
}
/* Basic */
html {
box-sizing: border-box
}
*, *:before, *:after {
box-sizing: inherit
}
/* Type */
body, select, textarea {
color: rgba(255, 255, 255, 0.8);
font-size: 16.5pt;
font-weight: normal;
line-height: 1.75
}
@media screen and (max-width: 1680px) {
body, input, select, textarea {
font-size: 13pt
}
}
@media screen and (max-width: 1280px) {
body, input, select, textarea {
font-size: 12pt
}
}
@media screen and (max-width: 360px) {
body, input, select, textarea {
font-size: 11pt
}
}
a {
-moz-transition: color 0.2s ease, border-bottom-color 0.2s ease;
-webkit-transition: color 0.2s ease, border-bottom-color 0.2s ease;
-ms-transition: color 0.2s ease, border-bottom-color 0.2s ease;
transition: color 0.2s ease, border-bottom-color 0.2s ease;
border-bottom: dotted 1px rgba(255, 255, 255, 0.35);
color: inherit;
text-decoration: none
}
a:hover {
border-bottom: solid 1px rgba(255, 255, 255, 0.88);
color: #ffffff
}
strong, b {
color: #ffffff;
font-weight: bold
}
em, i {
font-style: italic
}
p {
margin: 0 0 2em 0
}
h1, h2, h3, h4, h5, h6 {
color: #ffffff;
font-weight: bold;
line-height: 1.5
}
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
color: inherit;
text-decoration: none
}
h1 {
font-size: 2.75em
}
h2 {
font-size: 1.75em
}
h3 {
font-size: 1.1em
}
h4 {
font-size: 1em
}
h5 {
font-size: 0.8em
}
h6 {
font-size: 0.6em
}
@media screen and (max-width: 736px) {
h1 {
font-size: 3em
}
h2 {
font-size: 1.75em
}
h3 {
font-size: 1em
}
h4 {
font-size: 0.8em
}
h5 {
font-size: 0.6em
}
h6 {
font-size: 0.6em
}
}
code {
background: rgba(255, 255, 255, 0.05);
border-radius: 0.25em;
border: solid 1px rgba(255, 255, 255, 0.15);
font-family: "Courier New", monospace;
font-size: 0.9em;
margin: 0 0.25em;
padding: 0.25em 0.65em
}
pre {
-webkit-overflow-scrolling: touch;
font-family: "Courier New", monospace;
font-size: 0.9em;
margin: 0 0 2em 0
}
pre code {
display: block;
line-height: 1.75em;
padding: 1em 1.5em;
overflow-x: auto
}
.text-center {
text-align: center
}
/* Button */
input[type="button"],
button,
.button {
-moz-appearance: none;
-webkit-appearance: none;
-ms-appearance: none;
appearance: none;
-moz-transition: border-color 0.2s ease;
-webkit-transition: border-color 0.2s ease;
-ms-transition: border-color 0.2s ease;
transition: border-color 0.2s ease;
background-color: #fff;
border: solid 1px !important;
border-color: rgba(255, 255, 255, 0.15) !important;
border-radius: 3em;
color: #393939 !important;
cursor: pointer;
display: inline-block;
font-size: 0.7em;
font-weight: bold;
letter-spacing: 0.25em;
line-height: 4.75em;
outline: 0;
padding: 0 3.75em;
position: relative;
text-align: center;
text-decoration: none;
text-transform: uppercase;
white-space: nowrap
}
input[type="button"]:after,
button:after,
.button:after {
-moz-transform: scale(0.25);
-webkit-transform: scale(0.25);
-ms-transform: scale(0.25);
transform: scale(0.25);
pointer-events: none;
-moz-transition: opacity 0.2s ease, -moz-transform 0.2s ease;
-webkit-transition: opacity 0.2s ease, -webkit-transform 0.2s ease;
-ms-transition: opacity 0.2s ease, -ms-transform 0.2s ease;
transition: opacity 0.2s ease, transform 0.2s ease;
background: #ffffff;
border-radius: 3em;
content: '';
height: 100%;
left: 0;
opacity: 0;
position: absolute;
top: 0;
width: 100%
}
input[type="button"]:hover,
button:hover,
.button:hover {
border-color: rgba(255, 255, 255, 0.6) !important
}
input[type="button"]:hover:after,
button:hover:after,
.button:hover:after {
opacity: 0.05;
-moz-transform: scale(1);
-webkit-transform: scale(1);
-ms-transform: scale(1);
transform: scale(1)
}
input[type="button"]:hover:active,
button:hover:active,
.button:hover:active {
border-color: #ffffff !important
}
input[type="button"]:hover:active:after,
button:hover:active:after,
.button:hover:active:after {
opacity: 0.1
}
input[type="password"] {
border: 0;
outline: 0;
padding: 15px;
border-radius: 10px;
width: 300px
}
/* Wrapper */
.wrapper {
position: relative
}
.wrapper > .inner {
width: 100%;
padding: 5em 4em 2em 4em
}
@media screen and (max-width: 1680px) {
footer > .inner {
padding: 2em 4em 2em 4em !important
}
}
@media screen and (max-width: 736px) {
.wrapper > .inner {
padding: 2em 2em 2em 2em
}
footer > .inner {
padding: 2em 2em 2em 2em !important
}
}
.wrapper.style2 {
background-color: #5052b5
}
.wrapper.fullscreen {
min-height: calc(87vh - 2.5em)
}
@media screen and (max-width: 736px) {
.wrapper.fullscreen {
min-height: calc(40vh - 5.5em)
}
}
/* Wrapper */
#topMenu + #wrapper {
margin-left: 0;
position: relative
}
@media screen and (max-width: 736px) {
#topMenu + #wrapper {
padding-top: 0;
top: 2em
}
}
#header + #wrapper > .wrapper > .inner {
margin: 0 auto
}
/* Menu */
#topMenu {
padding: 0;
background:0;
cursor: default;
height: 5.4em;
left: 0;
text-align: center;
top: 0;
width: 100%;
line-height: 3.5em;
position: relative;
z-index: 20
}
#topMenu > .inner {
display: -moz-flex;
display: -webkit-flex;
display: -ms-flex;
display: flex;
-moz-flex-direction: row;
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-moz-justify-content: center;
-webkit-justify-content: center;
-ms-justify-content: center;
justify-content: center;
-moz-transform: translateY(0);
-webkit-transform: translateY(0);
-ms-transform: translateY(0);
transform: translateY(0);
-moz-transition: opacity 1s ease;
-webkit-transition: opacity 1s ease;
-ms-transition: opacity 1s ease;
transition: opacity 1s ease;
min-height: 100%;
opacity: 1;
width: 100%
}
#topMenu nav {
height: inherit;
line-height: inherit;
margin-top: 1em
}
#topMenu nav ul {
display: -moz-flex;
display: -webkit-flex;
display: -ms-flex;
display: flex;
height: inherit;
line-height: inherit;
list-style: none;
padding: 0
}
#topMenu nav a {
height: inherit;
line-height: inherit;
padding: 0
}
#topMenu nav > ul > li {
margin: 0 1em 0 1em;
opacity: 1;
padding: 0;
position: relative;
height: inherit;
line-height: inherit
}
#topMenu nav a {
border: 0;
font-size: 0.70em;
font-weight: bold;
letter-spacing: 0.25em;
line-height: 1.75;
outline: 0;
padding: 2em 0;
position: relative;
text-decoration: none;
text-transform: uppercase
}
#topMenu nav li.active, nav li.active a {
color: #fff !important
}
#topMenu nav .active a{
border-bottom: 1px solid #ffffff7d
}
#topMenu nav a:hover {
border-bottom: 1px solid #ffffff59
}
#topMenu nav a.active {
color: #ffffff
}
#topMenu nav a.active:after {
max-width: 100%
}
@media screen and (max-width: 736px) {
#topMenu {
height: auto;
font-size: 0.94em;
position: relative;
background-color: rgba(0, 0, 0, 0.30);
padding-bottom: 20px
}
#topMenu nav ul {
display: block;
float: left
}
#topMenu nav > ul > li {
display: block;
float: left;
margin: 0 1em 0 2em
}
#topMenu nav .active a {
border-bottom: 1px solid #fff
}
footer {
font-size: 1em
}
}
/* Intro */
#intro p {
font-size: 1.25em
}
@media screen and (max-width: 736px) {
#intro p {
font-size: 1em
}
}
/* Footer */
footer {
text-align: right
}
/* Submenus */
.subPageDropdown a {
border: 0 !important
}
.subPageDropdown ul {
margin: 0;
padding-left: 0
}
.subPageDropdown li {
color: #fff;
display: block;
float: left;
position: relative;
padding: 0 1em 0 1em;
text-decoration: none;
transition-duration: 0.5s
}
#topMenu li a {
color: rgba(255, 255, 255, 0.8)
}
#topMenu li:hover,
#topMenu li:focus-within {
cursor: pointer
}
#topMenu li:focus-within a {
outline: none
}
#topMenu .nav-item {
margin-top: 5px
}
ul.subPageDropdown {
visibility: hidden;
opacity: 0;
position: absolute;
margin-top: 10px;
display: none;
padding-left: 10px !important
}
#topMenu ul li:hover > ul,
#topMenu ul li:focus-within > ul,
#topMenu ul li ul:hover,
#topMenu ul li ul:focus {
visibility: visible;
opacity: 1;
display: block
}
#topMenu ul li ul li {
clear: both;
text-align: left;
background-color: rgba(0, 0, 0, 0.30);
white-space: nowrap
}
/* Submenus dropdown arrow */
.menu li > a:after {
content: ' ▼';
font-weight: bold
}
.menu > li > a:after {
content: ' ▼';
font-weight: bold
}
.menu li > a:only-child:after {
content: ''
}

View File

@ -1,84 +0,0 @@
<?php global $Wcms ?>
<!DOCTYPE html>
<html lang="<?= $Wcms->getSiteLanguage() ?>">
<head>
<!-- Encoding, browser compatibility, viewport -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- Search Engine Optimization (SEO) -->
<meta name="title" content="<?= $Wcms->get('config', 'siteTitle') ?> - <?= $Wcms->page('title') ?>" />
<meta name="description" content="<?= $Wcms->page('description') ?>">
<meta name="keywords" content="<?= $Wcms->page('keywords') ?>">
<meta property="og:url" content="<?= $this->url() ?>" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="<?= $Wcms->get('config', 'siteTitle') ?>" />
<meta property="og:title" content="<?= $Wcms->page('title') ?>" />
<meta name="twitter:site" content="<?= $this->url() ?>" />
<meta name="twitter:title" content="<?= $Wcms->get('config', 'siteTitle') ?> - <?= $Wcms->page('title') ?>" />
<meta name="twitter:description" content="<?= $Wcms->page('description') ?>" />
<!-- Website and page title -->
<title>
<?= $Wcms->get('config', 'siteTitle') ?> - <?= $Wcms->page('title') ?>
</title>
<!-- Admin CSS -->
<?= $Wcms->css() ?>
<!-- Theme CSS -->
<link rel="stylesheet" rel="preload" as="style" href="<?= $Wcms->asset('css/style.css') ?>">
</head>
<body>
<!-- Admin settings panel and alerts -->
<?= $Wcms->settings() ?>
<?= $Wcms->alerts() ?>
<section id="topMenu">
<div class="inner">
<nav>
<ul class="menu">
<!-- Menu -->
<?= $Wcms->menu() ?>
</ul>
</nav>
</div>
</section>
<div id="wrapper">
<section id="intro" class="wrapper style1 fullscreen">
<div class="inner">
<!-- Main content for each page -->
<?= $Wcms->page('content') ?>
</div>
</section>
<section class="wrapper style2">
<div class="inner">
<!-- Static editable block, same on each page -->
<?= $Wcms->block('subside') ?>
</div>
</section>
</div>
<footer class="wrapper style2">
<div class="inner">
<!-- Footer -->
<?= $Wcms->footer() ?>
</div>
</footer>
<!-- Admin JavaScript. More JS libraries can be added below -->
<?= $Wcms->js() ?>
</body>
</html>

View File

@ -1,13 +0,0 @@
{
"version": 1,
"themes": {
"sky": {
"name": "Sky",
"repo": "https://github.com/robiso/sky/tree/master",
"zip": "https://github.com/robiso/sky/archive/master.zip",
"summary": "Default WonderCMS theme (2022). Theme works without Bootstrap and jQuery.",
"version": "3.2.4",
"image": "https://raw.githubusercontent.com/robiso/sky/master/preview.jpg"
}
}
}

View File

@ -1,125 +0,0 @@
#!/bin/bash
#
# A service script to backup the entire WonderCMS website.
# Creates a tarball in $BASE_DIR/storage/backups/tarballs folder
# (by default). An optional parameter may change the target folder.
#
# Call as a Docker manager user (member of the docker Linux group) via cron.
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-03-05 v0.2
# mod: gitbackup handling stub has been temporarily removed.
# 2025-01-14 v0.1 Initial version.
# Accepted environment variables and their defaults.
#
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_BACKUPDIR=${PAR_BACKUPDIR:-""} # Folder to dump within
# Messages (maybe overridden by configuration).
#
MSG_DOCKERGRPNEED="You must be a member of the docker group."
MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file"
MSG_NONWRITE="The target directory isn't writable"
MSG_NOLOCATE="Cannot locate the WonderCMS container."
# Other initialisations.
#
BACKUPDIR="storage/backups/tarballs" # Folder to dump within
GITBACKUP="storage_gitbackup.sh" # Git backup utility
SERVICENAME="wondercms" # The composed WonderCMS service
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml"
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname docker \
find grep hostname id pwd tail xargs
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
#
# Let's find which version of docker-compose is installed.
if [ $($DOCKER compose version 2>&1 >/dev/null; echo $?) -eq 0 ]; then
# We'll use v2 if it is available.
DOCKER_COMPOSE="$DOCKER"
commandstring="compose"
else
# Otherwise falling back to v1.
DOCKER_COMPOSE="$(which docker-compose)"
commandstring=""
fi
# One of the two is mandatory.
if [ -z "$DOCKER_COMPOSE" ];then echo "$MSG_MISSINGDEP docker-compose" >&2; exit 1; fi
# Below docker-compose should be called as "$DOCKER_COMPOSE" $commandstring sequence.
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Need to be root or a Docker manager user.
#
[[ "$USER" != 'root' ]] \
&& [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \
&& echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #"
# Searches the base folder, containing a docker-compose.yml file.
#
# Called from the base folder (./)?
BASE_DIR="$PAR_BASEDIR"
TEST_DIR="$SCRPATH"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$BASE_DIR" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && BASE_DIR="$TEST_DIR"
# On failure gives it up here.
if [ -z "$BASE_DIR" -o ! -r "$BASE_DIR/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute paths.
BACKUPDIR="${PAR_BACKUPDIR:-$BASE_DIR/$BACKUPDIR}"
# The dump target folder must be writable.
#
[[ ! -w "$BACKUPDIR" ]] \
&& echo "$MSG_NONWRITE: $BACKUPDIR" >&2 && exit 1
# The service must be running - silently gives up here if not.
#
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1
# Converts the service name to an actual running container's name.
#
MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps -q "$SERVICENAME") | "$CUT" -c2-)"
# Gives up here if failed.
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Tries the FS backup.
if [ -w "$BACKUPDIR" ]; then
BACKUP_NAME=$MYCONTAINER.$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME")
"$DOCKER" exec $MYCONTAINER sh \
-c "cd /var/www/html; tar cz ." \
> "$BACKUPDIR/$BACKUP_NAME.tgz" 2>>"$BACKUPDIR/$BACKUP_NAME.log"
fi
# That's all, Folks! :)

View File

@ -1,5 +1,6 @@
# Wordpress with MariaDB # Wordpress with MariaDB
# #
#version: '3'
services: services:
# https://hub.docker.com/_/wordpress # https://hub.docker.com/_/wordpress
# https://github.com/docker-library/docs/tree/master/wordpress # https://github.com/docker-library/docs/tree/master/wordpress

View File

@ -1,8 +1,8 @@
#!/bin/bash #!/bin/bash
# #
# A service script to backup the docker-composed MySQL/MariaDB database. # A service script to backup the docker-composed WordPress instance.
# Dumps database to the $BASE_DIR/storage/backups/dumps folder (by default). # Dumps the MySQL/MariaDB database to the $BASE_DIR/storage/backups/dumps
# An optional parameter may change the target folder. # folder (by default). An optional parameter may change the target folder.
# #
# This script gets the database credentials from the docker-compose.yml file # This script gets the database credentials from the docker-compose.yml file
# and calls the mysql_dumpdb worker script which should be installed in # and calls the mysql_dumpdb worker script which should be installed in
@ -10,11 +10,8 @@
# #
# Call as a Docker manager user (member of the docker Linux group) via cron. # Call as a Docker manager user (member of the docker Linux group) via cron.
# #
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu> # Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html # License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-02-26 v0.3
# mod: doesn't tied to a particular composition (Mediawiki, Wordpress, etc).
# 2024-12-01 v0.2.1 # 2024-12-01 v0.2.1
# fix: typo in docker-compose version detection. # fix: typo in docker-compose version detection.
# 2024-08-25 v0.2 # 2024-08-25 v0.2
@ -25,7 +22,6 @@
# #
PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder PAR_BASEDIR=${PAR_BASEDIR:-""} # Service's base folder
PAR_DUMPDIR=${PAR_DUMPDIR:-""} # Folder to dump within PAR_DUMPDIR=${PAR_DUMPDIR:-""} # Folder to dump within
PAR_SERVICE=${PAR_SERVICE:-"database"} # Service's name in composition
# Messages (maybe overridden by configuration). # Messages (maybe overridden by configuration).
# #
@ -115,7 +111,7 @@ DUMPDIR="${PAR_DUMPDIR:-$BASE_DIR/$DUMPDIR}"
[[ ! -w "$DUMPDIR" ]] \ [[ ! -w "$DUMPDIR" ]] \
&& echo "$MSG_NONWRITE: $DUMPDIR" >&2 && exit 1 && echo "$MSG_NONWRITE: $DUMPDIR" >&2 && exit 1
# The composition must be running - silently gives up here if not. # The service must be running - silently gives up here if not.
# #
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \ [[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1 && exit 1
@ -139,14 +135,14 @@ function parse { [[ -z "$1" ]] && return
echo -e "$value"; return echo -e "$value"; return
} }
# All parameters are mandatories. # All parameters are mandatories.
MYCONTAINER="$PAR_SERVICE" # TODO: guess from the yml MYCONTAINER="$(parse "WORDPRESS_DB_HOST")"
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM PAR_SERVICE" >&2; exit 1; fi1; fi if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM WORDPRESS_DB_HOST" >&2; exit 1; fi
MYDATABASE="$(parse "MYSQL_DATABASE")" MYDATABASE="$(parse "WORDPRESS_DB_NAME")"
if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM MYSQL_DATABASE" >&2; exit 1; fi if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM WORDPRESS_DB_NAME" >&2; exit 1; fi
MYUSER="$(parse "MYSQL_USER")" MYUSER="$(parse "WORDPRESS_DB_USER")"
if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM MYSQL_USER" >&2; exit 1; fi if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM WORDPRESS_DB_USER" >&2; exit 1; fi
MYPASSWORD="$(parse "MYSQL_PASSWORD")" MYPASSWORD="$(parse "WORDPRESS_DB_PASSWORD")"
if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM MYSQL_PASSWORD" >&2; exit 1; fi if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM WORDPRESS_DB_PASSWORD" >&2; exit 1; fi
# We've the configuration parsed. # We've the configuration parsed.
# Converts the database service name to an actual running container's name. # Converts the database service name to an actual running container's name.

View File

@ -9,8 +9,6 @@
# #
# Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu> # Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html # License: GNU/GPL 3+ https://www.gnu.org/licenses/gpl-3.0.en.html
# 2025-03-05 v0.2.1
# mod: reworded some comments and renamed a variable.
# 2024-08-25 v0.2 # 2024-08-25 v0.2
# new: docker-compose v2 compatibility - tested with Ubuntu 24.04 LTS. # new: docker-compose v2 compatibility - tested with Ubuntu 24.04 LTS.
# 2021-10-19 v0.1 Initial version. # 2021-10-19 v0.1 Initial version.
@ -27,7 +25,7 @@ MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency" MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file" MSG_MISSINGYML="Fatal: didn't find the docker-compose.yml file"
MSG_NONWRITE="The target directory isn't writable" MSG_NONWRITE="The target directory isn't writable"
MSG_NOLOCATE="Cannot locate the service container." MSG_NOLOCATE="Cannot locate the Mediawiki container."
# Other initialisations. # Other initialisations.
# #
@ -110,16 +108,16 @@ BACKUPDIR="${PAR_BACKUPDIR:-$BASE_DIR/$BACKUPDIR}"
[[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \ [[ -z "$(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1 && exit 1
# Converts the service name to an actual running container's name. # Converts the WordPress service name to an actual running container's name.
# #
MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandline ps -q "$SERVICENAME") | "$CUT" -c2-)" WPCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$BASE_DIR"; "$DOCKER_COMPOSE" $commandline ps -q "$SERVICENAME") | "$CUT" -c2-)"
# Gives up here if failed. # Gives up here if failed.
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi if [ -z "$WPCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Tries the FS backup. # Tries the FS backup.
if [ -w "$BACKUPDIR" ]; then if [ -w "$BACKUPDIR" ]; then
BACKUP_NAME=$MYCONTAINER.$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME") BACKUP_NAME=$WPCONTAINER.$("$DATE" '+%Y%m%d_%H%M%S').$("$HOSTNAME")
"$DOCKER" exec $MYCONTAINER sh \ "$DOCKER" exec $WPCONTAINER sh \
-c "cd /var/www/html; tar cz ." \ -c "cd /var/www/html; tar cz ." \
> "$BACKUPDIR/$BACKUP_NAME.tgz" 2>>"$BACKUPDIR/$BACKUP_NAME.log" > "$BACKUPDIR/$BACKUP_NAME.tgz" 2>>"$BACKUPDIR/$BACKUP_NAME.log"
fi fi

View File

@ -1,195 +0,0 @@
#!/bin/bash
#
# Restores a composed MySQL/MariaDB database from a dump file.
# Gets all necessary data from the docker-compose.yml file.
#
# This is a wrapper script to the system-wide mysql_restoredb tool.
# Database recovey with the necessary user management and grants
# requires superuser privileges in MySQL, but simple data recovery
# is possible if the user and privileges are already set.
#
# You have to call this script as a Docker manager user (member of the
# 'docker' Linux group). The worker tool must be available somewhere
# in PATH. At least 5.7.6 MySQL or at least 10.1.3 MariaDB is required.
#
# Usage:
# $0 path_to_the_dumpfile [ path_to_the_service's_base ]
#
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html)
#
# 2025-02-26 v0.1 Forked from the Smartfront repository and rewritten.
# Accepted environment variables and their defaults.
#
PAR_SERVICE=${SERVICE:-"database"} # Database container's name
# Other initialisations.
#
BACKUPFOLDER="storage/backups/dumps" # Skeleton's default dump folder
PROP_DBAPASS="MYSQL_ROOT_PASSWORD" # DB admin password property
PROP_DBNAME="MYSQL_DATABASE" # DB name property
PROP_DBPASS="MYSQL_PASSWORD" # DB password property
PROP_DBUSER="MYSQL_USER" # DB username property
USER=${USER:-LOGNAME} # Fix for cron enviroment only
YMLFILE="docker-compose.yml"
# Basic environment settings.
#
LANG=C
LC_ALL=C
# Messages.
#
MSG_BADDUMP="Fatal: doesn't exist or doesn't a dumpfile:"
MSG_DOCKERGRPNEED="You must be a member of the docker group."
MSG_DOESNOTRUN="This service doesn't run."
MSG_MISSINGDEP="Fatal: missing dependency"
MSG_MISSINGCONF="Fatal: missing config file"
MSG_MISSINGYML="Fatal: didn't find the $YMLFILE file"
MSG_NOLOCATE="Cannot locate the database container."
MSG_NOPARAM="Missing environment parameter"
MSG_USAGE="Usage: $0 dump_pathname [ composition_base_pathname ]\n"
MSG_USAGE+="ENVVAR:\n"
MSG_USAGE+="SERVICE \tDatabase service's name in composition\n"
# Checks the dependencies.
#
TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
for item in basename cat cut date dirname docker \
grep id mysql_restoredb readlink tail xargs
do
if [ -n "$(which $item)" ]
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]' | "$TR" '-' '_')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done
# All dependencies are available via "$THECOMMAND" (upper case) call.
#
# Let's find which version of docker-compose is installed.
if [ $($DOCKER compose version >/dev/null 2>&1; echo $?) -eq 0 ]; then
# We'll use v2 if it is available.
DOCKER_COMPOSE="$DOCKER"
commandstring="compose"
else
# Otherwise falling back to v1.
DOCKER_COMPOSE="$(which docker-compose)"
commandstring=""
fi
# One of the two is mandatory.
if [ -z "$DOCKER_COMPOSE" ];then echo "$MSG_MISSINGDEP docker-compose" >&2; exit 1; fi
# Below docker-compose should be called as "$DOCKER_COMPOSE" $commandstring sequence.
# Where I'm?
# https://gist.github.com/TheMengzor/968e5ea87e99d9c41782
SOURCE="$0"
while [ -h "$SOURCE" ]; do
# resolve $SOURCE until the file is no longer a symlink
SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
SOURCE="$("$READLINK" "$SOURCE")"
# if $SOURCE was a relative symlink, we need to resolve it
# relative to the path where the symlink file was located
[[ $SOURCE != /* ]] && SOURCE="$SCRPATH/$SOURCE"
done; SCRPATH="$( cd -P "$("$DIRNAME" "$SOURCE" )" && echo "$PWD" )" #"
# Need to be root or a Docker manager user.
#
[[ "$USER" != 'root' ]] \
&& [[ -z "$(echo "$("$ID" -Gn "$USER") " | "$GREP" ' docker ')" ]] \
&& echo "$MSG_DOCKERGRPNEED" >&2 && exit 1 #"
# Gets the command line parameters.
#
# DUMPFILE is mandatory
if [ -n "$1" ]; then DUMPFILE="$1"; shift
else echo -e "$MSG_USAGE" >&2; exit 1; fi
# SERVICE_BASE is optional
if [ -n "$1" ]; then SERVICE_BASE="$1"; shift; fi
# We've read the unchecked command line parameters.
# Searches the base folder, containing the YMLFILE.
#
if [ -z "$SERVICE_BASE" ]; then
# Called from the base folder (./)?
TEST_DIR="$SCRPATH"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
# Called from ./tools?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
# Called from ./tools/*.d?
TEST_DIR="$("$DIRNAME" "$TEST_DIR")"
[[ -z "$SERVICE_BASE" ]] && [[ -r "$TEST_DIR/$YMLFILE" ]] && SERVICE_BASE="$TEST_DIR"
fi
# On failure gives it up here.
if [ -z "$SERVICE_BASE" -o ! -r "$SERVICE_BASE/$YMLFILE" ]; then
echo "$MSG_MISSINGYML" >&2; exit 1
fi
# Sets the absolute path.
YMLFILE="$SERVICE_BASE/$YMLFILE"
# We've the YMLFILE.
# Finds the DUMPFILE to use.
#
# The DUMPFILE must point to a readable file.
# If doesn't it tries the skeleton's standard backup folder as well.
if [ ! -r "$DUMPFILE" ]
then DUMPFILE="$("$DIRNAME" "$SERVICE_BASE")/$BACKUPFOLDER/$DUMPFILE"; fi
# If it is an existing symlink dereferences it to ensure, it points to a file.
if [ -h "$DUMPFILE" ]; then
if [[ "$("$READLINK" "$DUMPFILE")" != /* ]]
# relative path in symlink
then DUMPFILE="$("$DIRNAME" "$DUMPFILE")/$("$READLINK" "$DUMPFILE")"
# absolute path in symlink
else DUMPFILE="$("$READLINK" "$DUMPFILE")"; fi
fi
# Let's check it!
if [ ! -r "$DUMPFILE" -o ! -f "$DUMPFILE" ]
then echo -e "$MSG_BADDUMP $DUMPFILE"; exit 1; fi
# We've an existing dumpfile.
# The composition must be running - silently gives up here if not.
#
[[ -z "$(cd "$SERVICE_BASE"; "$DOCKER_COMPOSE" $commandstring ps --services --filter "status=running")" ]] \
&& exit 1
# Parses the YMLFILE for parameters to use.
#
function parse { [[ -z "$1" ]] && return
# Gets the live lines containing the parameter.
value=$("$CAT" "$YMLFILE" | "$GREP" -ve '^#' | \
"$GREP" -e "^ *$1" | "$TR" -d '\r')
# If multiple the last one to consider.
value=$(echo -e "$value" | "$TAIL" -n1)
# Right side of the colon W/O leading and trailing spaces and quotes.
value=$(echo -ne "$value" | "$CUT" -d':' -f2 | "$XARGS")
# Removes the trailing semicolon (if any).
value=${value%;*}
echo -e "$value"; return
}
# These parameters are mandatory.
MYCONTAINER="$PAR_SERVICE" # TODO: guess from the yml
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOPARAM PAR_SERVICE" >&2; exit 1; fi1; fi
MYDATABASE="$(parse "$PROP_DBNAME")"
if [ -z "$MYDATABASE" ]; then echo "$MSG_NOPARAM $PROP_DBNAME" >&2; exit 1; fi
MYUSER="$(parse "$PROP_DBUSER")"
if [ -z "$MYUSER" ]; then echo "$MSG_NOPARAM $PROP_DBUSER" >&2; exit 1; fi
MYPASSWORD="$(parse "$PROP_DBPASS")"
if [ -z "$MYPASSWORD" ]; then echo "$MSG_NOPARAM $PROP_DBPASS" >&2; exit 1; fi
# These are optional.
MYDBAUSER="root"
MYDBAPASSWORD="$(parse "$PROP_DBAPASS")"
# We've the configuration parsed.
# Converts the database service name to an actual running container's name.
#
MYCONTAINER="$("$DOCKER" inspect -f '{{.Name}}' $(cd "$SERVICE_BASE"; "$DOCKER_COMPOSE" $commandstring ps -q "$MYCONTAINER") | "$CUT" -c2-)"
# Gives up here if failed.
if [ -z "$MYCONTAINER" ]; then echo "$MSG_NOLOCATE" >&2; exit 1; fi
# Calls the worker script to make the job.
#
export MYDBAUSER MYDBAPASSWORD MYPASSWORD
"$MYSQL_RESTOREDB" -C "$MYCONTAINER" -U "$MYUSER" "$MYDATABASE" "$DUMPFILE"
# That's all, Folks! :)

View File

@ -21,13 +21,9 @@
# [-C container] [-d database] [-f dumpfile ] # [-C container] [-d database] [-f dumpfile ]
# [database (if not in -d)] [dumpfile (if not in -f)] # [database (if not in -d)] [dumpfile (if not in -f)]
# #
# Author: Kovács Zoltán <kovacsz@marcusconsulting.hu> # Author: Kovács Zoltán <kovacs.zoltan@smartfront.hu>
# Kovács Zoltán <kovacs.zoltan@smartfront.hu> # Kovács Zoltán <kovacsz@marcusconsulting.hu>
# License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html) # License: GNU/GPL v3+ (https://www.gnu.org/licenses/gpl-3.0.en.html)
# 2025-03-04 v1.1
# new: Works with dockerized databases but hasn't yet been tested with natives.
# mod: Database user creation and grants rewritten. Now create user @'%'
# (instead of @'myhost') if it doesn't already exist.
# 2023-06-18 v1.0 # 2023-06-18 v1.0
# new: forked from the "SMARTERP_skeleton" repository. # new: forked from the "SMARTERP_skeleton" repository.
# 2022-04-07 v0.4 # 2022-04-07 v0.4
@ -50,10 +46,14 @@ MYDBAUSER=${MYDBAUSER:-""} # Database admin superuser
MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user MYDBAPASSWORD=${MYDBAPASSWORD:-""} # Credential for the DBA user
MYDUMP=${MYDUMP-""} # Dump file pathname MYDUMP=${MYDUMP-""} # Dump file pathname
MYHOST=${MYHOST:-"localhost"} # Connection parameter MYHOST=${MYHOST:-"localhost"} # Connection parameter
MYOPTIONS=${MYOPTIONS-""} # Options to pass to pg_dump
MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner MYPASSWORD=${MYPASSWORD-""} # Credential for the DB owner
MYPORT=${MYPORT:-"3306"} # Connection parameter MYPORT=${MYPORT:-"3306"} # Connection parameter
MYUSER=${MYUSER:-"root"} # Owner of the restored DB MYUSER=${MYUSER:-"root"} # Owner of the restored DB
### Temporailly ignored! Need to sanitize.
MYOPTIONS=""
# Basic environment settings. # Basic environment settings.
# #
LANG=C LANG=C
@ -133,21 +133,14 @@ do
;; ;;
esac esac
done; shift $((OPTIND -1)) done; shift $((OPTIND -1))
#
# All options have been processed. # All options have been processed.
# Checks the dependencies. # Checks the dependencies.
# #
# Conditional dependencies (according to native or dockerized environment). # Conditional dependencies (according to native or dockerized environment).
if [ -n "$MYCONTAINER" ]; then [[ -z "$MYCONTAINER" ]] \
# Dockerized && additem="mysql" \
additem="docker" || additem="docker"
else
# Native - MySQL or MariaDB CLI?
if [ -n "$(which mysql)" ]
then additem="mysql"
else additem="mariadb"; fi
fi
# Common dependencies. # Common dependencies.
TR=$(which tr 2>/dev/null) TR=$(which tr 2>/dev/null)
if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi if [ -z "$TR" ]; then echo "$MSG_MISSINGDEP tr."; exit 1 ; fi
@ -158,12 +151,6 @@ do
then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item) then export $(echo $item | "$TR" '[:lower:]' '[:upper:]')=$(which $item)
else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi else echo "$MSG_MISSINGDEP $item." >&2; exit 1; fi
done done
#
# Unifies the call of the clients in a native environment.
if [ -z "$MYCONTAINER" ]; then
if [ -z "$MYSQL" -a -n "$MARIADB" ]; then MYSQL="$MARIADB"; fi
fi
#
# All dependencies are available via "$THECOMMAND" (upper case) call. # All dependencies are available via "$THECOMMAND" (upper case) call.
# Sanitizing the parameters. # Sanitizing the parameters.
@ -197,7 +184,6 @@ fi
# #
[[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \ [[ -n "$MYUSER" ]] && [[ ! "$MYUSER" =~ ^([[:alnum:]]|[.-_\\+])*$ ]] \
&& echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1 && echo -e "$MSG_BADPARAM $MYUSER\n$MSG_USAGE" >&2 && exit 1
#
# We've at least a minimally checked parameters. # We've at least a minimally checked parameters.
# Need to be root or a Docker manager user if the DB runs in a container. # Need to be root or a Docker manager user if the DB runs in a container.
@ -227,7 +213,6 @@ for veto in $vetodatabases ""
do do
[[ "$MYDATABASE" = "$veto" ]] && exit 0 [[ "$MYDATABASE" = "$veto" ]] && exit 0
done done
#
# We've a database name to restore. # We've a database name to restore.
# Determines the dumpfile. # Determines the dumpfile.
@ -249,7 +234,6 @@ fi
# Let's check it! # Let's check it!
if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ] if [ ! -r "$MYDUMPFILE" -o ! -f "$MYDUMPFILE" ]
then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi then echo -e "$MSG_BADDUMP $MYDUMPFILE"; exit 1; fi
#
# We've an existing dumpfile. # We've an existing dumpfile.
# Tries to get the locale settings (actually CHARACTER SET) of this dump. # Tries to get the locale settings (actually CHARACTER SET) of this dump.
@ -268,7 +252,6 @@ if [ -z "$MYCHARSET" ]; then
# Trims the character set's name itself (the first word after the equal sign). # Trims the character set's name itself (the first word after the equal sign).
[[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #' [[ -n "$MYCHARSET" ]] && MYCHARSET=$(echo -e "$MYCHARSET" | "$SED" 's/^.*= \(.*\) .*$/\1/') #'
fi fi
#
# We've a raw guess about the character sets used. # We've a raw guess about the character sets used.
# Finds the LOGFILE to use. # Finds the LOGFILE to use.
@ -279,7 +262,6 @@ fi
&& LOGFILE="${MYDUMPFILE%.gz}" \ && LOGFILE="${MYDUMPFILE%.gz}" \
&& LOGFILE="${LOGFILE%.*}.log" \ && LOGFILE="${LOGFILE%.*}.log" \
|| LOGFILE="/dev/null" || LOGFILE="/dev/null"
#
# We've a suitable logfile. # We've a suitable logfile.
# Opens the log and takes care to close it when finish. # Opens the log and takes care to close it when finish.
@ -292,64 +274,35 @@ function close_log() {
"$TEE" -a "$LOGFILE" "$TEE" -a "$LOGFILE"
} }
trap -- 'close_log' EXIT trap -- 'close_log' EXIT
#
# We started logging. # We started logging.
# Prepopulates two SQL command skeletons (macros). # Prepopulates the SQL command skeleton (macro).
# #
# This skeleton makes the SQL calls independent to the environment # This skeleton makes the SQL calls independent to the environment
# (native or dockerized) and credentials. We need only actualize the # (native or dockerized) and credentials. We need only actualize the
# CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLSTREAM or # CONNECT, DATABASE and SQLVERB clauses then eval $DO_SQLVERB.
# $DO_SQLVERB. Warning: the parameters must be sanitized! # Warning: the parameters must had been sanitized!
# DO_SQLVERB=""
if [ -n "$MYCONTAINER" ]; then DO_SQLVERB+="export MYSQL_PWD; "
# When MySQL runs in the container. DO_SQLVERB+="\"\$MYSQL\" \$CONNECT -N \$DATABASE "
#
if [ -n "$("$DOCKER" exec $MYCONTAINER which mysql)" ]; then
DO_SQLVERB="\"\$DOCKER\" exec -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER mysql "
# When MariaDB runs in the container.
elif [ -n "$("$DOCKER" exec $MYCONTAINER which mariadb)" ]; then
DO_SQLVERB="\"\$DOCKER\" exec -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER mariadb "
DO_SQLSTREAM="\"\$DOCKER\" exec -i -e MYSQL_PWD=\"\$MYSQL_PWD\" \$MYCONTAINER /bin/bash -c \"mariadb "
# Otherwise gives it up here.
else
echo -e "$MSG_BADDBTYPE in $MYCONTAINER." | "$TEE" -a "$LOGFILE" >&2
echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2
exit 1
fi
# Common parameters.
DO_SQLVERB+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE "
DO_SQLVERB+="-e \"\$SQLVERB\"" DO_SQLVERB+="-e \"\$SQLVERB\""
DO_SQLSTREAM+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE \"" # We've a suitable SQL macro.
else
# Native environment.
#
DO_SQLVERB="export \"MYSQL_PWD\"; \"\$MYSQL\" "
DO_SQLVERB+="\$CONNECT -sN --ssl-verify-server-cert=false \$DATABASE "
DO_SQLVERB+="-e \"\$SQLVERB\""
SO_SQLSTREAM="$DO_SQLVERB"
fi
#
# We've two suitable SQL macros.
# Are we able to connect to the database, preferably as a DBA? # Do we connect the database as a DBA?
# #
SQLVERB="SELECT 1;" SQLVERB="SELECT 1;"
result=""
# Sets the default DBA username for dockerized and native RDBMS as well. # Sets the default DBA username for dockerized and native RDBMS as well.
if [ -z "$MYDBAUSER" ]; then if [ -z "$MYDBAUSER" ]; then
[[ -n "$MYCONTAINER" ]] \ [[ -n "$MYCONTAINER" ]] \
&& MYDBAUSER="root" \ && MYDBAUSER="root" \
|| MYDBAUSER="root" || MYDBAUSER="$USER"
fi fi
# In a native environment we'll try the local connection #
# (Unix-domain socket) first. # We'll try the local connection (Unix-domain socket) first.
if [ -z "$MYCONTAINER" ]; then
CONNECT="" CONNECT=""
DATABASE="" DATABASE=""
result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$? #result=$(eval "$DO_SQLVERB" 2>/dev/null); excode=$?
result="${result//[[:space:]]/}" result="${result//[[:space:]]/}"
fi
if [ "$result" != "1" ]; then if [ "$result" != "1" ]; then
# #
# On failure we'll try the TCP connection. # On failure we'll try the TCP connection.
@ -374,12 +327,10 @@ if [ "$result" != "1" ]; then
fi fi
fi fi
fi fi
#
# We've valid MYSQL_PWD and CONNECT clauses. # We've valid MYSQL_PWD and CONNECT clauses.
# Checks the superuser privilege. # Checks the superuser privilege.
# Better check: TODO! # Better check: TODO!
#
ISDBA=false ISDBA=false
DATABASE="" DATABASE=""
SQLVERB="SHOW GRANTS;" SQLVERB="SHOW GRANTS;"
@ -392,12 +343,10 @@ else
echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONSUPER" | "$TEE" -a "$LOGFILE" >&2
echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2 echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2
fi fi
#
# We know we're a DB superuser or not. # We know we're a DB superuser or not.
# Following steps need the superuser privileges. # Following steps need the superuser privileges.
# Lack of this we'll skip them. # Lack of this we'll skip them.
#
if $ISDBA; then if $ISDBA; then
DATABASE="mysql" DATABASE="mysql"
@ -430,21 +379,32 @@ if $ISDBA; then
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
fi fi
#
# RDBMS version is proper. # RDBMS version is proper.
# Database user revision. # Creates the database user (owner) if it doesn't exist.
# #
# If '$MYUSER'@'$MYHOST' exists, it will provide the necessaty privileges. echo -e "CREATE USER" | "$TEE" -a "$LOGFILE"
# If '$MYUSER'@'%' doesn't exist, it will create it then provide the SQLVERB=" CREATE USER '$MYUSER'@'$MYHOST'; "
# necessary privileges.
#
# Checks '$MYUSER'@'$MYHOST'
SQLVERB="SELECT COUNT(1) FROM mysql.user WHERE user = '$MYUSER' AND host = '$MYHOST'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}" result="${result//[[:space:]]/}"
if [[ $excode -eq 0 && $result -eq 1 ]]; then if [[ $excode -ne 0 ]]; then
# It exists, let's give it privileges. # Already exists (or something went wrong).
echo -e "$MSG_FAILUSER $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2
echo -e "$MSG_NONBLOCKING" | "$TEE" -a "$LOGFILE" >&2
else
# Sets the password only if the user has just created.
echo -e "SET PASSWORD" | "$TEE" -a "$LOGFILE"
SQLVERB="SET PASSWORD FOR '$MYUSER'@'$MYHOST' = '$MYPASSWORD'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \
&& echo -e "$MSG_FAILPASS $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1
fi
#
# Grants all privileges on the database to the user.
#
echo -e "GRANT" | "$TEE" -a "$LOGFILE" echo -e "GRANT" | "$TEE" -a "$LOGFILE"
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; " SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'$MYHOST'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$? result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
@ -453,34 +413,7 @@ if $ISDBA; then
&& echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_FAILGRANT $MYUSER@$MYHOST" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
fi # We've the database user with the proper password.
# Checks '$MYUSER'@'%' as well.
SQLVERB="SELECT COUNT(1) FROM mysql.user WHERE user = '$MYUSER' AND host = '%'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}"
if [[ $excode -eq 0 && $result -ne 1 ]]; then
# Creates if it doesn't exist yet.
echo -e "CREATE USER %" | "$TEE" -a "$LOGFILE"
SQLVERB="CREATE USER '$MYUSER'@'%' IDENTIFIED BY '$MYPASSWORD'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}"
# Gives it up here if something went wrong.
[[ $excode -ne 0 ]] \
&& echo -e "$MSG_FAILUSER $MYUSER@%" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1
fi
# Let's give it privileges.
echo -e "GRANT %" | "$TEE" -a "$LOGFILE"
SQLVERB="GRANT ALL PRIVILEGES ON $MYDATABASE.* TO '$MYUSER'@'%'; "
result=$(eval "$DO_SQLVERB" 2> >("$TEE" -a "$LOGFILE" >&2)); excode=$?
result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \
&& echo -e "$MSG_FAILGRANT $MYUSER@%" | "$TEE" -a "$LOGFILE" >&2 \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1
#
# We've the database user(s) with the proper grants.
# Drops all existing connections to the database being restored. # Drops all existing connections to the database being restored.
# #
@ -505,10 +438,8 @@ if $ISDBA; then
fi fi
done done
fi fi
#
# Connections have eliminated (we hope). # Connections have eliminated (we hope).
fi fi
#
# Done with the superuser part. # Done with the superuser part.
# Drops the database. # Drops the database.
@ -521,7 +452,7 @@ result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
#
# Recreates the database. # Recreates the database.
# #
echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE" echo -e "CREATE DATABASE" | "$TEE" -a "$LOGFILE"
@ -532,7 +463,6 @@ result="${result//[[:space:]]/}"
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \ && echo -e "$MSG_BLOCKING" | "$TEE" -a "$LOGFILE" >&2 \
&& exit 1 && exit 1
#
# We've an empty database. # We've an empty database.
# Sets the default character set. # Sets the default character set.
@ -555,15 +485,13 @@ if [ -n "$MYCHARSET" ]; then
&& exit 1 && exit 1
fi fi
fi fi
#
# We've the character set adjusted. # We've the character set adjusted.
# Restores the database from the dump. # Restores the database from the dump.
# #
# This isn't so straightforward as in e.g PostgreSQL. # This isn't so straightforward as in PostgreSQL.
# We'll use the database user's credentials, not the superuser's # We'll use the database user's credentials, not the superuser's
# to mitigate the effect of an unsanitized dump. # to mitigate the effect of an unsanitized dump.
#
echo -e "RESTORE" | "$TEE" -a "$LOGFILE" echo -e "RESTORE" | "$TEE" -a "$LOGFILE"
# Let's identify the file is gzipped or not. # Let's identify the file is gzipped or not.
UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE") UNPACKER=$("$FILE" --mime-type "$MYDUMPFILE")
@ -573,21 +501,24 @@ UNPACKER=${UNPACKER##* } # The last word is the MIME-type.
&& UNPACKER="$GUNZIP" \ && UNPACKER="$GUNZIP" \
|| UNPACKER="$CAT" || UNPACKER="$CAT"
# This is a sed expression to modify the security definers within the dump. # This is a sed expression to modify the security definers within the dump.
MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=CURRENT_USER/" MOD_DEFINER="s/DEFINER=.*@[^ ]*/DEFINER=\`$MYUSER\`@\`$MYHOST\`/"
# # Considers the RDBMS environment.
# We'll read the dump, on the fly unpack it and modify the security definer, if [ -n "$MYCONTAINER" ]; then
# then we'll pass the data stream to the MySQL client. # Dockerized RDBMS.
# echo "MySQL dockerized - TODO!" | "$TEE" -a "$LOGFILE" >&2
DATABASE="$MYDATABASE" else
SQLVERB="" # Native RDBMS.
(eval "$DO_SQLSTREAM") \ # Reads the dump, on the fly unpacks it and modifies the scurity definer,
< <("$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER") \ # then passes the data stream to the MySQL client.
"$CAT" "$MYDUMPFILE" | "$UNPACKER" | "$SED" "$MOD_DEFINER" | \
"$MYSQL" -u "$MYUSER" -p$MYPASSWORD -h "$MYHOST" -P "$MYPORT" \
-f -D "$MYDATABASE" \
>/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$? >/dev/null 2> >("$TEE" -a "$LOGFILE" >&2); excode=$?
# Unfortunately the result code doesn't differentiate the # Unfortunately the result code doesn't differentiate the
# blocking and non-blocking states. # blocking and non-blocking states.
[[ $excode -ne 0 ]] \ [[ $excode -ne 0 ]] \
&& echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2 && echo -e "$MSG_NONZERO: $excode" | "$TEE" -a "$LOGFILE" >&2
# fi
# We had a try to restore the database - the result isn't properly defined. # We had a try to restore the database - the result isn't properly defined.
# Closing log entry will be handled via EXIT trap. # Closing log entry will be handled via EXIT trap.