diff --git a/domain-monitor-docker/.docker/Dockerfile b/domain-monitor-docker/.docker/Dockerfile new file mode 100644 index 0000000..7f59593 --- /dev/null +++ b/domain-monitor-docker/.docker/Dockerfile @@ -0,0 +1,24 @@ +# Latest stable PHP + Apache +FROM php:apache + +# Build deps for requested PHP extensions +RUN apt-get update && apt-get install -y --no-install-recommends \ + libicu-dev libzip-dev libpng-dev libjpeg62-turbo-dev libfreetype6-dev \ + libxml2-dev libcurl4-openssl-dev libonig-dev pkg-config unzip git tzdata \ + && docker-php-ext-configure gd --with-jpeg --with-freetype \ + && docker-php-ext-install -j"$(nproc)" \ + pdo pdo_mysql mysqli intl zip gd bcmath mbstring curl xml \ + && a2enmod rewrite headers \ + && rm -rf /var/lib/apt/lists/* + +# Set Apache DocumentRoot to /var/www/html/public +ARG APACHE_DOCUMENT_ROOT=/var/www/html/public +RUN sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/000-default.conf + +# Apache timeout only (no proxy needed) +RUN set -eux; \ + echo "Timeout 300" > /etc/apache2/conf-available/timeouts.conf; \ + a2enconf timeouts + +WORKDIR /var/www/html + diff --git a/domain-monitor-docker/.docker/php.ini b/domain-monitor-docker/.docker/php.ini new file mode 100644 index 0000000..1c26419 --- /dev/null +++ b/domain-monitor-docker/.docker/php.ini @@ -0,0 +1,20 @@ +; ---- PHP limits (as requested) ---- +max_execution_time = 0 +max_input_time = -1 +max_input_vars = 3000 +memory_limit = 512M +post_max_size = 32M +upload_max_filesize = 16M +default_socket_timeout = 120 + +; Recommended defaults +date.timezone = UTC +display_errors = On +error_reporting = E_ALL + +; Opcache (safe defaults) +opcache.enable=1 +opcache.enable_cli=1 +opcache.validate_timestamps=1 +opcache.revalidate_freq=2 + diff --git a/domain-monitor-docker/.gitignore b/domain-monitor-docker/.gitignore new file mode 100644 index 0000000..1eadf52 --- /dev/null +++ b/domain-monitor-docker/.gitignore @@ -0,0 +1,50 @@ + === +.env +.env.docker +*.env.bak* +*.env.backup* +app/.env + +# === LOGS === +app/logs/ +*.log + +# === COMPOSER === +app/vendor/ +app/composer.lock + +# === DOCKER / BUILD ARTIFACTS === +dbdata/ +.docker/*.pid +.docker/*.sock +.docker/.cache/ +*.pid +*.sock + +# === TEMPORARY FILES === +*.tmp +*.swp +*.bak +*.orig + +# === OS / EDITOR === +.DS_Store +Thumbs.db +.idea/ +.vscode/ +*.code-workspace + +# === BACKUPS === +*.bak.* +*.tar +*.zip + +# === GIT IGNORES REPO CLONED APP (OPTIONAL) === +# If you keep domain-monitor-docker in its own Git repo and want to ignore app/ +# uncomment this line: +# app/ + +# === SYSTEM / MISC === +node_modules/ +coverage/ + diff --git a/domain-monitor-docker/README.md b/domain-monitor-docker/README.md new file mode 100644 index 0000000..936d242 --- /dev/null +++ b/domain-monitor-docker/README.md @@ -0,0 +1,157 @@ +# Domain Monitor Docker Setup + +This directory contains Docker configuration for running Domain Monitor in a containerized environment. + +## Quick Start + +1. **Configure your environment:** + - Copy `env.docker.example` to `.env.docker` + - Edit `.env.docker` and change the default passwords: + ```bash + cp env.docker.example .env.docker + # Edit .env.docker with your real passwords + ``` + +2. **Run the bootstrap script:** + ```bash + chmod +x bootstrap.sh + ./bootstrap.sh + ``` + +## What the bootstrap script does + +The `bootstrap.sh` script will: + +- **Clone the Domain Monitor repository** from GitHub into the `app/` folder (only if `app/` doesn't exist or is empty) +- **Set up the application environment** by creating `.env` file with database configuration +- **Install PHP dependencies** using Composer +- **Configure proper file permissions** for the web server +- **Load environment variables** from `.env.docker` into the shell +- **Start the Docker stack** (web server, database, and phpMyAdmin) + +### Running bootstrap multiple times + +The bootstrap script is **safe to run multiple times**: +- If `app/` folder exists and has content, it skips the git clone +- It will reinstall Composer dependencies (ensures they're up to date) +- It will restart the Docker containers with `--build` flag +- Your database data is preserved (stored in Docker volumes) + +## Environment Configuration + +The bootstrap script loads environment variables from `.env.docker` into the shell environment, which Docker Compose then uses for variable substitution. This approach ensures that all services get the correct database credentials. + +## Services + +After running the bootstrap script, you'll have: + +- **Domain Monitor App**: http://localhost:8080 +- **phpMyAdmin**: http://localhost:8081 (Server: `domain-monitor-mariadb`) + +## Managing the Docker Stack + +### Starting the stack +```bash +# First time setup (clones repo, installs dependencies, starts containers) +./bootstrap.sh + +# Or if you just want to start existing containers +docker compose up -d +``` + +### Stopping the stack +```bash +# Stop all containers +docker compose down + +# Stop and remove volumes (WARNING: This will delete your database data!) +docker compose down -v +``` + +### Restarting the stack +```bash +# Restart containers (keeps data) +docker compose restart + +# Rebuild and restart (if you made changes to Docker config) +docker compose up -d --build + +# Full restart (stops, rebuilds, starts) +docker compose down && docker compose up -d --build +``` + +### Viewing logs +```bash +# View logs for all services +docker compose logs + +# View logs for specific service +docker compose logs web +docker compose logs db +docker compose logs pma + +# Follow logs in real-time +docker compose logs -f +``` + +## Environment Configuration + +You need to create a `.env.docker` file with your database credentials. The script will copy `env.docker.example` to `.env.docker` if it doesn't exist, but you **must edit it** with real passwords before the script will work. + +Required variables in `.env.docker`: +- `DB_DATABASE` - Database name +- `DB_USERNAME` - Database user +- `DB_PASSWORD` - Database password +- `DB_ROOT_PASSWORD` - Database root password +- `TZ` - Timezone (optional, defaults to UTC) + +## Requirements + +- Docker and Docker Compose +- Git +- Internet connection (to clone the repository) + +## Troubleshooting + +- Make sure you're running the script from the `domain-monitor-docker` directory +- Ensure Docker is running and you have permission to use it +- Check that you've edited `.env.docker` with real passwords (not the example values) + +## Quick Reference + +### Common scenarios + +**First time setup:** +```bash +cp env.docker.example .env.docker +# Edit .env.docker with real passwords +./bootstrap.sh +``` + +**Daily usage:** +```bash +# Start (if stopped) +docker compose up -d + +# Stop +docker compose down +``` + +**After making changes to the app:** +```bash +# Restart to pick up changes +docker compose restart web +``` + +**After making changes to Docker config:** +```bash +# Rebuild containers +docker compose up -d --build +``` + +**Complete reset (WARNING: deletes all data):** +```bash +docker compose down -v +rm -rf app/ +./bootstrap.sh +``` diff --git a/domain-monitor-docker/bootstrap.sh b/domain-monitor-docker/bootstrap.sh new file mode 100644 index 0000000..c5b09f1 --- /dev/null +++ b/domain-monitor-docker/bootstrap.sh @@ -0,0 +1,131 @@ +#!/usr/bin/env bash +set -euo pipefail + +### --- CONFIG --- +GIT_URL="${GIT_URL:-https://github.com/Hosteroid/domain-monitor.git}" +APP_ENV_DEFAULT="${APP_ENV:-production}" + +# Always treat the CURRENT directory as project root +PROJECT_ROOT="$(pwd)" +APP_DIR="$PROJECT_ROOT/app" +DOCKER_ENV_FILE="$PROJECT_ROOT/.env.docker" +ENV_TMPL="$APP_DIR/env.example.txt" +ENV_FILE="$APP_DIR/.env" + +# Numeric IDs for php:apache (Debian) www-data +WWW_UID=33 +WWW_GID=33 +ROOT_UID=0 + +dc() { + if command -v docker &>/dev/null && docker compose version &>/dev/null; then + docker compose "$@" + elif command -v docker-compose &>/dev/null; then + docker-compose "$@" + else + echo "Error: docker compose not found." >&2 + exit 1 + fi +} + +fail() { echo "Error: $*" >&2; exit 1; } +upsert_kv() { + local file="$1" key="$2" val="$3" + if grep -qE "^${key}=" "$file" 2>/dev/null; then + sed -i "s#^${key}=.*#${key}=${val}#g" "$file" + else + printf "%s=%s\n" "$key" "$val" >> "$file" + fi +} + +echo "==> Domain Monitor bootstrap" + +[[ -f "$PROJECT_ROOT/docker-compose.yml" ]] || fail "Run this from the domain-monitor-docker folder." + +# Ensure .env.docker exists (or copy example then exit) +if [[ ! -f "$DOCKER_ENV_FILE" ]]; then + if [[ -f "$PROJECT_ROOT/env.docker.example" ]]; then + echo "==> .env.docker not found. Creating from example..." + cp "$PROJECT_ROOT/env.docker.example" "$DOCKER_ENV_FILE" + echo " Edit $DOCKER_ENV_FILE with real passwords, then re-run." + exit 1 + else + fail ".env.docker not found (and no example to copy)." + fi +fi + +# Load Docker env +set -a +# shellcheck disable=SC1090 +. "$DOCKER_ENV_FILE" +set +a + +: "${DB_DATABASE:?Missing DB_DATABASE in .env.docker}" +: "${DB_USERNAME:?Missing DB_USERNAME in .env.docker}" +: "${DB_PASSWORD:?Missing DB_PASSWORD in .env.docker}" +: "${DB_ROOT_PASSWORD:?Missing DB_ROOT_PASSWORD in .env.docker}" +TZ="${TZ:-UTC}" + +# Clone repo if needed +if [[ ! -d "$APP_DIR" || -z "$(ls -A "$APP_DIR" 2>/dev/null || true)" ]]; then + echo "==> Cloning $GIT_URL into app/" + git clone "$GIT_URL" "$APP_DIR" +else + echo "==> app/ exists; skipping clone" +fi + +# Create/merge app .env +if [[ -f "$ENV_FILE" ]]; then + cp -a "$ENV_FILE" "$ENV_FILE.bak.$(date +%s)" + echo " - Existing .env backed up." +elif [[ -f "$ENV_TMPL" ]]; then + cp "$ENV_TMPL" "$ENV_FILE" + echo " - Created .env from env.example.txt" +else + touch "$ENV_FILE" + echo " - Created empty .env (env.example.txt not found)" +fi + +# Write DB + env for container network +upsert_kv "$ENV_FILE" "DB_HOST" "db" +upsert_kv "$ENV_FILE" "DB_PORT" "3306" +upsert_kv "$ENV_FILE" "DB_DATABASE" "$DB_DATABASE" +upsert_kv "$ENV_FILE" "DB_USERNAME" "$DB_USERNAME" +upsert_kv "$ENV_FILE" "DB_PASSWORD" "$DB_PASSWORD" +upsert_kv "$ENV_FILE" "APP_ENV" "${APP_ENV_DEFAULT}" + +# --- PERMISSIONS using numeric IDs (works across host/container) --- +echo "==> Applying permissions with numeric IDs (root:${WWW_GID} for code; ${WWW_UID}:${WWW_GID} for writable dirs)" +mkdir -p "$APP_DIR/logs" + +# Code owned by root, group = www-data (33) +chown -R ${ROOT_UID}:${WWW_GID} "$APP_DIR" || true + +# Dirs 755, files 644 +find "$APP_DIR" -type d -print0 | xargs -0 chmod 755 +find "$APP_DIR" -type f -print0 | xargs -0 chmod 644 + +# Writable dirs for the app at runtime +for d in logs storage cache tmp runtime; do + if [ -d "$APP_DIR/$d" ]; then + echo " - Making $d writable by ${WWW_UID}:${WWW_GID}" + chown -R ${WWW_UID}:${WWW_GID} "$APP_DIR/$d" + chmod -R 775 "$APP_DIR/$d" + fi +done + +# .env readable by root & group only +chmod 640 "$APP_DIR/.env" || true + +# Install vendors via Composer container +echo "==> Installing Composer vendors (composer:2) ..." +docker run --rm -v "$APP_DIR":/app -w /app composer:2 install --no-interaction --prefer-dist + +# Bring stack up (build/rebuild) +echo "==> Starting stack ..." +dc up -d --build + +echo "==> Done." +echo " App URL: http://:8080" +echo " phpMyAdmin: http://:8081 (Server: domain-monitor-mariadb)" +echo " Note: On the host, UID 33 may display as 'tape'; inside the container it's www-data. This is normal." diff --git a/domain-monitor-docker/docker-compose.yml b/domain-monitor-docker/docker-compose.yml new file mode 100644 index 0000000..d9280e4 --- /dev/null +++ b/domain-monitor-docker/docker-compose.yml @@ -0,0 +1,50 @@ +services: + web: + build: + context: . + dockerfile: .docker/Dockerfile + container_name: domain-monitor-web + ports: + - "8080:80" + volumes: + - ./app:/var/www/html:Z + - ./.docker/php.ini:/usr/local/etc/php/conf.d/custom.ini:ro,Z + environment: + APACHE_DOCUMENT_ROOT: /var/www/html/public + TZ: ${TZ:-UTC} + depends_on: + - db + + db: + image: mariadb:latest + container_name: domain-monitor-mariadb + restart: unless-stopped + environment: + MARIADB_DATABASE: ${DB_DATABASE} + MARIADB_USER: ${DB_USERNAME} + MARIADB_PASSWORD: ${DB_PASSWORD} + MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} + TZ: ${TZ:-UTC} + command: [ + "--character-set-server=utf8mb4", + "--collation-server=utf8mb4_unicode_ci" + ] + volumes: + - dbdata:/var/lib/mysql + + pma: + image: phpmyadmin:latest + container_name: domain-monitor-pma + restart: unless-stopped + environment: + PMA_HOST: domain-monitor-mariadb + UPLOAD_LIMIT: 256M + TZ: ${TZ:-UTC} + ports: + - "8081:80" + depends_on: + - db + +volumes: + dbdata: + diff --git a/domain-monitor-docker/env.docker.example b/domain-monitor-docker/env.docker.example new file mode 100644 index 0000000..baa18f9 --- /dev/null +++ b/domain-monitor-docker/env.docker.example @@ -0,0 +1,12 @@ +# Example Docker environment file +# Copy this file to .env.docker and update your credentials + +# === Database Configuration === +DB_DATABASE=domain_monitor +DB_USERNAME=user_example +DB_PASSWORD=example_password +DB_ROOT_PASSWORD=example_root_password + +# === Timezone (for PHP, MariaDB, phpMyAdmin) === +TZ=UTC +