From 6765ec89913e0ba54e396bbcb48ecc77f63e33da Mon Sep 17 00:00:00 2001 From: latypov Date: Tue, 29 Sep 2026 22:08:18 +0700 Subject: [PATCH] Initial --- .dockerignore | 7 + .env.example | 21 ++ .gitignore | 12 ++ Dockerfile | 127 +++++++++++++ README.md | 316 +++++++++++++++++++++++++++++++ backup/.gitkeep | 0 compose.yaml | 131 +++++++++++++ data/pictures/.gitkeep | 0 data/tmp/.gitkeep | 0 data/user/.gitkeep | 0 docker/nginx/egeya.conf.template | 54 ++++++ docker/php/egeya-entrypoint.sh | 71 +++++++ docker/php/www-egeya.conf | 4 + scripts/import-db.sh | 23 +++ scripts/migrate-files.sh | 22 +++ scripts/prepare.sh | 37 ++++ secrets/.gitkeep | 0 17 files changed, 825 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 backup/.gitkeep create mode 100644 compose.yaml create mode 100644 data/pictures/.gitkeep create mode 100644 data/tmp/.gitkeep create mode 100644 data/user/.gitkeep create mode 100644 docker/nginx/egeya.conf.template create mode 100644 docker/php/egeya-entrypoint.sh create mode 100644 docker/php/www-egeya.conf create mode 100644 scripts/import-db.sh create mode 100644 scripts/migrate-files.sh create mode 100644 scripts/prepare.sh create mode 100644 secrets/.gitkeep diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..77af5f2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +.gitignore +.env +backup +secrets +data +*.zip diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e52a34d --- /dev/null +++ b/.env.example @@ -0,0 +1,21 @@ +COMPOSE_PROJECT_NAME=egeya + +EGEYA_VERSION=4199 +EGEYA_URL= +EGEYA_SHA256= +EGEYA_DB_NAME=egeya +EGEYA_DB_USER=egeya + +MARIADB_VERSION=10.11 + +HTTP_BIND=127.0.0.1 +HTTP_PORT=8080 +NGINX_SERVER_NAME=_ + +PHP_MEMORY_LIMIT=512M +PHP_UPLOAD_MAX_FILESIZE=512M +PHP_POST_MAX_SIZE=1024M +PHP_MAX_EXECUTION_TIME=300 +PHP_MAX_INPUT_TIME=300 + +TZ=Europe/Berlin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d614bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.env +backup/* +!backup/.gitkeep +data/* +!data/user/ +!data/user/.gitkeep +!data/pictures/ +!data/pictures/.gitkeep +!data/tmp/ +!data/tmp/.gitkeep +secrets/* +!secrets/.gitkeep diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d0f8012 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,127 @@ +ARG EGEYA_VERSION=4199 +ARG EGEYA_URL="" +ARG EGEYA_SHA256="" + +FROM debian:bookworm-slim AS egeya-source + +ARG EGEYA_VERSION +ARG EGEYA_URL +ARG EGEYA_SHA256 + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends ca-certificates curl unzip; \ + rm -rf /var/lib/apt/lists/*; \ + archive_url="${EGEYA_URL:-https://blogengine.ru/download/e2_distr_v${EGEYA_VERSION}.zip}"; \ + work_dir="$(mktemp -d)"; \ + curl -fL \ + --retry 5 \ + --retry-delay 2 \ + --connect-timeout 20 \ + --user-agent "egeya-docker/${EGEYA_VERSION}" \ + "$archive_url" \ + -o "$work_dir/egeya.zip"; \ + if [ -n "$EGEYA_SHA256" ]; then \ + echo "$EGEYA_SHA256 $work_dir/egeya.zip" | sha256sum -c -; \ + fi; \ + mkdir -p "$work_dir/unpacked" /opt/egeya-release; \ + unzip -q "$work_dir/egeya.zip" -d "$work_dir/unpacked"; \ + if [ -f "$work_dir/unpacked/index.php" ]; then \ + source_dir="$work_dir/unpacked"; \ + else \ + source_dir="$(find "$work_dir/unpacked" -maxdepth 2 -type f -name index.php -printf '%h\n' | head -n 1)"; \ + fi; \ + test -n "$source_dir"; \ + cp -a "$source_dir"/. /opt/egeya-release/; \ + test -f /opt/egeya-release/index.php; \ + rm -rf "$work_dir" + +FROM php:8.2-fpm-bookworm AS php + +ENV EGEYA_ROOT=/var/www/html \ + EGEYA_DATA_DIR=/var/www/egeya-data \ + EGEYA_DEFAULTS_DIR=/opt/egeya-defaults + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libavif-dev \ + libfreetype6-dev \ + libjpeg62-turbo-dev \ + libpng-dev \ + libwebp-dev \ + libzip-dev; \ + docker-php-ext-configure gd \ + --with-avif \ + --with-freetype \ + --with-jpeg \ + --with-webp; \ + docker-php-ext-install -j"$(nproc)" \ + exif \ + gd \ + mbstring \ + opcache \ + pdo_mysql \ + zip; \ + mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"; \ + rm -rf /var/lib/apt/lists/* + +COPY --from=egeya-source /opt/egeya-release/ /var/www/html/ + +RUN set -eux; \ + mkdir -p "$EGEYA_DEFAULTS_DIR/user" "$EGEYA_DEFAULTS_DIR/pictures"; \ + if [ -d "$EGEYA_ROOT/user" ]; then \ + cp -a "$EGEYA_ROOT/user"/. "$EGEYA_DEFAULTS_DIR/user"/; \ + fi; \ + if [ -d "$EGEYA_ROOT/pictures" ]; then \ + cp -a "$EGEYA_ROOT/pictures"/. "$EGEYA_DEFAULTS_DIR/pictures"/; \ + fi; \ + rm -rf "$EGEYA_ROOT/user" "$EGEYA_ROOT/pictures"; \ + ln -s "$EGEYA_DATA_DIR/user" "$EGEYA_ROOT/user"; \ + ln -s "$EGEYA_DATA_DIR/pictures" "$EGEYA_ROOT/pictures"; \ + chown -R www-data:www-data "$EGEYA_ROOT" "$EGEYA_DEFAULTS_DIR" + +COPY docker/php/egeya-entrypoint.sh /usr/local/bin/egeya-entrypoint +COPY docker/php/www-egeya.conf /usr/local/etc/php-fpm.d/zz-egeya.conf + +RUN chmod 0755 /usr/local/bin/egeya-entrypoint + +WORKDIR /var/www/html + +EXPOSE 9000 + +HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=5 \ + CMD php -r '$s=@fsockopen("127.0.0.1",9000,$e,$m,2); if(!$s){exit(1);} fclose($s);' + +ENTRYPOINT ["egeya-entrypoint"] +CMD ["php-fpm"] + +FROM nginx:1.28-bookworm AS nginx + +ENV EGEYA_ROOT=/var/www/html \ + EGEYA_DATA_DIR=/var/www/egeya-data + +RUN set -eux; \ + apt-get update; \ + apt-get install -y --no-install-recommends curl; \ + rm -rf /var/lib/apt/lists/*; \ + if getent passwd www-data >/dev/null; then \ + sed -ri 's/^user[[:space:]]+[^;]+;/user www-data;/' /etc/nginx/nginx.conf; \ + fi + +COPY --from=egeya-source /opt/egeya-release/ /var/www/html/ + +RUN set -eux; \ + rm -rf "$EGEYA_ROOT/user" "$EGEYA_ROOT/pictures"; \ + ln -s "$EGEYA_DATA_DIR/user" "$EGEYA_ROOT/user"; \ + ln -s "$EGEYA_DATA_DIR/pictures" "$EGEYA_ROOT/pictures"; \ + rm -f /etc/nginx/conf.d/default.conf + +COPY docker/nginx/egeya.conf.template /etc/nginx/templates/egeya.conf.template + +WORKDIR /var/www/html + +EXPOSE 80 + +HEALTHCHECK --interval=15s --timeout=5s --start-period=20s --retries=5 \ + CMD curl -fsS http://127.0.0.1/ >/dev/null || exit 1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f1330b8 --- /dev/null +++ b/README.md @@ -0,0 +1,316 @@ +# Egeya 11.5 v4199 in Docker + +Stack: + +- nginx +- PHP-FPM 8.2 +- MariaDB 10.11 +- Egeya v4199 + +The layout intentionally follows a tested nginx + PHP-FPM deployment rather than the older Apache-based Docker example. + +## Why nginx + PHP-FPM + +Egeya 11 supports PHP up to 8.2. The nginx configuration uses synthetic URLs through: + +```nginx +location / { + log_not_found off; + rewrite ^(.*)$ /index.php?go=$1 last; +} +``` + +Do not replace this with: + +```nginx +try_files $uri $uri/ /index.php?go=$uri; +``` + +The rewrite form preserves the original query string used by Egeya upload requests, including fields such as `entity` and `entity-id`. + +The PHP location also passes: + +```nginx +fastcgi_param E2_SYNTH_URLS true; +``` + +Direct access to PHP files under `/system` and `/user`, `user/settings.json`, `*.psa`, and `.ht*` is blocked. + +## Upload temporary directory + +The `data` directory is mounted once at `/var/www/egeya-data`. + +Persistent paths are: + +```text +/var/www/egeya-data/user +/var/www/egeya-data/pictures +/var/www/egeya-data/tmp +``` + +Inside both application images, `/var/www/html/user` and `/var/www/html/pictures` are symbolic links into this single mount. + +PHP is configured with: + +```ini +upload_tmp_dir = /var/www/egeya-data/tmp +sys_temp_dir = /var/www/egeya-data/tmp +``` + +This is deliberate. A standard container `/tmp` is on the container filesystem, while `pictures` is on a bind mount. Moving an uploaded file between them may cross mount boundaries and fail with `EXDEV`. Keeping the upload temporary directory and `pictures` under the same mount avoids that class of failure. + +## Project structure + +```text +Dockerfile +compose.yaml +.env.example +.dockerignore +.gitignore +docker/nginx/egeya.conf.template +docker/php/egeya-entrypoint.sh +docker/php/www-egeya.conf +scripts/prepare.sh +scripts/migrate-files.sh +scripts/import-db.sh +data/user +data/pictures +data/tmp +backup +secrets +``` + +Create the structure manually if required: + +```bash +mkdir -p docker/nginx docker/php scripts data/user data/pictures data/tmp backup secrets +touch Dockerfile compose.yaml .env.example .dockerignore .gitignore \ + docker/nginx/egeya.conf.template \ + docker/php/egeya-entrypoint.sh docker/php/www-egeya.conf \ + scripts/prepare.sh scripts/migrate-files.sh scripts/import-db.sh \ + data/user/.gitkeep data/pictures/.gitkeep data/tmp/.gitkeep \ + backup/.gitkeep secrets/.gitkeep +``` + +## Prepare + +```bash +chmod +x scripts/*.sh +./scripts/prepare.sh +``` + +Review `.env` before starting. + +## Build + +```bash +docker compose build --pull +``` + +Both `nginx` and `php` targets are built from the same Egeya source stage, so the static files seen by nginx and the PHP code executed by PHP-FPM are from the same Egeya release. + +Optional source integrity check: + +```text +EGEYA_SHA256= +``` + +## Start + +```bash +docker compose up -d + +docker compose ps + +docker compose logs --tail=100 nginx php db +``` + +By default nginx listens only on: + +```text +127.0.0.1:8080 +``` + +This is suitable when another reverse proxy terminates HTTPS on the Docker host. + +## Data model + +The application code is immutable and comes from the Docker images. + +Persistent data is limited to: + +- `data/user` +- `data/pictures` +- `data/tmp` +- MariaDB named volume `db_data` + +Do not copy an old `/system` directory into the container. Updating the image updates `/system` and the bundled application files. + +Custom additions stored below `user`, including `user/extras`, migrate together with the user data. + +## Migration from a bare-metal installation + +Assume the current installation is: + +```text +/var/www/blog +``` + +### 1. Stop writes on the old installation + +Stop the web application or otherwise prevent writes for the final synchronization. + +### 2. Create a database dump + +Example: + +```bash +mysqldump \ + --single-transaction \ + --quick \ + --triggers \ + --default-character-set=utf8mb4 \ + egeya > egeya.sql +``` + +Copy the dump to: + +```text +backup/egeya.sql +``` + +### 3. Copy file data + +If the old blog tree is locally available: + +```bash +./scripts/migrate-files.sh /var/www/blog ./data +``` + +For a remote source, an equivalent rsync is: + +```bash +rsync -aHAX old-host:/var/www/blog/user/ ./data/user/ +rsync -aHAX old-host:/var/www/blog/pictures/ ./data/pictures/ +``` + +Do not copy `/system`. + +Do not copy the old nginx configuration into the application image. Use `docker/nginx/egeya.conf.template`. + +### 4. Prepare secrets and configuration + +```bash +./scripts/prepare.sh +``` + +Set the target database name and user in `.env` before initializing MariaDB. + +### 5. Start MariaDB and import the dump + +```bash +./scripts/import-db.sh backup/egeya.sql +``` + +### 6. Start PHP and nginx + +```bash +docker compose up -d php nginx +``` + +### 7. Validate + +```bash +docker compose ps + +docker compose logs --tail=200 php nginx +``` + +Check at least: + +- public pages +- author login +- note editor +- image upload +- audio/video upload if used +- editing an existing note +- built-in security check +- access to `/system/core.php` returns 403 +- access to `/user/settings.json` returns 403 + +For upload diagnostics, the application log is available at: + +```text +data/user/logs/main.log +``` + +It is created by the PHP container entrypoint if absent. + +## Database credentials + +Egeya receives database settings through its supported environment variables: + +```text +E2_DB_SERVER +E2_DB_USER_NAME +E2_DB_PASSW +E2_DB_NAME +``` + +The Compose file does not place the password directly in its environment section. It mounts a Docker secret and the PHP entrypoint exports its contents as `E2_DB_PASSW` immediately before starting PHP-FPM. + +PHP-FPM is explicitly configured with `clear_env = no` so the Egeya process can see these variables. + +## Updating Egeya + +Change: + +```text +EGEYA_VERSION= +``` + +If necessary also set `EGEYA_URL` and `EGEYA_SHA256`. + +Then rebuild and recreate the application containers: + +```bash +docker compose build --pull php nginx +docker compose up -d --no-deps --force-recreate php nginx +``` + +The persistent `user`, `pictures`, and database data are not replaced. + +Before an update, make a database dump and a copy of `data`. + +## Useful commands + +Validate Compose rendering: + +```bash +docker compose config +``` + +PHP configuration: + +```bash +docker compose exec php php -i | grep -E 'upload_tmp_dir|sys_temp_dir|upload_max_filesize|post_max_size' +``` + +Verify that temp and pictures resolve to the same mounted filesystem: + +```bash +docker compose exec php sh -c 'df -T /var/www/egeya-data/tmp /var/www/egeya-data/pictures' +``` + +Check the nginx configuration: + +```bash +docker compose exec nginx nginx -T +``` + +Check protected paths: + +```bash +curl -i http://127.0.0.1:8080/system/core.php +curl -i http://127.0.0.1:8080/user/settings.json +``` diff --git a/backup/.gitkeep b/backup/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..5ae550d --- /dev/null +++ b/compose.yaml @@ -0,0 +1,131 @@ +name: ${COMPOSE_PROJECT_NAME:-egeya} + +services: + db: + image: mariadb:${MARIADB_VERSION:-10.11} + restart: unless-stopped + + environment: + MARIADB_DATABASE: ${EGEYA_DB_NAME:-egeya} + MARIADB_USER: ${EGEYA_DB_USER:-egeya} + MARIADB_PASSWORD_FILE: /run/secrets/db_password + MARIADB_ROOT_PASSWORD_FILE: /run/secrets/db_root_password + TZ: ${TZ:-UTC} + + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + + volumes: + - db_data:/var/lib/mysql + + secrets: + - db_password + - db_root_password + + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + + networks: + - backend + + php: + build: + context: . + target: php + args: + EGEYA_VERSION: ${EGEYA_VERSION:-4199} + EGEYA_URL: ${EGEYA_URL:-} + EGEYA_SHA256: ${EGEYA_SHA256:-} + + image: ${EGEYA_PHP_IMAGE:-local/egeya-php}:${EGEYA_VERSION:-4199} + restart: unless-stopped + + depends_on: + db: + condition: service_healthy + + environment: + E2_DB_SERVER: db + E2_DB_USER_NAME: ${EGEYA_DB_USER:-egeya} + E2_DB_PASSW_FILE: /run/secrets/db_password + E2_DB_NAME: ${EGEYA_DB_NAME:-egeya} + PHP_MEMORY_LIMIT: ${PHP_MEMORY_LIMIT:-512M} + PHP_UPLOAD_MAX_FILESIZE: ${PHP_UPLOAD_MAX_FILESIZE:-512M} + PHP_POST_MAX_SIZE: ${PHP_POST_MAX_SIZE:-1024M} + PHP_MAX_EXECUTION_TIME: ${PHP_MAX_EXECUTION_TIME:-300} + PHP_MAX_INPUT_TIME: ${PHP_MAX_INPUT_TIME:-300} + TZ: ${TZ:-UTC} + + volumes: + - ./data:/var/www/egeya-data + + secrets: + - db_password + + healthcheck: + test: + - CMD + - php + - -r + - '$$s=@fsockopen("127.0.0.1",9000,$$e,$$m,2); if(!$$s){exit(1);} fclose($$s);' + interval: 15s + timeout: 5s + retries: 5 + start_period: 20s + + networks: + - backend + + nginx: + build: + context: . + target: nginx + args: + EGEYA_VERSION: ${EGEYA_VERSION:-4199} + EGEYA_URL: ${EGEYA_URL:-} + EGEYA_SHA256: ${EGEYA_SHA256:-} + + image: ${EGEYA_NGINX_IMAGE:-local/egeya-nginx}:${EGEYA_VERSION:-4199} + restart: unless-stopped + + depends_on: + php: + condition: service_healthy + + environment: + NGINX_SERVER_NAME: ${NGINX_SERVER_NAME:-_} + NGINX_CLIENT_MAX_BODY_SIZE: ${PHP_POST_MAX_SIZE:-1024M} + TZ: ${TZ:-UTC} + + ports: + - "${HTTP_BIND:-127.0.0.1}:${HTTP_PORT:-8080}:80" + + volumes: + - ./data:/var/www/egeya-data:ro + + networks: + - frontend + - backend + +volumes: + db_data: + +networks: + frontend: + driver: bridge + + backend: + driver: bridge + internal: true + +secrets: + db_password: + file: ./secrets/db_password + + db_root_password: + file: ./secrets/db_root_password diff --git a/data/pictures/.gitkeep b/data/pictures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/tmp/.gitkeep b/data/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/user/.gitkeep b/data/user/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docker/nginx/egeya.conf.template b/docker/nginx/egeya.conf.template new file mode 100644 index 0000000..b9edc81 --- /dev/null +++ b/docker/nginx/egeya.conf.template @@ -0,0 +1,54 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + server_name ${NGINX_SERVER_NAME}; + + keepalive_timeout 70; + client_max_body_size ${NGINX_CLIENT_MAX_BODY_SIZE}; + + root /var/www/html; + index index.php index.html; + + location ~* ^.+\.(jpg|jpeg|gif|css|html|htm|png|js|webp|avif|svg|mp3|ogg|mp4|mov)$ { + access_log off; + expires 30d; + } + + error_page 404 /errors/404.html; + + location / { + log_not_found off; + rewrite ^(.*)$ /index.php?go=$1 last; + } + + location ~ ^/(system|user)/.*\.php$ { + deny all; + return 403; + } + + location = /user/settings.json { + deny all; + return 403; + } + + location = /user/settings.psa { + deny all; + return 403; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param E2_SYNTH_URLS true; + fastcgi_pass php:9000; + fastcgi_index index.php; + } + + location ~ /\.ht { + deny all; + } + + location ~ \.psa$ { + deny all; + } +} diff --git a/docker/php/egeya-entrypoint.sh b/docker/php/egeya-entrypoint.sh new file mode 100644 index 0000000..27100f2 --- /dev/null +++ b/docker/php/egeya-entrypoint.sh @@ -0,0 +1,71 @@ +#!/bin/bash +set -euo pipefail + +EGEYA_DATA_DIR="${EGEYA_DATA_DIR:-/var/www/egeya-data}" +EGEYA_DEFAULTS_DIR="${EGEYA_DEFAULTS_DIR:-/opt/egeya-defaults}" +PHP_RUNTIME_INI="/usr/local/etc/php/conf.d/zz-egeya-runtime.ini" + +PHP_MEMORY_LIMIT="${PHP_MEMORY_LIMIT:-512M}" +PHP_UPLOAD_MAX_FILESIZE="${PHP_UPLOAD_MAX_FILESIZE:-512M}" +PHP_POST_MAX_SIZE="${PHP_POST_MAX_SIZE:-1024M}" +PHP_MAX_EXECUTION_TIME="${PHP_MAX_EXECUTION_TIME:-300}" +PHP_MAX_INPUT_TIME="${PHP_MAX_INPUT_TIME:-300}" + +copy_defaults_if_empty() { + local name="$1" + local source_dir="${EGEYA_DEFAULTS_DIR}/${name}" + local target_dir="${EGEYA_DATA_DIR}/${name}" + + mkdir -p "$target_dir" + + if [ -d "$source_dir" ] && [ -z "$(find "$target_dir" -mindepth 1 -print -quit)" ]; then + cp -a "$source_dir"/. "$target_dir"/ + fi +} + +read_secret_to_env() { + local env_name="$1" + local file_env_name="${env_name}_FILE" + local file_path="${!file_env_name:-}" + + if [ -n "$file_path" ]; then + if [ ! -r "$file_path" ]; then + printf 'Secret file is not readable: %s\n' "$file_path" >&2 + exit 1 + fi + printf -v "$env_name" '%s' "$(cat "$file_path")" + export "$env_name" + fi +} + +copy_defaults_if_empty user +copy_defaults_if_empty pictures + +mkdir -p \ + "$EGEYA_DATA_DIR/tmp" \ + "$EGEYA_DATA_DIR/tmp/sessions" \ + "$EGEYA_DATA_DIR/user/logs" + +touch "$EGEYA_DATA_DIR/user/logs/main.log" + +chown -R www-data:www-data \ + "$EGEYA_DATA_DIR/user" \ + "$EGEYA_DATA_DIR/pictures" \ + "$EGEYA_DATA_DIR/tmp" + +chmod 0700 "$EGEYA_DATA_DIR/tmp" "$EGEYA_DATA_DIR/tmp/sessions" + +read_secret_to_env E2_DB_PASSW + +cat > "$PHP_RUNTIME_INI" <&2 + exit 1 +fi + +cd "$PROJECT_DIR" + +docker compose up -d db + +docker compose exec -T db sh -ec ' + exec mariadb \ + -u"$MARIADB_USER" \ + -p"$(cat /run/secrets/db_password)" \ + "$MARIADB_DATABASE" +' < "$DUMP_FILE" + +printf 'Imported database dump: %s\n' "$DUMP_FILE" diff --git a/scripts/migrate-files.sh b/scripts/migrate-files.sh new file mode 100644 index 0000000..56f378e --- /dev/null +++ b/scripts/migrate-files.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -euo pipefail + +SOURCE_BLOG_DIR="${1:-/var/www/blog}" +TARGET_DATA_DIR="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/data}" + +for name in user pictures; do + source_dir="${SOURCE_BLOG_DIR}/${name}" + target_dir="${TARGET_DATA_DIR}/${name}" + + if [ ! -d "$source_dir" ]; then + printf 'Source directory not found: %s\n' "$source_dir" >&2 + exit 1 + fi + + mkdir -p "$target_dir" + rsync -aHAX --delete "$source_dir"/ "$target_dir"/ +done + +mkdir -p "${TARGET_DATA_DIR}/tmp" + +printf 'Copied user and pictures from %s to %s\n' "$SOURCE_BLOG_DIR" "$TARGET_DATA_DIR" diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100644 index 0000000..a4951b0 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -euo pipefail + +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ENV_FILE="${PROJECT_DIR}/.env" +ENV_EXAMPLE="${PROJECT_DIR}/.env.example" +SECRETS_DIR="${PROJECT_DIR}/secrets" +DATA_DIR="${PROJECT_DIR}/data" + +create_secret() { + local path="$1" + + if [ ! -s "$path" ]; then + umask 077 + openssl rand -base64 48 | tr -d '\n' > "$path" + printf '\n' >> "$path" + fi +} + +mkdir -p \ + "$SECRETS_DIR" \ + "$DATA_DIR/user" \ + "$DATA_DIR/pictures" \ + "$DATA_DIR/tmp" + +if [ ! -f "$ENV_FILE" ]; then + cp "$ENV_EXAMPLE" "$ENV_FILE" +fi + +create_secret "$SECRETS_DIR/db_password" +create_secret "$SECRETS_DIR/db_root_password" + +chmod 0600 \ + "$SECRETS_DIR/db_password" \ + "$SECRETS_DIR/db_root_password" + +printf 'Project prepared: %s\n' "$PROJECT_DIR" diff --git a/secrets/.gitkeep b/secrets/.gitkeep new file mode 100644 index 0000000..e69de29