diff --git a/galaxy.yml b/galaxy.yml index c208d8f..91cd337 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -19,7 +19,6 @@ readme: README.md authors: - Bert-Jan Fikse - Tobias Wüst -- Simon Bärlocher ### OPTIONAL but strongly recommended # A short summary description of the collection diff --git a/roles/bookstack/README.md b/roles/bookstack/README.md deleted file mode 100644 index 25fb789..0000000 --- a/roles/bookstack/README.md +++ /dev/null @@ -1,145 +0,0 @@ -# Ansible Role: bookstack - -Deploys [BookStack](https://www.bookstackapp.com/) as a self-contained Docker -Compose stack behind Traefik, with its own MariaDB container, OIDC SSO -(Entra ID by default) and a daily systemd-timer driven backup of database -and uploads. - -## Requirements - -- Docker Engine + Compose plugin on the target host -- Traefik already running, with the external network referenced by - `bookstack_traefik_network` (default: `proxy`) -- `community.docker` collection on the controller -- DNS for `bookstack_domain` pointing at the Traefik host - -## Required variables - -The role asserts these are set; the play fails fast if any is empty: - -| Variable | Description | -|---|---| -| `bookstack_db_root_password` | MariaDB root password | -| `bookstack_db_password` | MariaDB user password | -| `bookstack_admin_password` | Initial local admin password | -| `bookstack_oidc_client_id` | Entra ID App Registration ID (if OIDC on) | -| `bookstack_oidc_client_secret` | Entra ID client secret (if OIDC on) | -| `bookstack_entra_tenant_id` | Entra tenant UUID (if OIDC on) | - -Provide via OpenBao lookup, Ansible Vault or `--extra-vars`. Never commit -real secrets. - -## Optional variables - -See `defaults/main.yml`. Frequently overridden: - -- `bookstack_domain`, `bookstack_base_url` -- `bookstack_image`, `bookstack_db_image` (pin in production) -- `bookstack_oidc_enabled` (set `false` to disable OIDC entirely) -- `bookstack_oidc_auto_initiate` (`true` redirects straight to IdP) -- `bookstack_oidc_user_to_groups` (`true` syncs roles from Entra groups) -- `bookstack_backup_enabled`, `bookstack_backup_schedule`, - `bookstack_backup_retention_days` - -## Entra ID app registration - -1. Azure Portal → Entra ID → App registrations → New registration -2. Redirect URI (Web): `https:///oidc/callback` -3. Front-channel logout URL: `https:///logout` -4. Certificates & secrets → New client secret → - `bookstack_oidc_client_secret` -5. For group sync (`bookstack_oidc_user_to_groups: true`): - - Token configuration → Add groups claim → Security groups - - In BookStack, create roles whose **External Auth ID** equals the - Entra group Object ID, so the mapping resolves on first login. - -## What the role does - -| Phase | Action | -|---|---| -| Validate | `assert` all required secrets are set | -| Prepare | install packages, create volume dirs, generate persistent `APP_KEY`, verify Traefik network | -| Deploy | render `docker-compose.yml`, pull images, bring stack up | -| Configure | wait for the app, create the initial local admin via `php artisan bookstack:create-admin` (idempotent) | -| Backup | render `/usr/local/bin/bookstack-backup.sh` + systemd timer (daily 03:00, 14-day retention) | - -## Example playbook - -```yaml -- name: Deploy BookStack service - hosts: bookstack_servers - become: true - roles: - - digitalboard.core.bookstack -``` - -With inventory variables: - -```yaml -# group_vars/bookstack_servers.yml -bookstack_domain: wiki.digitalboard.ch -bookstack_base_url: "https://wiki.digitalboard.ch" -bookstack_entra_tenant_id: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.tenant_id }}" -bookstack_oidc_client_id: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.client_id }}" -bookstack_oidc_client_secret: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.client_secret }}" -bookstack_db_root_password: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.db_root_password }}" -bookstack_db_password: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.db_password }}" -bookstack_admin_password: "{{ lookup('community.hashi_vault.vault_kv2_get', - 'digitalboard/bookstack', - mount_point='kv').data.data.admin_password }}" -``` - -## Backup / restore - -Backups land in `{{ bookstack_backup_dir }}` (default -`/srv/data/bookstack/backup`) with three files per run: - -- `bookstack-db-.sql.gz` — mariadb-dump -- `bookstack-files-.tar.gz` — uploads, attachments -- `bookstack-appkey-.txt` — APP_KEY (required for restore!) - -Manual trigger: `systemctl start bookstack-backup.service` -Timer status: `systemctl list-timers bookstack-backup.timer` - -Restore procedure: - -1. Stop the stack: `docker compose down` in `bookstack_docker_compose_dir` -2. Restore the APP_KEY: copy the `.txt` content to - `{{ bookstack_docker_volume_dir }}/.app_key` (the key MUST match or - encrypted DB values become unreadable) -3. Start only the DB container, then load the dump: - ```bash - gunzip -c bookstack-db-.sql.gz \ - | docker exec -i bookstack-db \ - mariadb -u root -p"" bookstack - ``` -4. Extract the files: `tar -xzf bookstack-files-.tar.gz -C - {{ bookstack_appdata_dir }}/www/` -5. Bring the stack back up: `docker compose up -d` - -## Notes - -- `bookstack_oidc_auto_initiate: false` (default) shows a login page - with an SSO button alongside the local login form. With `true`, users - go straight to the IdP — the local admin then has to use - `https:///login?email_login=1`. -- `bookstack_oidc_user_to_groups: true` only makes sense once BookStack - roles with the correct **External Auth IDs** (= Entra group Object - IDs) exist; otherwise users lose their role assignment on every login. -- Image tags default to pinned versions; bump them deliberately rather - than chasing `latest`. -- BookStack officially supports MySQL/MariaDB only — no PostgreSQL. - -## License - -MIT diff --git a/roles/bookstack/defaults/main.yml b/roles/bookstack/defaults/main.yml deleted file mode 100644 index 3efbadb..0000000 --- a/roles/bookstack/defaults/main.yml +++ /dev/null @@ -1,85 +0,0 @@ -#SPDX-License-Identifier: MIT-0 ---- -# defaults file for bookstack - -# Base directory configuration (inherited from base role or defined here) -docker_compose_base_dir: /etc/docker/compose -docker_volume_base_dir: /srv/data - -# bookstack-specific configuration -bookstack_service_name: bookstack -bookstack_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ bookstack_service_name }}" -bookstack_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ bookstack_service_name }}" -bookstack_appdata_dir: "{{ bookstack_docker_volume_dir }}/appdata" -bookstack_db_data_dir: "{{ bookstack_docker_volume_dir }}/db" -bookstack_backup_dir: "{{ bookstack_docker_volume_dir }}/backup" - -# Service configuration -bookstack_domain: "wiki.local.test" -bookstack_base_url: "https://{{ bookstack_domain }}" - -# Images — pin via inventory in production -bookstack_image: "lscr.io/linuxserver/bookstack:version-v26.03.3" -bookstack_db_image: "lscr.io/linuxserver/mariadb:11.4.9" - -# Traefik configuration -bookstack_traefik_network: "proxy" -bookstack_traefik_certresolver: "le" - -# Timezone / UID -bookstack_tz: "Europe/Zurich" -bookstack_puid: "1000" -bookstack_pgid: "1000" - -# Database configuration -bookstack_db_name: "bookstack" -bookstack_db_user: "bookstack" - -# REQUIRED SECRETS — empty defaults force `assert` to fail until set. -# Provide via OpenBao lookup, Ansible Vault, or extra-vars. -# Never commit real secrets to version control. -# -# Generate with: -# bookstack_db_root_password: openssl rand -base64 32 | tr -d '/+=' -# bookstack_db_password: openssl rand -base64 32 | tr -d '/+=' -# bookstack_admin_password: openssl rand -base64 24 | tr -d '/+=' -bookstack_db_root_password: "" -bookstack_db_password: "" -bookstack_admin_password: "" -bookstack_oidc_client_secret: "" - -# APP_KEY is generated automatically on first run and persisted on the host. -# Set explicitly only if restoring an existing instance. -bookstack_app_key: "" - -# Initial local admin (fallback account, lives alongside OIDC) -bookstack_admin_name: "Admin" -bookstack_admin_email: "admin@local.test" -bookstack_artisan_path: "/app/www/artisan" - -# Mail configuration -bookstack_mail_driver: "smtp" -bookstack_mail_host: "smtp.local.test" -bookstack_mail_port: 587 -bookstack_mail_encryption: "tls" -bookstack_mail_from: "bookstack@local.test" -bookstack_mail_from_name: "BookStack" -bookstack_mail_username: "" -bookstack_mail_password: "" - -# OIDC configuration (Entra ID by default; override `bookstack_oidc_issuer` -# for Keycloak or any other provider) -bookstack_oidc_enabled: false -bookstack_oidc_name: "SSO" -bookstack_entra_tenant_id: "" -bookstack_oidc_issuer: "https://login.microsoftonline.com/{{ bookstack_entra_tenant_id }}/v2.0" -bookstack_oidc_client_id: "" -bookstack_oidc_auto_initiate: false -bookstack_oidc_user_to_groups: false -bookstack_oidc_groups_claim: "groups" -bookstack_oidc_additional_scopes: "openid profile email" - -# Backup configuration -bookstack_backup_enabled: true -bookstack_backup_retention_days: 14 -bookstack_backup_schedule: "*-*-* 03:00:00" diff --git a/roles/bookstack/handlers/main.yml b/roles/bookstack/handlers/main.yml deleted file mode 100644 index eb6a769..0000000 --- a/roles/bookstack/handlers/main.yml +++ /dev/null @@ -1,19 +0,0 @@ -#SPDX-License-Identifier: MIT-0 ---- -# handlers file for bookstack - -- name: stop bookstack - community.docker.docker_compose_v2: - project_src: "{{ bookstack_docker_compose_dir }}" - state: stopped - listen: restart bookstack - -- name: start bookstack - community.docker.docker_compose_v2: - project_src: "{{ bookstack_docker_compose_dir }}" - state: present - listen: restart bookstack - -- name: reload systemd - ansible.builtin.systemd: - daemon_reload: true diff --git a/roles/bookstack/meta/argument_specs.yml b/roles/bookstack/meta/argument_specs.yml deleted file mode 100644 index 8546cde..0000000 --- a/roles/bookstack/meta/argument_specs.yml +++ /dev/null @@ -1,194 +0,0 @@ ---- -argument_specs: - main: - short_description: Deploy BookStack (LSIO image + MariaDB) via Docker Compose. - description: - - Renders a Compose stack for the linuxserver.io BookStack image - with a sibling MariaDB container behind Traefik, then bootstraps - the initial admin user via C(php artisan bookstack:create-admin) - and optionally enables OIDC SSO (Entra ID by default). - - "Persists the Laravel C(APP_KEY) on the host so the same key is - re-used across deploys (a fresh key would orphan all encrypted - database values: 2FA secrets, API tokens, OIDC client_secret)." - - Ships an optional systemd timer that backs up the database dump, - uploads tarball and APP_KEY daily with configurable retention. - options: - docker_compose_base_dir: - type: path - default: /etc/docker/compose - docker_volume_base_dir: - type: path - default: /srv/data - bookstack_service_name: - type: str - default: bookstack - bookstack_docker_compose_dir: - type: path - bookstack_docker_volume_dir: - type: path - bookstack_appdata_dir: - type: path - bookstack_db_data_dir: - type: path - bookstack_backup_dir: - type: path - - bookstack_domain: - type: str - default: wiki.local.test - description: Hostname used in the Traefik Host rule. - bookstack_base_url: - type: str - description: Defaults to C("https://{{ bookstack_domain }}"). - - bookstack_image: - type: str - default: "lscr.io/linuxserver/bookstack:version-v26.03.3" - bookstack_db_image: - type: str - default: "lscr.io/linuxserver/mariadb:11.4.9" - - bookstack_traefik_network: - type: str - default: proxy - bookstack_traefik_certresolver: - type: str - default: le - - bookstack_tz: - type: str - default: Europe/Zurich - bookstack_puid: - type: str - default: "1000" - bookstack_pgid: - type: str - default: "1000" - - bookstack_db_name: - type: str - default: bookstack - bookstack_db_user: - type: str - default: bookstack - bookstack_db_root_password: - type: str - required: true - description: MariaDB C(root) password. Override per-inventory. - bookstack_db_password: - type: str - required: true - description: MariaDB C(bookstack_db_user) password. Override per-inventory. - - bookstack_admin_password: - type: str - required: true - description: - - Password for the local admin user that the role creates via - C(bookstack:create-admin). Lives alongside any OIDC users. - - bookstack_app_key: - type: str - default: '' - description: - - When empty the role generates a persistent C(APP_KEY) on first - run and stores it under C({{ bookstack_docker_volume_dir }}/.app_key). - Override only when restoring an existing instance — a mismatching - key orphans all encrypted database values. - - bookstack_admin_name: - type: str - default: Admin - bookstack_admin_email: - type: str - default: admin@local.test - bookstack_artisan_path: - type: path - default: /app/www/artisan - description: - - Path to BookStack's C(artisan) script inside the container. The - LSIO image's C(WORKDIR) is not the app directory, so this must - be absolute. - - bookstack_mail_driver: - type: str - choices: [smtp, log, sendmail, mailgun, ses, postmark] - default: smtp - bookstack_mail_host: - type: str - default: smtp.local.test - bookstack_mail_port: - type: int - default: 587 - bookstack_mail_encryption: - type: str - choices: [tls, ssl, ''] - default: tls - bookstack_mail_from: - type: str - default: bookstack@local.test - bookstack_mail_from_name: - type: str - default: BookStack - bookstack_mail_username: - type: str - default: '' - bookstack_mail_password: - type: str - default: '' - - bookstack_oidc_enabled: - type: bool - default: false - bookstack_oidc_name: - type: str - default: SSO - description: Display name of the SSO button on the login page. - bookstack_entra_tenant_id: - type: str - default: '' - description: Entra tenant UUID. Required when C(bookstack_oidc_enabled=true). - bookstack_oidc_issuer: - type: str - description: - - OIDC issuer URL. Defaults to the Entra v2 issuer template - built from C(bookstack_entra_tenant_id). Override for - Keycloak or any other provider. - bookstack_oidc_client_id: - type: str - default: '' - description: Required when C(bookstack_oidc_enabled=true). - bookstack_oidc_client_secret: - type: str - default: '' - description: Required when C(bookstack_oidc_enabled=true). - bookstack_oidc_auto_initiate: - type: bool - default: false - description: - - When true users are redirected straight to the IdP and the - local login is reachable only via C(?email_login=1). - bookstack_oidc_user_to_groups: - type: bool - default: false - description: - - When true BookStack syncs roles from the IdP groups claim - on every login. Requires BookStack roles whose - C(External Auth ID) matches the IdP group's Object ID. - bookstack_oidc_groups_claim: - type: str - default: groups - bookstack_oidc_additional_scopes: - type: str - default: openid profile email - - bookstack_backup_enabled: - type: bool - default: true - bookstack_backup_retention_days: - type: int - default: 14 - bookstack_backup_schedule: - type: str - default: "*-*-* 03:00:00" - description: systemd C(OnCalendar) expression for the backup timer. diff --git a/roles/bookstack/meta/main.yml b/roles/bookstack/meta/main.yml deleted file mode 100644 index a6e941d..0000000 --- a/roles/bookstack/meta/main.yml +++ /dev/null @@ -1,25 +0,0 @@ -galaxy_info: - author: digitalboard - description: Deploy BookStack as a self-contained Docker Compose stack behind Traefik - company: digitalboard - license: MIT - - min_ansible_version: "2.14" - - platforms: - - name: Debian - versions: - - bookworm - - name: Ubuntu - versions: - - jammy - - noble - - galaxy_tags: - - docker - - bookstack - - wiki - - documentation - - digitalboard - -dependencies: [] diff --git a/roles/bookstack/tasks/main.yml b/roles/bookstack/tasks/main.yml deleted file mode 100644 index 1ea325b..0000000 --- a/roles/bookstack/tasks/main.yml +++ /dev/null @@ -1,223 +0,0 @@ -#SPDX-License-Identifier: MIT-0 ---- -# tasks file for bookstack - -# ===================================================================== -# 1. VALIDATE REQUIRED SECRETS -# ===================================================================== - -- name: Assert required secrets are set - ansible.builtin.assert: - that: - - bookstack_db_root_password | length > 0 - - bookstack_db_password | length > 0 - - bookstack_admin_password | length > 0 - - (not bookstack_oidc_enabled) or (bookstack_oidc_client_id | length > 0) - - (not bookstack_oidc_enabled) or (bookstack_oidc_client_secret | length > 0) - - (not bookstack_oidc_enabled) or (bookstack_entra_tenant_id | length > 0) - fail_msg: >- - One or more required secrets are unset. Provide them via OpenBao - lookup, Ansible Vault or --extra-vars. See README for the full list. - quiet: true - -# ===================================================================== -# 2. PREPARATION: Packages, directories, APP_KEY -# ===================================================================== - -- name: Ensure required packages are installed - ansible.builtin.package: - name: - - python3-docker - - python3-requests - state: present - -- name: Create docker compose directory - ansible.builtin.file: - path: "{{ bookstack_docker_compose_dir }}" - state: directory - mode: '0755' - -- name: Create BookStack data directories - ansible.builtin.file: - path: "{{ item }}" - state: directory - owner: "{{ bookstack_puid }}" - group: "{{ bookstack_pgid }}" - mode: '0755' - loop: - - "{{ bookstack_docker_volume_dir }}" - - "{{ bookstack_appdata_dir }}" - - "{{ bookstack_db_data_dir }}" - - "{{ bookstack_backup_dir }}" - -- name: Verify Traefik network exists - community.docker.docker_network_info: - name: "{{ bookstack_traefik_network }}" - register: _traefik_net - failed_when: not _traefik_net.exists - -- name: Check whether APP_KEY has been generated before - ansible.builtin.stat: - path: "{{ bookstack_docker_volume_dir }}/.app_key" - register: _app_key_file - -- name: Generate persistent APP_KEY on first run - ansible.builtin.shell: | - set -o pipefail - umask 077 - echo "base64:$(openssl rand -base64 32)" > {{ bookstack_docker_volume_dir }}/.app_key - args: - executable: /bin/bash - creates: "{{ bookstack_docker_volume_dir }}/.app_key" - when: - - not _app_key_file.stat.exists - - bookstack_app_key | length == 0 - -- name: Write inventory-provided APP_KEY - ansible.builtin.copy: - content: "{{ bookstack_app_key }}\n" - dest: "{{ bookstack_docker_volume_dir }}/.app_key" - mode: '0600' - when: - - not _app_key_file.stat.exists - - bookstack_app_key | length > 0 - no_log: true - -- name: Read APP_KEY back into a fact - ansible.builtin.slurp: - src: "{{ bookstack_docker_volume_dir }}/.app_key" - register: _app_key_slurp - no_log: true - -- name: Register APP_KEY fact - ansible.builtin.set_fact: - bookstack_resolved_app_key: "{{ _app_key_slurp.content | b64decode | trim }}" - no_log: true - -# ===================================================================== -# 3. DEPLOY: Render compose, bring stack up -# ===================================================================== - -- name: Render docker-compose.yml for BookStack - ansible.builtin.template: - src: docker-compose.yml.j2 - dest: "{{ bookstack_docker_compose_dir }}/docker-compose.yml" - mode: '0640' - notify: restart bookstack - -- name: Start BookStack containers - community.docker.docker_compose_v2: - project_src: "{{ bookstack_docker_compose_dir }}" - state: present - pull: always - wait: true - -# ===================================================================== -# 4. CONFIGURE: Wait for app and seed initial admin user -# ===================================================================== - -- name: Wait for BookStack to be ready - ansible.builtin.command: - cmd: docker exec {{ bookstack_service_name }} curl -sf -o /dev/null -w "%{http_code}" http://localhost/login - register: _bookstack_health - retries: 30 - delay: 5 - until: _bookstack_health.stdout == "200" - changed_when: false - -- name: Wait for BookStack migrations to be complete - community.docker.docker_container_exec: - container: "{{ bookstack_service_name }}-db" - argv: - - mariadb - - --protocol=tcp - - -h - - 127.0.0.1 - - -u - - "{{ bookstack_db_user }}" - - "-p{{ bookstack_db_password }}" - - "{{ bookstack_db_name }}" - - -Nse - - "SHOW TABLES LIKE 'users';" - register: _users_table - retries: 30 - delay: 5 - until: _users_table.stdout | trim == 'users' - changed_when: false - no_log: true - -- name: Check whether the initial admin already exists - community.docker.docker_container_exec: - container: "{{ bookstack_service_name }}-db" - argv: - - mariadb - - --protocol=tcp - - -h - - 127.0.0.1 - - -u - - "{{ bookstack_db_user }}" - - "-p{{ bookstack_db_password }}" - - "{{ bookstack_db_name }}" - - -Nse - - "SELECT COUNT(*) FROM users WHERE email = '{{ bookstack_admin_email }}';" - register: _admin_exists - changed_when: false - no_log: true - -- name: Create initial admin user - community.docker.docker_container_exec: - container: "{{ bookstack_service_name }}" - argv: - - php - - "{{ bookstack_artisan_path }}" - - bookstack:create-admin - - "--email={{ bookstack_admin_email }}" - - "--name={{ bookstack_admin_name }}" - - "--password={{ bookstack_admin_password }}" - when: (_admin_exists.stdout | trim | int) == 0 - no_log: true - -# ===================================================================== -# 5. BACKUP: systemd timer for daily DB + uploads dump -# ===================================================================== - -- name: Render backup script - ansible.builtin.template: - src: backup.sh.j2 - dest: /usr/local/bin/bookstack-backup.sh - owner: root - group: root - mode: '0750' - when: bookstack_backup_enabled | bool - -- name: Render backup systemd service - ansible.builtin.template: - src: bookstack-backup.service.j2 - dest: /etc/systemd/system/bookstack-backup.service - mode: '0644' - when: bookstack_backup_enabled | bool - notify: reload systemd - -- name: Render backup systemd timer - ansible.builtin.template: - src: bookstack-backup.timer.j2 - dest: /etc/systemd/system/bookstack-backup.timer - mode: '0644' - when: bookstack_backup_enabled | bool - notify: reload systemd - -- name: Enable and start backup timer - ansible.builtin.systemd: - name: bookstack-backup.timer - enabled: true - state: started - daemon_reload: true - when: bookstack_backup_enabled | bool - -- name: Disable backup timer when feature is off - ansible.builtin.systemd: - name: bookstack-backup.timer - enabled: false - state: stopped - when: not (bookstack_backup_enabled | bool) - failed_when: false diff --git a/roles/bookstack/templates/backup.sh.j2 b/roles/bookstack/templates/backup.sh.j2 deleted file mode 100644 index 65217c2..0000000 --- a/roles/bookstack/templates/backup.sh.j2 +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash -# {{ ansible_managed }} -set -euo pipefail - -BACKUP_DIR="{{ bookstack_backup_dir }}" -RETENTION_DAYS={{ bookstack_backup_retention_days }} -APPDATA_DIR="{{ bookstack_appdata_dir }}" -STAMP="$(date +%Y%m%d-%H%M%S)" - -mkdir -p "$BACKUP_DIR" - -# --- DB dump (mariadb-dump from inside the DB container) --- -# Use the app user via TCP because root@localhost is unix_socket-auth only -# in the LSIO MariaDB image and root@% does not exist. -docker exec {{ bookstack_service_name }}-db \ - mariadb-dump \ - --protocol=tcp -h 127.0.0.1 \ - -u "{{ bookstack_db_user }}" -p"{{ bookstack_db_password }}" \ - --single-transaction --routines --triggers --quick \ - "{{ bookstack_db_name }}" \ - | gzip -9 > "$BACKUP_DIR/bookstack-db-$STAMP.sql.gz" - -# --- File uploads (images, attachments) --- -# LSIO BookStack stores user uploads under /config/www/{uploads,storage/uploads,files}. -tar --warning=no-file-changed \ - -czf "$BACKUP_DIR/bookstack-files-$STAMP.tar.gz" \ - -C "$APPDATA_DIR/www" \ - uploads storage/uploads files 2>/dev/null || true - -# --- APP_KEY backup (critical for restore!) --- -install -m 0600 "{{ bookstack_docker_volume_dir }}/.app_key" \ - "$BACKUP_DIR/bookstack-appkey-$STAMP.txt" - -# --- Retention --- -find "$BACKUP_DIR" -type f \ - \( -name 'bookstack-db-*.sql.gz' \ - -o -name 'bookstack-files-*.tar.gz' \ - -o -name 'bookstack-appkey-*.txt' \) \ - -mtime +"$RETENTION_DAYS" -delete - -echo "Backup complete: $STAMP" \ No newline at end of file diff --git a/roles/bookstack/templates/bookstack-backup.service.j2 b/roles/bookstack/templates/bookstack-backup.service.j2 deleted file mode 100644 index cb63795..0000000 --- a/roles/bookstack/templates/bookstack-backup.service.j2 +++ /dev/null @@ -1,12 +0,0 @@ -# {{ ansible_managed }} -[Unit] -Description=BookStack backup (DB + uploads) -Requires=docker.service -After=docker.service - -[Service] -Type=oneshot -ExecStart=/usr/local/bin/bookstack-backup.sh -Nice=10 -IOSchedulingClass=best-effort -IOSchedulingPriority=7 diff --git a/roles/bookstack/templates/bookstack-backup.timer.j2 b/roles/bookstack/templates/bookstack-backup.timer.j2 deleted file mode 100644 index e13238d..0000000 --- a/roles/bookstack/templates/bookstack-backup.timer.j2 +++ /dev/null @@ -1,11 +0,0 @@ -# {{ ansible_managed }} -[Unit] -Description=Daily BookStack backup - -[Timer] -OnCalendar={{ bookstack_backup_schedule }} -Persistent=true -RandomizedDelaySec=300 - -[Install] -WantedBy=timers.target diff --git a/roles/bookstack/templates/docker-compose.yml.j2 b/roles/bookstack/templates/docker-compose.yml.j2 deleted file mode 100644 index 863e316..0000000 --- a/roles/bookstack/templates/docker-compose.yml.j2 +++ /dev/null @@ -1,87 +0,0 @@ -#---------------------------------------------------------------------# -# BookStack - Self-hosted wiki / knowledge base. # -#---------------------------------------------------------------------# ---- -services: - {{ bookstack_service_name }}: - image: {{ bookstack_image }} - container_name: {{ bookstack_service_name }} - restart: unless-stopped - environment: - PUID: "{{ bookstack_puid }}" - PGID: "{{ bookstack_pgid }}" - TZ: "{{ bookstack_tz }}" - APP_URL: "{{ bookstack_base_url }}" - APP_KEY: "{{ bookstack_resolved_app_key }}" - DB_HOST: "{{ bookstack_service_name }}-db" - DB_PORT: "3306" - DB_DATABASE: "{{ bookstack_db_name }}" - DB_USERNAME: "{{ bookstack_db_user }}" - DB_PASSWORD: "{{ bookstack_db_password }}" - MAIL_DRIVER: "{{ bookstack_mail_driver }}" - MAIL_HOST: "{{ bookstack_mail_host }}" - MAIL_PORT: "{{ bookstack_mail_port }}" - MAIL_USERNAME: "{{ bookstack_mail_username }}" - MAIL_PASSWORD: "{{ bookstack_mail_password }}" - MAIL_ENCRYPTION: "{{ bookstack_mail_encryption }}" - MAIL_FROM: "{{ bookstack_mail_from }}" - MAIL_FROM_NAME: "{{ bookstack_mail_from_name }}" -{% if bookstack_oidc_enabled %} - AUTH_METHOD: "oidc" - AUTH_AUTO_INITIATE: "{{ bookstack_oidc_auto_initiate | string | lower }}" - OIDC_NAME: "{{ bookstack_oidc_name }}" - OIDC_DISPLAY_NAME_CLAIMS: "name" - OIDC_CLIENT_ID: "{{ bookstack_oidc_client_id }}" - OIDC_CLIENT_SECRET: "{{ bookstack_oidc_client_secret }}" - OIDC_ISSUER: "{{ bookstack_oidc_issuer }}" - OIDC_ISSUER_DISCOVER: "true" - OIDC_END_SESSION_ENDPOINT: "true" - OIDC_ADDITIONAL_SCOPES: "{{ bookstack_oidc_additional_scopes }}" - OIDC_USER_TO_GROUPS: "{{ bookstack_oidc_user_to_groups | string | lower }}" - OIDC_GROUPS_CLAIM: "{{ bookstack_oidc_groups_claim }}" -{% endif %} - volumes: - - {{ bookstack_appdata_dir }}:/config - networks: - - {{ bookstack_traefik_network }} - - internal - depends_on: - {{ bookstack_service_name }}-db: - condition: service_healthy - labels: - - "traefik.enable=true" - - "traefik.docker.network={{ bookstack_traefik_network }}" - - "traefik.http.routers.{{ bookstack_service_name }}.rule=Host(`{{ bookstack_domain }}`)" - - "traefik.http.routers.{{ bookstack_service_name }}.entrypoints=websecure" - - "traefik.http.routers.{{ bookstack_service_name }}.tls=true" - - "traefik.http.routers.{{ bookstack_service_name }}.tls.certresolver={{ bookstack_traefik_certresolver }}" - - "traefik.http.services.{{ bookstack_service_name }}.loadbalancer.server.port=80" - - {{ bookstack_service_name }}-db: - image: {{ bookstack_db_image }} - container_name: {{ bookstack_service_name }}-db - restart: unless-stopped - environment: - PUID: "{{ bookstack_puid }}" - PGID: "{{ bookstack_pgid }}" - TZ: "{{ bookstack_tz }}" - MYSQL_ROOT_PASSWORD: "{{ bookstack_db_root_password }}" - MYSQL_DATABASE: "{{ bookstack_db_name }}" - MYSQL_USER: "{{ bookstack_db_user }}" - MYSQL_PASSWORD: "{{ bookstack_db_password }}" - volumes: - - {{ bookstack_db_data_dir }}:/config - networks: - - internal - healthcheck: - test: ["CMD-SHELL", "mariadb-admin ping -h 127.0.0.1 -u root --password=\"$$MYSQL_ROOT_PASSWORD\" --silent"] - interval: 10s - timeout: 5s - retries: 12 - start_period: 60s - -networks: - {{ bookstack_traefik_network }}: - external: true - internal: - driver: bridge diff --git a/roles/bookstack/tests/inventory b/roles/bookstack/tests/inventory deleted file mode 100644 index 2fbb50c..0000000 --- a/roles/bookstack/tests/inventory +++ /dev/null @@ -1 +0,0 @@ -localhost diff --git a/roles/bookstack/tests/test.yml b/roles/bookstack/tests/test.yml deleted file mode 100644 index 15b9be3..0000000 --- a/roles/bookstack/tests/test.yml +++ /dev/null @@ -1,5 +0,0 @@ ---- -- hosts: localhost - remote_user: root - roles: - - bookstack diff --git a/roles/bookstack/vars/main.yml b/roles/bookstack/vars/main.yml deleted file mode 100644 index e04b89a..0000000 --- a/roles/bookstack/vars/main.yml +++ /dev/null @@ -1,3 +0,0 @@ -#SPDX-License-Identifier: MIT-0 ---- -# vars file for bookstack diff --git a/plugins/filter/homarr_layout.py b/roles/homarr/filter_plugins/homarr_layout.py similarity index 100% rename from plugins/filter/homarr_layout.py rename to roles/homarr/filter_plugins/homarr_layout.py diff --git a/roles/homarr/filter_plugins/tests/test_homarr_layout.py b/roles/homarr/filter_plugins/tests/test_homarr_layout.py index a96d672..3a49f2b 100644 --- a/roles/homarr/filter_plugins/tests/test_homarr_layout.py +++ b/roles/homarr/filter_plugins/tests/test_homarr_layout.py @@ -15,11 +15,7 @@ import sys # Make the filter importable without having Ansible auto-discovery in # the way (it would only run during a real `ansible-playbook` invocation). -sys.path.insert( - 0, - os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', - 'plugins', 'filter') -) +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) import pytest # noqa: E402 diff --git a/roles/homarr/tasks/main.yml b/roles/homarr/tasks/main.yml index ffb0bb7..9d00cde 100644 --- a/roles/homarr/tasks/main.yml +++ b/roles/homarr/tasks/main.yml @@ -136,7 +136,7 @@ - name: Compute Homarr app layouts ansible.builtin.set_fact: - homarr_layout: "{{ homarr_apps | digitalboard.core.homarr_compute_layouts }}" + homarr_layout: "{{ homarr_apps | homarr_compute_layouts }}" - name: Show computed app layouts ansible.builtin.debug: