From 489791403ce0f715a65dd6ecccc4203eaa05326c Mon Sep 17 00:00:00 2001 From: jack Date: Fri, 27 Mar 2026 03:04:54 +0700 Subject: [PATCH] feat: migrate Outline + n8n to main server, rename S3 buckets to walava-* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Outline, outline-db, outline-redis, n8n, outline-mcp containers to main docker-compose - Add env.outline.j2 template with Resend SMTP and S3 (walava-outline bucket) - Update Traefik routes: wiki → outline:3000, auto → n8n:5678 (local, not cross-server) - Rename S3 buckets: visual-backup → walava-backup, visual-outline → walava-outline - Extend backup.sh.j2: add Outline DB, n8n, Plane MinIO to backup scope - Add outline_image, n8n_image, outline_mcp_image to services/defaults - Remove Authelia config deployment tasks from configs.yml - Add outline-internal and n8n-internal networks to docker-compose Co-Authored-By: Claude Sonnet 4.6 --- roles/backup/defaults/main.yml | 2 +- roles/backup/templates/backup.sh.j2 | 37 ++++- roles/services/defaults/main.yml | 5 + roles/services/tasks/configs.yml | 27 ++-- .../services/templates/docker-compose.yml.j2 | 129 ++++++++++++++++++ roles/services/templates/env.outline.j2 | 42 ++++++ .../templates/traefik/dynamic/routes.yml.j2 | 6 +- 7 files changed, 224 insertions(+), 24 deletions(-) create mode 100644 roles/services/templates/env.outline.j2 diff --git a/roles/backup/defaults/main.yml b/roles/backup/defaults/main.yml index 65e4b0b..6478b34 100644 --- a/roles/backup/defaults/main.yml +++ b/roles/backup/defaults/main.yml @@ -5,4 +5,4 @@ backup_user: deploy # Timeweb S3 offsite backups s3_endpoint: "https://s3.timeweb.cloud" -s3_bucket: "visual-backup" +s3_bucket: "walava-backup" diff --git a/roles/backup/templates/backup.sh.j2 b/roles/backup/templates/backup.sh.j2 index f06de13..ed8dc14 100644 --- a/roles/backup/templates/backup.sh.j2 +++ b/roles/backup/templates/backup.sh.j2 @@ -32,6 +32,12 @@ docker exec plane-db pg_dump -U plane plane \ | gzip > "${WORK_DIR}/data/databases/plane.sql.gz" log " → databases/plane.sql.gz ($(du -sh "${WORK_DIR}/data/databases/plane.sql.gz" | cut -f1))" +# ── PostgreSQL: Outline ────────────────────────────────────────────────────── +log "Dumping outline-db..." +docker exec outline-db pg_dump -U outline outline \ + | gzip > "${WORK_DIR}/data/databases/outline.sql.gz" +log " → databases/outline.sql.gz ($(du -sh "${WORK_DIR}/data/databases/outline.sql.gz" | cut -f1))" + # ── Forgejo data volume (repos, attachments, LFS) ─────────────────────────── log "Backing up Forgejo data..." docker run --rm \ @@ -50,6 +56,24 @@ docker run --rm \ tar czf /backup/uptime-kuma.tar.gz /app/data log " → volumes/uptime-kuma.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/uptime-kuma.tar.gz" | cut -f1))" +# ── n8n workflows + credentials ────────────────────────────────────────────── +log "Backing up n8n..." +docker run --rm \ + --volumes-from n8n \ + -v "${WORK_DIR}/data/volumes:/backup" \ + alpine:3 \ + tar czf /backup/n8n.tar.gz /home/node/.n8n +log " → volumes/n8n.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/n8n.tar.gz" | cut -f1))" + +# ── Plane MinIO (uploaded files / attachments) ─────────────────────────────── +log "Backing up Plane MinIO..." +docker run --rm \ + --volumes-from plane-minio \ + -v "${WORK_DIR}/data/volumes:/backup" \ + alpine:3 \ + tar czf /backup/plane-minio.tar.gz /data +log " → volumes/plane-minio.tar.gz ($(du -sh "${WORK_DIR}/data/volumes/plane-minio.tar.gz" | cut -f1))" + # ── Add restore instructions ───────────────────────────────────────────────── cat > "${WORK_DIR}/data/RESTORE.md" << 'RESTORE_EOF' # Restore Instructions @@ -72,6 +96,9 @@ zcat data/databases/forgejo.sql.gz | docker exec -i forgejo-db psql -U forgejo f # Plane DB zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane + +# Outline DB +zcat data/databases/outline.sql.gz | docker exec -i outline-db psql -U outline outline ``` ## Step 3 — Restore volume data @@ -80,9 +107,17 @@ zcat data/databases/plane.sql.gz | docker exec -i plane-db psql -U plane plane docker run --rm --volumes-from forgejo -v $(pwd)/data/volumes:/backup \ alpine:3 sh -c "cd / && tar xzf /backup/forgejo.tar.gz" -# Uptime Kuma — extracts /app/data/ into the container +# Uptime Kuma docker run --rm --volumes-from uptime-kuma -v $(pwd)/data/volumes:/backup \ alpine:3 sh -c "cd / && tar xzf /backup/uptime-kuma.tar.gz" + +# n8n +docker run --rm --volumes-from n8n -v $(pwd)/data/volumes:/backup \ + alpine:3 sh -c "cd / && tar xzf /backup/n8n.tar.gz" + +# Plane MinIO (uploaded files) +docker run --rm --volumes-from plane-minio -v $(pwd)/data/volumes:/backup \ + alpine:3 sh -c "cd / && tar xzf /backup/plane-minio.tar.gz" ``` ## Step 4 — Restart services diff --git a/roles/services/defaults/main.yml b/roles/services/defaults/main.yml index fb67020..873e756 100644 --- a/roles/services/defaults/main.yml +++ b/roles/services/defaults/main.yml @@ -28,3 +28,8 @@ promtail_image: "grafana/promtail:3.4.3" # https://hub crowdsec_image: "crowdsecurity/crowdsec:v1.6.8" # https://hub.docker.com/r/crowdsecurity/crowdsec/tags redis_image: "redis:7-alpine" uptime_kuma_image: "louislam/uptime-kuma:1" # https://hub.docker.com/r/louislam/uptime-kuma/tags +outline_image: "outlinewiki/outline:0.80.2" # https://hub.docker.com/r/outlinewiki/outline/tags +outline_db_image: "postgres:15-alpine" +outline_redis_image: "redis:7-alpine" +n8n_image: "n8nio/n8n:1.89.2" # https://hub.docker.com/r/n8nio/n8n/tags +outline_mcp_image: "git.{{ domain_base }}/jack/outline-mcp:latest" diff --git a/roles/services/tasks/configs.yml b/roles/services/tasks/configs.yml index fc80180..4b79a8a 100644 --- a/roles/services/tasks/configs.yml +++ b/roles/services/tasks/configs.yml @@ -8,6 +8,15 @@ mode: "0600" notify: Restart stack +- name: Deploy Outline .env file + ansible.builtin.template: + src: env.outline.j2 + dest: "{{ services_root }}/.env.outline" + owner: "{{ deploy_user }}" + group: "{{ deploy_group }}" + mode: "0600" + notify: Restart stack + - name: Deploy docker-compose.yml ansible.builtin.template: src: docker-compose.yml.j2 @@ -143,24 +152,6 @@ mode: "0644" notify: Restart stack -- name: Deploy Authelia configuration - ansible.builtin.template: - src: authelia/configuration.yml.j2 - dest: "{{ services_root }}/authelia/configuration.yml" - owner: "{{ deploy_user }}" - group: "{{ deploy_group }}" - mode: "0600" - notify: Restart stack - -- name: Deploy Authelia users database - ansible.builtin.template: - src: authelia/users.yml.j2 - dest: "{{ services_root }}/authelia/users.yml" - owner: "{{ deploy_user }}" - group: "{{ deploy_group }}" - mode: "0600" - notify: Restart stack - - name: Deploy Traefik logrotate config ansible.builtin.template: src: logrotate/traefik.j2 diff --git a/roles/services/templates/docker-compose.yml.j2 b/roles/services/templates/docker-compose.yml.j2 index 2a7979f..012a62b 100644 --- a/roles/services/templates/docker-compose.yml.j2 +++ b/roles/services/templates/docker-compose.yml.j2 @@ -26,6 +26,12 @@ networks: monitoring: driver: bridge internal: true + outline-internal: + driver: bridge + internal: true + n8n-internal: + driver: bridge + internal: true volumes: forgejo_data: forgejo_db_data: @@ -39,6 +45,9 @@ volumes: loki_data: crowdsec_data: uptime_kuma_data: + outline_db_data: + outline_redis_data: + n8n_data: services: @@ -595,3 +604,123 @@ services: options: max-size: "5m" max-file: "2" + + # ── Outline wiki ──────────────────────────────────────────────────────────── + outline: + image: {{ outline_image }} + container_name: outline + restart: unless-stopped + env_file: .env.outline + networks: + - outline-internal + - backend + depends_on: + outline-db: + condition: service_healthy + outline-redis: + condition: service_healthy + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:3000/_health"] + interval: 30s + timeout: 5s + retries: 3 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + outline-db: + image: {{ outline_db_image }} + container_name: outline-db + restart: unless-stopped + environment: + POSTGRES_DB: outline + POSTGRES_USER: outline + POSTGRES_PASSWORD: {{ outline_db_password }} + networks: + - outline-internal + volumes: + - outline_db_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U outline"] + interval: 10s + timeout: 5s + retries: 5 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + outline-redis: + image: {{ outline_redis_image }} + container_name: outline-redis + restart: unless-stopped + networks: + - outline-internal + volumes: + - outline_redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 5s + retries: 5 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + # ── n8n workflow automation ────────────────────────────────────────────────── + n8n: + image: {{ n8n_image }} + container_name: n8n + restart: unless-stopped + networks: + - n8n-internal + - backend + volumes: + - n8n_data:/home/node/.n8n + environment: + - N8N_HOST={{ domain_n8n }} + - N8N_PORT=5678 + - N8N_PROTOCOL=https + - WEBHOOK_URL=https://{{ domain_n8n }}/ + - N8N_ENCRYPTION_KEY={{ n8n_encryption_key }} + - N8N_USER_MANAGEMENT_JWT_SECRET={{ n8n_jwt_secret }} + - GENERIC_TIMEZONE=Europe/Moscow + - TZ=Europe/Moscow + - N8N_METRICS=false + - N8N_LOG_LEVEL=warn + - EXECUTIONS_DATA_PRUNE=true + - EXECUTIONS_DATA_MAX_AGE=336 + healthcheck: + test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/healthz"] + interval: 30s + timeout: 5s + retries: 3 + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" + + # ── Outline MCP server ─────────────────────────────────────────────────────── + outline-mcp: + image: {{ outline_mcp_image }} + container_name: outline-mcp + restart: unless-stopped + networks: + - backend + environment: + - OUTLINE_URL=https://{{ domain_wiki }} + - OUTLINE_API_KEY={{ outline_mcp_api_key }} + - PORT=8765 + - HOST=0.0.0.0 + - LOG_LEVEL=INFO + logging: + driver: json-file + options: + max-size: "10m" + max-file: "3" diff --git a/roles/services/templates/env.outline.j2 b/roles/services/templates/env.outline.j2 new file mode 100644 index 0000000..19d0af5 --- /dev/null +++ b/roles/services/templates/env.outline.j2 @@ -0,0 +1,42 @@ +# Outline env — generated by Ansible +NODE_ENV=production +SECRET_KEY={{ outline_secret_key }} +UTILS_SECRET={{ outline_utils_secret }} + +# Database +DATABASE_URL=postgres://outline:{{ outline_db_password }}@outline-db:5432/outline +PGSSLMODE=disable + +# Redis +REDIS_URL=redis://outline-redis:6379 + +# App URL +URL=https://{{ domain_wiki }} +PORT=3000 + +# S3 file storage (Timeweb Object Storage) +AWS_ACCESS_KEY_ID={{ s3_access_key }} +AWS_SECRET_ACCESS_KEY={{ s3_secret_key }} +AWS_REGION=ru-1 +AWS_S3_UPLOAD_BUCKET_NAME=walava-outline +AWS_S3_UPLOAD_BUCKET_URL=https://s3.timeweb.cloud +AWS_S3_FORCE_PATH_STYLE=true +AWS_S3_ACL=private +FILE_STORAGE=s3 + +# Auth +AUTH_PROVIDERS=email + +# SMTP via Resend (direct — main server has outbound SMTP) +SMTP_HOST=smtp.resend.com +SMTP_PORT=587 +SMTP_USERNAME=resend +SMTP_PASSWORD={{ resend_api_key }} +SMTP_FROM_EMAIL=noreply@{{ domain_base }} +SMTP_FROM_NAME=Visual Wiki +SMTP_SECURE=false + +# Optional +DEFAULT_LANGUAGE=en_US +RATE_LIMITER_ENABLED=true +ENABLE_UPDATES=false diff --git a/roles/services/templates/traefik/dynamic/routes.yml.j2 b/roles/services/templates/traefik/dynamic/routes.yml.j2 index 72dc9d0..da8b793 100644 --- a/roles/services/templates/traefik/dynamic/routes.yml.j2 +++ b/roles/services/templates/traefik/dynamic/routes.yml.j2 @@ -89,7 +89,6 @@ http: service: walava-landing middlewares: [rate-limit-default] - # ── Cross-server: tools ({{ ip_tools }}) ───────────────────────────────── wiki: rule: "Host(`{{ domain_wiki }}`)" entrypoints: [websecure] @@ -147,16 +146,15 @@ http: servers: - url: "http://walava-web:80" - # ── Cross-server services ───────────────────────────────────────────────── wiki: loadBalancer: servers: - - url: "http://{{ ip_tools }}:3000" + - url: "http://outline:3000" n8n: loadBalancer: servers: - - url: "http://{{ ip_tools }}:5678" + - url: "http://n8n:5678" middlewares: # ── Security Headers (applied globally via entrypoint) ─────────────────