From 3a382719698606fced83fdd8821e21d1ea381338 Mon Sep 17 00:00:00 2001 From: il Date: Thu, 9 Jul 2026 12:22:54 +0900 Subject: [PATCH] feat(papra, romm): add feature --- ansible/inventory/group_vars/all.yaml | 63 +++++--- ansible/playbooks/app/site.yaml | 32 +++- ansible/roles/app/handlers/main.yaml | 47 ++++-- .../app/tasks/services/set_booklogr.yaml | 112 +++++++++++++ .../app/tasks/services/set_nextcloud.yaml | 2 +- .../roles/app/tasks/services/set_papra.yaml | 111 +++++++++++++ .../roles/app/tasks/services/set_romm.yaml | 146 +++++++++++++++++ .../common/tasks/services/set_kopia.yaml | 23 ++- ansible/roles/infra/handlers/main.yaml | 5 +- .../roles/infra/tasks/services/set_ldap.yaml | 9 +- config/secrets/secrets.yaml | 58 ++++--- .../app/booklogr/booklogr-api.container.j2 | 51 ++++++ .../app/booklogr/booklogr-web.container.j2 | 42 +++++ .../app/homepage/config/bookmarks.yaml.j2 | 3 + .../mastodon/mastodon-streaming.container.j2 | 2 +- .../nextcloud-cron.service | 0 .../nextcloud-cron.timer | 0 .../containers/app/papra/papra.container.j2 | 43 +++++ .../app/papra/script/papra-exporter.sh | 151 ++++++++++++++++++ .../app/papra/services/papra-backup.service | 68 ++++++++ .../app/papra/services/papra-backup.timer | 14 ++ .../containers/app/romm/romm.container.j2 | 80 ++++++++++ .../auth/authelia/config/authelia.yaml.j2 | 110 ++++++++++--- .../containers/common/caddy/etc/Caddyfile.j2 | 11 +- .../common/kopia/kopia-backup.timer.j2 | 6 +- .../systemd/fw/blocky/etc/config.yaml.j2 | 3 +- .../app/ezbookkeeping/ezbookkeeping.md | 2 +- .../services/app/paperless/authelia.yaml | 23 +++ .../build/paperless.containerfile.j2 | 0 .../services/app/paperless/group_vars.yaml | 14 ++ .../app/paperless/paperless.container.j2 | 0 .../app/paperless/secret.example.yaml | 13 ++ .../app/paperless}/set_paperless.yaml | 0 .../services/app/readur/authelia.yaml | 21 +++ .../services/app/readur/group_vars.yaml | 13 ++ .../services/app/readur/readur.container.j2 | 47 ++++++ .../services/app/readur/secret.example.yaml | 10 ++ .../services/app/readur/set_readur.yaml | 148 +++++++++++++++++ 38 files changed, 1372 insertions(+), 111 deletions(-) create mode 100644 ansible/roles/app/tasks/services/set_booklogr.yaml create mode 100644 ansible/roles/app/tasks/services/set_papra.yaml create mode 100644 ansible/roles/app/tasks/services/set_romm.yaml create mode 100644 config/services/containers/app/booklogr/booklogr-api.container.j2 create mode 100644 config/services/containers/app/booklogr/booklogr-web.container.j2 rename config/services/containers/app/nextcloud/{systemd => services}/nextcloud-cron.service (100%) rename config/services/containers/app/nextcloud/{systemd => services}/nextcloud-cron.timer (100%) create mode 100644 config/services/containers/app/papra/papra.container.j2 create mode 100644 config/services/containers/app/papra/script/papra-exporter.sh create mode 100644 config/services/containers/app/papra/services/papra-backup.service create mode 100644 config/services/containers/app/papra/services/papra-backup.timer create mode 100644 config/services/containers/app/romm/romm.container.j2 create mode 100644 docs/archives/services/app/paperless/authelia.yaml rename {config/services/containers => docs/archives/services}/app/paperless/build/paperless.containerfile.j2 (100%) create mode 100644 docs/archives/services/app/paperless/group_vars.yaml rename {config/services/containers => docs/archives/services}/app/paperless/paperless.container.j2 (100%) create mode 100644 docs/archives/services/app/paperless/secret.example.yaml rename {ansible/roles/app/tasks/services => docs/archives/services/app/paperless}/set_paperless.yaml (100%) create mode 100644 docs/archives/services/app/readur/authelia.yaml create mode 100644 docs/archives/services/app/readur/group_vars.yaml create mode 100644 docs/archives/services/app/readur/readur.container.j2 create mode 100644 docs/archives/services/app/readur/secret.example.yaml create mode 100644 docs/archives/services/app/readur/set_readur.yaml diff --git a/ansible/inventory/group_vars/all.yaml b/ansible/inventory/group_vars/all.yaml index f246a51..1debfe5 100644 --- a/ansible/inventory/group_vars/all.yaml +++ b/ansible/inventory/group_vars/all.yaml @@ -184,10 +184,12 @@ services: public: "mastodon" internal: "mastodon.app" ports: - http: "3006" - streaming: "4000" - redis: "6385" + http: "3002" + api: "4000" + redis: "6379" subuid: "100990" + route: + api: "/api/v1/streaming*" dependency: web: "app-public" db: "postgresql" @@ -196,8 +198,8 @@ services: public: "nextcloud" internal: "nextcloud.app" ports: - http: "8002" - redis: "6382" + http: "8001" + redis: "6380" subuid: "100032" dependency: web: "app-public" @@ -217,20 +219,33 @@ services: internal: "immich.app" ports: http: "2283" - redis: "6379" + redis: "6381" dependency: web: "app-public" db: "postgresql" immich-ml: ports: http: "3003" + booklogr: + domain: + public: "booklogr" + internal: "booklogr.app" + ports: + http: "5150" + api: "5000" + route: + api: "/v1 /v1/*" + deny: "/v1/login /v1/login/* /v1/register /v1/register/*" + dependency: + web: "app-public" + db: "postgresql" outline: domain: public: "outline" internal: "outline.app" ports: http: "3004" - redis: "6384" + redis: "6382" subuid: "101000" dependency: web: "app-public" @@ -250,29 +265,27 @@ services: public: "sure" internal: "sure.app" ports: - http: "3002" + http: "3006" redis: "6383" subuid: "100999" dependency: web: "app-public" db: "postgresql" - paperless: + papra: domain: - public: "paperless" - internal: "paperless.app" + public: "papra" + internal: "papra.app" ports: - http: "8001" - redis: "6380" - subuid: "100999" + http: "1221" + subuid: "100998" dependency: web: "app-public" - db: "postgresql" mealie: domain: public: "mealie" internal: "mealie.app" ports: - http: "9001" + http: "9000" subuid: "100910" dependency: web: "app-public" @@ -286,6 +299,18 @@ services: dependency: web: "app-public" db: "postgresql" + romm: + domain: + public: "romm" + internal: "romm.app" + ports: + http: "8002" + redis: "6384" + subuid: "100999" + dependency: + web: "app-public" + db: "postgresql" + version: packages: @@ -303,8 +328,6 @@ version: postgresql: "18.4" ### For immich - https://github.com/immich-app/base-images/blob/main/postgres/versions.yaml vectorchord: "1.1.1" - ### For bonfire - postgis: "3" ## lldap ldap: "v0.6.3" ## maddy @@ -320,7 +343,6 @@ version: gitea: "1.26.4" redis: "8.8.0" immich: "v3.0.1" - paperless: "2.20.15" nextcloud: "34.0.1" collabora: "26.04.2.1.1" sure: "0.7.2" @@ -331,3 +353,6 @@ version: homepage: "v1.13.2" snappymail: "v2.38.2" mastodon: "v4.6.3" + booklogr: "v1.11.1" + papra: "26.6.1-rootless" + romm: "5.0.0-beta.2" diff --git a/ansible/playbooks/app/site.yaml b/ansible/playbooks/app/site.yaml index 449b194..14bd43d 100644 --- a/ansible/playbooks/app/site.yaml +++ b/ansible/playbooks/app/site.yaml @@ -202,14 +202,6 @@ tags: ["site", "immich"] tags: ["site", "immich"] - - name: Set paperless - ansible.builtin.include_role: - name: "app" - tasks_from: "services/set_paperless" - apply: - tags: ["site", "paperless"] - tags: ["site", "paperless"] - - name: Set nextcloud ansible.builtin.include_role: name: "app" @@ -274,6 +266,30 @@ tags: ["site", "mastodon"] tags: ["site", "mastodon"] + - name: Set booklogr + ansible.builtin.include_role: + name: "app" + tasks_from: "services/set_booklogr" + apply: + tags: ["site", "booklogr"] + tags: ["site", "booklogr"] + + - name: Set papra + ansible.builtin.include_role: + name: "app" + tasks_from: "services/set_papra" + apply: + tags: ["site", "papra"] + tags: ["site", "papra"] + + - name: Set romm + ansible.builtin.include_role: + name: "app" + tasks_from: "services/set_romm" + apply: + tags: ["site", "romm"] + tags: ["site", "romm"] + - name: Set homepage ansible.builtin.include_role: name: "app" diff --git a/ansible/roles/app/handlers/main.yaml b/ansible/roles/app/handlers/main.yaml index bc85f4b..35aefb3 100644 --- a/ansible/roles/app/handlers/main.yaml +++ b/ansible/roles/app/handlers/main.yaml @@ -43,17 +43,6 @@ listen: "notification_restart_immich-ml" ignore_errors: true # noqa: ignore-errors -- name: Restart paperless - ansible.builtin.systemd: - name: "paperless.service" - state: "restarted" - enabled: true - scope: "user" - daemon_reload: true - changed_when: false - listen: "notification_restart_paperless" - ignore_errors: true # noqa: ignore-errors - - name: Restart nextcloud ansible.builtin.systemd: name: "nextcloud.service" @@ -172,3 +161,39 @@ changed_when: false listen: "notification_restart_mastodon" ignore_errors: true # noqa: ignore-errors + +- name: Restart booklogr + ansible.builtin.systemd: + name: "{{ item }}" + state: "restarted" + enabled: true + scope: "user" + daemon_reload: true + loop: + - "booklogr-api.service" + - "booklogr-web.service" + changed_when: false + listen: "notification_restart_booklogr" + ignore_errors: true # noqa: ignore-errors + +- name: Restart papra + ansible.builtin.systemd: + name: "papra.service" + state: "restarted" + enabled: true + scope: "user" + daemon_reload: true + changed_when: false + listen: "notification_restart_papra" + ignore_errors: true # noqa: ignore-errors + +- name: Restart romm + ansible.builtin.systemd: + name: "romm.service" + state: "restarted" + enabled: true + scope: "user" + daemon_reload: true + changed_when: false + listen: "notification_restart_romm" + ignore_errors: true # noqa: ignore-errors diff --git a/ansible/roles/app/tasks/services/set_booklogr.yaml b/ansible/roles/app/tasks/services/set_booklogr.yaml new file mode 100644 index 0000000..bf5f23f --- /dev/null +++ b/ansible/roles/app/tasks/services/set_booklogr.yaml @@ -0,0 +1,112 @@ +--- +- name: Create booklogr directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "data/containers/booklogr" + - "data/containers/booklogr/profile_pictures" + - "data/containers/booklogr/export_data" + - "containers/booklogr" + - "containers/booklogr/ssl" + become: true + + +- name: Deploy root certificate + ansible.builtin.copy: + content: | + {{ hostvars['console']['ca']['root']['crt'] }} + dest: "{{ node['home_path'] }}/containers/booklogr/ssl/{{ root_cert_filename }}" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0440" + become: true + notify: "notification_restart_booklogr" + no_log: true + +- name: Register secret value to podman secret + containers.podman.podman_secret: + name: "{{ item.name }}" + data: "{{ item.value }}" + state: "present" + force: true + loop: + - name: "BOOKLOGR_AUTH_SECRET_KEY" + value: "{{ hostvars['console']['booklogr']['secret_key'] }}" + - name: "BOOKLOGR_POSTGRES_PASSWORD" + value: "{{ hostvars['console']['postgresql']['password']['booklogr'] }}" + - name: "BOOKLOGR_OIDC_CLIENT_SECRET" + value: "{{ hostvars['console']['booklogr']['oidc']['secret'] }}" + notify: "notification_restart_booklogr" + no_log: true + +- name: Pull image + containers.podman.podman_image: + name: "{{ item }}" + tag: "{{ version['containers']['booklogr'] }}" + state: "present" + pull: true + loop: + - "docker.io/mozzo/booklogr" + - "docker.io/mozzo/booklogr-web" + +- name: Deploy booklogr.container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/booklogr/{{ item }}.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/{{ item }}" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + loop: + - "booklogr-api.container" + - "booklogr-web.container" + notify: "notification_restart_booklogr" + +- name: Enable booklogr.service + ansible.builtin.systemd: + name: "{{ item }}" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + loop: + - "booklogr-api.service" + - "booklogr-web.service" + +- name: Check container archive images + ansible.builtin.stat: + path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar" + loop: + - name: "booklogr" + image: "docker.io/mozzo/booklogr:{{ version['containers']['booklogr'] }}" + file: "docker.io_mozzo_booklogr_{{ version['containers']['booklogr'] }}" + - name: "booklogr-web" + image: "docker.io/mozzo/booklogr-web:{{ version['containers']['booklogr'] }}" + file: "docker.io_mozzo_booklogr-web_{{ version['containers']['booklogr'] }}" + loop_control: + label: "{{ item.name }}" + register: container_archive_images + +- name: Save container archive images + containers.podman.podman_save: + image: + - "{{ item.item.image }}" + dest: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + format: "oci-archive" + force: false + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}" + when: not item.stat.exists + +- name: Fetch container archive images + ansible.builtin.fetch: + src: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/" + flat: true + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}" diff --git a/ansible/roles/app/tasks/services/set_nextcloud.yaml b/ansible/roles/app/tasks/services/set_nextcloud.yaml index 0a992f0..9c5df0d 100644 --- a/ansible/roles/app/tasks/services/set_nextcloud.yaml +++ b/ansible/roles/app/tasks/services/set_nextcloud.yaml @@ -157,7 +157,7 @@ - name: Deploy nextcloud-cron service ansible.builtin.copy: - src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/systemd/{{ item }}" + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/nextcloud/services/{{ item }}" dest: "{{ node['home_path'] }}/.config/systemd/user/{{ item }}" owner: "{{ ansible_user }}" group: "{{ node['group'] }}" diff --git a/ansible/roles/app/tasks/services/set_papra.yaml b/ansible/roles/app/tasks/services/set_papra.yaml new file mode 100644 index 0000000..777cef8 --- /dev/null +++ b/ansible/roles/app/tasks/services/set_papra.yaml @@ -0,0 +1,111 @@ +--- +- name: Create papra directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['papra']['subuid'] }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "data/containers/papra" + - "data/containers/papra/data" + - "data/containers/papra/backup" + become: true + +- name: Pull image + containers.podman.podman_image: + name: "ghcr.io/papra-hq/papra" + tag: "{{ version['containers']['papra'] }}" + state: "present" + pull: true + +- name: Register secret value to podman secret + containers.podman.podman_secret: + name: "{{ item.name }}" + data: "{{ item.value }}" + state: "present" + force: true + loop: + - name: "PAPRA_AUTH_SECRET" + value: "{{ hostvars['console']['papra']['secret_key'] }}" + - name: "PAPRA_AUTH_PROVIDERS_CUSTOMS" + value: | + [ + { + "providerId": "authelia", + "providerName": "Authelia", + "providerIconUrl": "https://www.authelia.com/images/branding/logo-cropped.png", + "clientId": "papra", + "clientSecret": "{{ hostvars['console']['papra']['oidc']['secret'] }}", + "type": "oidc", + "discoveryUrl": "https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/.well-known/openid-configuration", + "scopes": ["openid", "profile", "email"] + } + ] + notify: "notification_restart_papra" + no_log: true + +- name: Deploy papra.container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/papra/papra.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/papra.container" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + become: true + notify: "notification_restart_papra" + +- name: Deploy papra-backup service + ansible.builtin.copy: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/papra/services/{{ item }}" + dest: "{{ node['home_path'] }}/.config/systemd/user/{{ item }}" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + loop: + - "papra-backup.service" + - "papra-backup.timer" + +- name: Deploy papra exporter + ansible.builtin.copy: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/papra/script/papra-exporter.sh" + dest: "{{ node['home_path'] }}/data/containers/papra/" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0755" + +- name: Enable papra.service + ansible.builtin.systemd: + name: "papra.service" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + +- name: Enable papra-backup.timer + ansible.builtin.systemd: + name: "papra-backup.timer" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + +- name: Check container archive images + ansible.builtin.stat: + path: "{{ node['home_path'] }}/archives/containers/ghcr.io_papra-hq_papra_{{ version['containers']['papra'] }}.tar" + register: container_archive_images + +- name: Save container archive images + containers.podman.podman_save: + image: + - "ghcr.io/papra-hq/papra:{{ version['containers']['papra'] }}" + dest: "{{ node['home_path'] }}/archives/containers/ghcr.io_papra-hq_papra_{{ version['containers']['papra'] }}.tar" + format: "oci-archive" + force: false + when: not container_archive_images.stat.exists + +- name: Fetch container archive images + ansible.builtin.fetch: + src: "{{ node['home_path'] }}/archives/containers/ghcr.io_papra-hq_papra_{{ version['containers']['papra'] }}.tar" + dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/" + flat: true diff --git a/ansible/roles/app/tasks/services/set_romm.yaml b/ansible/roles/app/tasks/services/set_romm.yaml new file mode 100644 index 0000000..ad76ee3 --- /dev/null +++ b/ansible/roles/app/tasks/services/set_romm.yaml @@ -0,0 +1,146 @@ +--- +- name: Set redis service name + ansible.builtin.set_fact: + redis_service: "romm" + +- name: Create redis_romm directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['redis']['subuid'] }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "containers/redis" + - "containers/redis/{{ redis_service }}" + - "containers/redis/{{ redis_service }}/data" + become: true + +- name: Deploy redis config file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.conf.j2" + dest: "{{ node['home_path'] }}/containers/redis/{{ redis_service }}/redis.conf" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + register: "is_redis_conf" + +- name: Deploy redis container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/redis_{{ redis_service }}.container" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + register: "is_redis_containerfile" + +- name: Enable (Restart) redis service + ansible.builtin.systemd: + name: "redis_{{ redis_service }}.service" + state: "restarted" + enabled: true + daemon_reload: true + scope: "user" + when: is_redis_conf.changed or is_redis_containerfile.changed # noqa: no-handler + +- name: Create romm directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['romm']['subuid'] }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "data/containers/romm" + - "data/containers/romm/library" + - "data/containers/romm/resources" + - "data/containers/romm/assets" + - "containers/romm" + - "containers/romm/ssl" + become: true + +- name: Deploy root certificate + ansible.builtin.copy: + content: | + {{ hostvars['console']['ca']['root']['crt'] }} + dest: "{{ node['home_path'] }}/containers/romm/ssl/{{ root_cert_filename }}" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0440" + notify: "notification_restart_romm" + no_log: true + +- name: Register secret value to podman secret + containers.podman.podman_secret: + name: "{{ item.name }}" + data: "{{ item.value }}" + state: "present" + force: true + loop: + - name: "ROMM_AUTH_SECRET_KEY" + value: "{{ hostvars['console']['romm']['secret_key'] }}" + - name: "ROMM_DATABASE_PASSWORD" + value: "{{ hostvars['console']['postgresql']['password']['romm'] }}" + - name: "ROMM_OIDC_CLIENT_SECRET" + value: "{{ hostvars['console']['romm']['oidc']['secret'] }}" + notify: "notification_restart_romm" + no_log: true + +- name: Pull image + containers.podman.podman_image: + name: "docker.io/rommapp/romm" + tag: "{{ version['containers']['romm'] }}" + state: "present" + pull: true + +- name: Deploy romm.container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/romm/romm.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/romm.container" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + notify: "notification_restart_romm" + +- name: Enable romm.service + ansible.builtin.systemd: + name: "romm.service" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + +- name: Check container archive images + ansible.builtin.stat: + path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar" + loop: + - name: "redis" + image: "docker.io/library/redis:{{ version['containers']['redis'] }}" + file: "docker.io_library_redis_{{ version['containers']['redis'] }}" + - name: "romm" + image: "docker.io/rommapp/romm:{{ version['containers']['romm'] }}" + file: "docker.io_rommapp_romm_{{ version['containers']['romm'] }}" + loop_control: + label: "{{ item.name }}" + register: container_archive_images + +- name: Save container archive images + containers.podman.podman_save: + image: + - "{{ item.item.image }}" + dest: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + format: "oci-archive" + force: false + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}" + when: not item.stat.exists + +- name: Fetch container archive images + ansible.builtin.fetch: + src: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/" + flat: true + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}" diff --git a/ansible/roles/common/tasks/services/set_kopia.yaml b/ansible/roles/common/tasks/services/set_kopia.yaml index edfc4a3..5d9389c 100644 --- a/ansible/roles/common/tasks/services/set_kopia.yaml +++ b/ansible/roles/common/tasks/services/set_kopia.yaml @@ -106,7 +106,7 @@ set +a /usr/bin/kopia repository create filesystem \ --path="{{ node['home_path'] }}/data/kopia/repository" \ - --description="ilnmors.internal kopia repository" \ + --description="{{ domain['internal'] }} kopia repository" \ --create-only when: not is_kopia_repository.stat.exists changed_when: true @@ -138,17 +138,16 @@ --user-password {{ item.password | quote }} \ {{ item.name }} loop: - - name: "console@console.ilnmors.internal" + - name: "console@console.{{ domain['internal'] }}" password: "{{ hostvars['console']['kopia']['users']['console'] }}" - - name: "infra@infra.ilnmors.internal" + - name: "infra@infra.{{ domain['internal'] }}" password: "{{ hostvars['console']['kopia']['users']['infra'] }}" - - name: "app@app.ilnmors.internal" + - name: "app@app.{{ domain['internal'] }}" password: "{{ hostvars['console']['kopia']['users']['app'] }}" when: - not is_kopia_repository.stat.exists - is_repository_connected.rc == 0 changed_when: true - register: "is_user_created" no_log: true - name: Set kopia console ACL @@ -157,7 +156,7 @@ . /etc/secrets/{{ node['uid'] }}/kopia.env set +a /usr/bin/kopia server acl add \ - --user console@console.ilnmors.internal \ + --user console@console.{{ domain['internal'] }} \ --target {{ item }} \ --access FULL loop: @@ -178,7 +177,7 @@ . /etc/secrets/{{ node['uid'] }}/kopia.env set +a /usr/bin/kopia server acl add \ - --user infra@infra.ilnmors.internal \ + --user infra@infra.{{ domain['internal'] }} \ --target {{ item.target }} \ --access {{ item.access }} loop: @@ -186,7 +185,7 @@ access: "FULL" - target: "type=policy" access: "READ" - - target: "type=policy,username=infra,hostname=infra.ilnmors.internal" + - target: "type=policy,username=infra,hostname=infra.{{ domain['internal'] }}" access: "FULL" - target: "type=content" access: "APPEND" @@ -202,7 +201,7 @@ . /etc/secrets/{{ node['uid'] }}/kopia.env set +a /usr/bin/kopia server acl add \ - --user app@app.ilnmors.internal \ + --user app@app.{{ domain['internal'] }} \ --target {{ item.target }} \ --access {{ item.access }} loop: @@ -210,7 +209,7 @@ access: "FULL" - target: "type=policy" access: "READ" - - target: "type=policy,username=app,hostname=app.ilnmors.internal" + - target: "type=policy,username=app,hostname=app.{{ domain['internal'] }}" access: "FULL" - target: "type=content" access: "APPEND" @@ -232,7 +231,7 @@ --global \ --compression=zstd - content: | - infra@infra.ilnmors.internal \ + infra@infra.{{ domain['internal'] }} \ --compression=zstd \ --keep-latest=10 \ --keep-hourly=0 \ @@ -241,7 +240,7 @@ --keep-monthly=6 \ --keep-annual=1 - content: | - app@app.ilnmors.internal \ + app@app.{{ domain['internal'] }} \ --compression=none \ --keep-latest=10 \ --keep-hourly=0 \ diff --git a/ansible/roles/infra/handlers/main.yaml b/ansible/roles/infra/handlers/main.yaml index 9f8a6a8..3cf5c55 100644 --- a/ansible/roles/infra/handlers/main.yaml +++ b/ansible/roles/infra/handlers/main.yaml @@ -1,3 +1,4 @@ +--- - name: Restart ca ansible.builtin.systemd: name: "ca.service" @@ -73,7 +74,7 @@ listen: "notification_restart_grafana" ignore_errors: true # noqa: ignore-errors -- name: Restart x509-exporter.service +- name: Restart x509-exporter ansible.builtin.systemd: name: "x509-exporter.service" state: "restarted" @@ -84,7 +85,7 @@ listen: "notification_restart_x509-exporter" ignore_errors: true # noqa: ignore-errors -- name: Restart maddy.service +- name: Restart maddy ansible.builtin.systemd: name: "maddy.service" state: "restarted" diff --git a/ansible/roles/infra/tasks/services/set_ldap.yaml b/ansible/roles/infra/tasks/services/set_ldap.yaml index 6e604c2..500c887 100644 --- a/ansible/roles/infra/tasks/services/set_ldap.yaml +++ b/ansible/roles/infra/tasks/services/set_ldap.yaml @@ -59,12 +59,11 @@ # The reason why this task doesn't use the way to check ".init" file is this tasks can override original database. # Absent of ".init" file cannot guarantee DB is empty. when: false - become: true no_log: true block: - name: Register extra secret value to podman secret containers.podman.podman_secret: - name: "LLDAP_LDAP_USER_PASSWORD" + name: "LLDAP_LDAP_USER_PASS" data: "{{ hostvars['console']['ldap']['password']['services']['admin'] }}" state: "present" force: true @@ -83,15 +82,17 @@ - "LLDAP_DATABASE_URL,type=env" - "LLDAP_KEY_SEED,type=env" - "LLDAP_JWT_SECRET,type=env" - - "LLDAP_LDAP_USER_PASSWORD,type=env" + - "LLDAP_LDAP_USER_PASS,type=env" volumes: - "{{ node['home_path'] }}/containers/ldap/data:/data:rw" - "{{ node['home_path'] }}/containers/ldap/ssl:/etc/ssl/ldap:ro" + etc_hosts: + "{{ {} | combine({ services['postgresql']['domain']['internal'] ~ '.' ~ domain['internal']: 'host-gateway' }) }}" always: - name: Clean extra secret value from podman secret containers.podman.podman_secret: - name: "LLDAP_LDAP_USER_PASSWORD" + name: "LLDAP_LDAP_USER_PASS" state: "absent" - name: Pull image diff --git a/config/secrets/secrets.yaml b/config/secrets/secrets.yaml index 7c2394c..0afb153 100644 --- a/config/secrets/secrets.yaml +++ b/config/secrets/secrets.yaml @@ -132,7 +132,6 @@ postgresql: vaultwarden: ENC[AES256_GCM,data:BPj5eFo54DTZ82n3yTIqEbm7kb/jWT0n2kZY//oV5q48eRch3C2RBuxn/Ko=,iv:DGC4ipHMyVs25gc4sNMt8LN1RsHjiR/b303vgiFoxMY=,tag:k1eb4DoRPLKvvMstSI1faQ==,type:str] gitea: ENC[AES256_GCM,data:l+pBCzyQa3000SE9z1R4htD0V0ONsBtKy92dfgsVYsZ3XlEyVJDIBOsugwM=,iv:5t/oHW1vFAmV/s2Ze/cV9Vuqo96Qu6QvZeRbio7VX2s=,tag:4zeQaXiXIzBpy+tXsxmN7Q==,type:str] immich: ENC[AES256_GCM,data:11jvxTKA/RL0DGL6y2/X092hnDohj6yTrYGK4IVojqBd1gCOBnDvUjgmx14=,iv:oBfHxsx9nxhyKY/WOuWfybxEX2bf+lHEtsaifFRS9lg=,tag:tAfkBdgQ8ZEkLIFcDICKDw==,type:str] - paperless: ENC[AES256_GCM,data:6VBrBbjVoam7SkZCSvoBTdrfkUoDghdGTiBmFLul04X/okXOHeC5zusJffY=,iv:iZumcJ3TWwZD77FzYx8THwCqC+EbnXUBrEKuPh3zgV8=,tag:u2m8SppAdxZ/duNdpuS3oQ==,type:str] nextcloud: ENC[AES256_GCM,data:ROsximNuWYMTZktmLJPx7W1Qol/uT+APgwoCtFO/6ZYYc3KxKvlk344eqEc=,iv:4d+MrfIHjJKAcwhvZ3g4go66uZcieuL7lngKErJd+fg=,tag:QbWOtxeCbiu62GyrE2atXg==,type:str] sure: ENC[AES256_GCM,data:FULJ2gjJ2gZC3s324itW+CjGRBHIP9RnOqw5TT1UaiUhb7UHAPm1na+LsZk=,iv:c0GnVZkxprJUzPPq3TCQaZvAes9QQuvDXqgVLLaiQIg=,tag:uDxy/Lkd2hNK4AWwMNMslw==,type:str] outline: ENC[AES256_GCM,data:0eJFCfHgS4olWXHOnUPixs4Dl5PoxJa+2H1mk3YgC4GUv+nX8gTPmzKTMVM=,iv:HGxX7aLODUc2nVLC8YXdet9qphU/wxMapWhk93YmHDM=,tag:6/2uckW/a24LgVFRH6E85Q==,type:str] @@ -140,6 +139,8 @@ postgresql: mealie: ENC[AES256_GCM,data:XbYHQv6HBWCXUBRmcbQZN5Y1Gw+V+Wra28Mp+m+ZGl8VRXsKMu5507vGHQM=,iv:zp4ZarqG9KshiBV8F8UksfI2QXmXHwqPw631r/5bYy8=,tag:jvsvXrUsCvpQGIbLV9jTYQ==,type:str] homebox: ENC[AES256_GCM,data:EhNnltOBwLai5vEMt7Dih5Ug6h3ojrdbwhq4Vr0tg/p/0nqI9L7O/Le0B4w=,iv:14oyl916t0inr+W/lztQCjYiSQ0vIIwC9NmbezDuvvY=,tag:IiXZd3NU+dUvDrHpxJXFew==,type:str] mastodon: ENC[AES256_GCM,data:hq1ETBK9iUI7kWFO+9pZwMQMbmyLExGk7/VzBwDdLFbzptXL2KEi6oYRCS0=,iv:hF1C8lsnP2i3LxDIuOA6op4qKXQKjJ7gqznDst7hVAU=,tag:maR7Jvsv3DJGrO77EzmBDA==,type:str] + booklogr: ENC[AES256_GCM,data:K+9Bb/GMbaviYIIesimugQg2RXF590L2T2XNlRI/bjc9BBb9/iivFlhLwa4=,iv:E/MGt0bKlITYKMYZjlmTZ2YonYTvNTaRI23n5xazyeE=,tag:Y3WUSHBHQiF1MVD/zQr5RQ==,type:str] + romm: ENC[AES256_GCM,data:nizBKAPWPRTxKZRPGOoff6WrbpK9z3s1RDhJmKghO4BhmgguH2C/5DlQ6V0=,iv:uiscRdOoS//Mxs8Lmw6sA+/YtcVhhuJoxP7AxQGJHNA=,tag:UM89ocQFBK3zVDsS8LhAiw==,type:str] #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # @@ -147,8 +148,8 @@ postgresql: ldap: key: ENC[AES256_GCM,data:PwQ/AtsQbbM454iM0wpUjGRfxyxNY5lGDARv/XOdk0yMCHRsf9qHKaW37EwcqNe+MVTJH+sx8lHDdxRYF1gO1yj6kasEIbMeo9cIaGQHvey5dYdber/qUkcjuAXyhSZGSUXI3PR1CuAsiqNUEruh/eKyDZAvHTTY0Rc5jk4HLFzPqqBjBOMkA6VQXJ2I+v4s8W0T8/JEQ5l6avwYbaaInIIY+uPV+sY0mBczAuaHLyeA3V7VQNTxQ9lUgdfH/TpgBn9EBH2wWqIGk2zQ6XmVrmOarCo/+JqwLQVEyY5TTrNuBto=,iv:iYzoRXjH8NjPnX5nBhU2gTQXOekXMirjTGhmpGt3YAs=,tag:IxwtGNXx1i54Jnhn1aZx3w==,type:str] crt: ENC[AES256_GCM,data:ulOZOUV23qfU2RATrXYFKPnPNW2vHShJe3O90XXoSA4bkUBA5Jz8uuAzCiVOZfx/Ni2uG6y5pP2x4ZnJADSrkfHD5P5IF3iUWWSgRo4NDDCY00r+fuPFP5DpOhwHl4LzdiHl8SBJ6YWBbhtT8sXVXFgmQIFLppuUD7WzJ4U2c251vF6G7Q0XemlkkNFq4fbkClTwogUyTRs2Dj0e187uilDkfCBS83oxMU5pB5tBvaFbLxaJmtNpaFRp0U2fgJqJrM7avZJeNUDr1hHttAw/TnANNK3X3mgFAruluTUGTS0dt8KPUlaUCf00I0t1F6l+cNktaTlnUtcp/HdWf0Rtg+tYBfFNgsGl3iB2z1utpab+6WsI2+LZYGezOLKojbFeYGNmVLkSucCSVAtuj48fqKFeObdLcBjihXilI151ie4m5BR+37h1Laob64C/n8lhNSm8QpTuIipV/3koyBKwIimMYYO8j8sHGQNJpdDuZXmYCEiXsdXZH2tFngWtlJ+39yV+GCjhKRYPkAWFKw+EtTSnPposC8fHPMLMZnB1hpsVmSy6VBDuZ2Qq96H+njgzWXIwhWcZteTyxp2fS6dNSG3zTYD2cp9qVrA4KhTlAjVojXdlRAv5Ayob9R89hD3WNKexe3QD/Nj1Hva7GyjEHqHijodYDNXCWsMgXdgItgwUz28vh+H8o5sSHCsfUDM2s7Y4w1rmKOkD4XpxTcyGM5zrNBacvhS1FzuDFYZ8BnN+STaQ1pvscVGtrys6EtXDBfrWbrIFnIhMGSp/GGUMhrT30gPV5VmzUqixRJzpKvYF1ljr8ybRu9Sf4xaprQNeFTTWq2KXonwK8JonbZJNwQQ6oDbp7AyuQD4EJD8GoXJUAy9H5TjnpjJsZH+RsTR6O19K+O45HQCqvZpEHfUUXu022VqrjmzRHnmB4bJKgb+BqE6YpQ==,iv:Vh9eqEun0E73s57RV53rHbjP5AWXn4NPjlEPwVDQ5VY=,tag:fxpzhMHk8Jmb60GHOHnYvw==,type:str] - seed_key: ENC[AES256_GCM,data:q6kodsHzwDuf0UHPSk9VbEI9pCwePJWcA7o6dQiZqyM=,iv:EbozH0UpLThEXjmWkNhkpHQxabH/K3/lJ8xki3lLGZo=,tag:rKO8csevxpH5kzzFWQoang==,type:str] - jwt_secret: ENC[AES256_GCM,data:AfPcqKAwdsHfiYHrZ6yKqOxD93gBcLEvZ4N3M8nhWMQ=,iv:awfSXZbc7Tzg8sRqUkp71TXivLb663ZjaxNkskE1Heg=,tag:e541/ejk7x+GOhJeoUPMGA==,type:str] + seed_key: ENC[AES256_GCM,data:1hv0p1uhHoJQduFLu/mJ3QvnFJUsjkeclnW4TXAszT411ceN4i5V8NIXImI=,iv:vf/lDc+A7wXX4MlXvfKSYzUUYuai4j8qlGQhA5CR85M=,tag:o8MQzkImlDLOPvO+7o440Q==,type:str] + jwt_secret: ENC[AES256_GCM,data:m3wA70WxYPGWxrcsHpFcbK+M4EkntA2ySWWS/chV/CoSa9fx4jHqgW4kiuA=,iv:VYCga9S19s7SsXt1KoTR2El7aOtADymNNUR57g6+sHA=,tag:eUUF1nPV8STOWGI3WaVB/g==,type:str] #ENC[AES256_GCM,data:/5Po+8hjJg74ZBepaNWTzZy7JrTjiwo2DNFT76hZTUFKEdchNd4sfdy3tbVwL5y9QPC/kC3kvBZL2oA0+WzcUmd51Ht4/xS6CnUCpodBrSWNHu4H+ohdmz/jMplREeJVWAFHuTZROqB9v3hw3rYJ5Gb+5XROAuzT4pKfrPoJ1B+ysGUpuvybhrC1O5Tg6IPs5fvIiR3yExGcIFuywZBiiA==,iv:gupWDnXb2TD1N8GhHwpiYuz0sDciLX8V0ww9cFKtFNI=,tag:7eDx5RZOrNXOUshvdijV5w==,type:comment] #ENC[AES256_GCM,data:+Q0n+z/P2NwlY6/aOzMNe2yZaoN7uSctcH8DlTUR6dYgNTnZlGQNNFAHs416XVMcYHyBqPwkSvdQLGl5TcJReZLm+8ESeYeQhjWgHZWKnGJK0e46cdMhznXKjQ==,iv:dayGt24JAY3+LV3OS0JUxM7vxfOUAoTl7O33d6y4wVI=,tag:KcfHxeqjmyAUfNDELBfx/w==,type:comment] #ENC[AES256_GCM,data:6AE31A6F6mflPwwac4jVxY5kJPYaKlYzNjtUC9/VmCPeS8iuvoFmxHNQ9cwxLa20Nw==,iv:N7tQ9RyBYvSCKzbLGWgH8NgyKCJpNM5cupGK9e9pQbM=,tag:nAduK50DDywtkkQ03psKRA==,type:comment] @@ -196,9 +197,9 @@ grafana: # #ENC[AES256_GCM,data:k+Y8/BDL9G+C1EnnJjy7gz4=,iv:IU/ZlXVvtX+zaeU7Zcz5baYk8ceDz95dZgzpl5/L9lg=,tag:t0OzLQCM+Up0wLkc3B/R/Q==,type:comment] authelia: - jwt_secret: ENC[AES256_GCM,data:NOsbV8poM41bodRZeNJ6vnRYIl7vGKXm2BpBNeXWVeE=,iv:1G4PnaoopJMIt7D+8x0x3ui5tefMPRnSI5KJRv4IuTo=,tag:EkEHgeJ3ta4htrgOCnYldg==,type:str] - session_secret: ENC[AES256_GCM,data:tPudIXLrAKMhR3VPFj8U1g/+FRvYgBo0wXLNf/a72x0=,iv:atfRSZNUYEYFK92+0WOvsD5Gi4tMEKv+dwsFcRDrEtM=,tag:cJu5G4EYQ9EnYOQ4w1OSpA==,type:str] - storage_secret: ENC[AES256_GCM,data:g3Z8t/2xFZJMz4baIfY7pGoAOdoKsr+Xn4POBl1lQCk=,iv:i9Owc4w76qC1rFSUXLv4NuW6/Y2rmgSoTr/EnZuRVE0=,tag:zitZ/UAgXiUSuyWDhvHa9A==,type:str] + jwt_secret: ENC[AES256_GCM,data:YwMsvOqmukD/UyLZZOxHyN7VCy9yT6ZIkWHCtRdb3Qh12/5QFMUrsuy9HC0=,iv:yg3dTjGAKpBCLT2ixfF99mqnKj+OMogs3uU748ouLwk=,tag:AeGAfyIfGY2FxCvJfHUd0w==,type:str] + session_secret: ENC[AES256_GCM,data:PPO8MhdI9EgfXFJsu7/FsCa+cudZ+34bq2lP709NDFdzLK4Svdy9JkhZqsE=,iv:Zfq/NDpbPtEgt7fmv9Z0kIL/nQUGALAJ5bEVaMPD8tA=,tag:FG2Nii5QeJY8rREKAtmdZQ==,type:str] + storage_secret: ENC[AES256_GCM,data:uORmJjre1o1JjjEzuI3qdLPW8Vgld1bwp5lrzrfLMhce35Iw9wm1UM0WkW8=,iv:ajDOHOo6AXI3r+32nd8nj+PK/pLb9BXkExIYkN90C4g=,tag:OOdraFk6SudP1GEn0Eox9Q==,type:str] hmac_secret: ENC[AES256_GCM,data:c6Xn0QceaQWIFDCFr6UAQcr9M/Np2aGCkcNiXJfM79vFakT5L8q27+38Mvs=,iv:QSpnN/ZVYNogHfFq10E1rR8jXnI/qLs3Z7k2qKHFt4A=,tag:N6Wx52yCmGx2kJlyQXl7bQ==,type:str] jwk_rs256: ENC[AES256_GCM,data:e0rbiDCUadbuRL3dFZ//LMMYimQ3THJtZo1OqkKntwayh10DbvdNrEhs6HF0VAMtVh5nkAaL69V3tqZnwLUadSg0Oomum8v9GkTE527iupAMjWRrXzAR/nsjvjI8gPUL3NJk++v0VA6ew/Gb9WfGlhUlmkf3K72Ab+gGiD8+xfAFeqRE/k7vHkTxkNwD1hRFnE+XhkUa4Q021nSd7xrW80sHwBWTKKUqA08xuouQN8Gkbjj22NgicT5rhy4VJiV8OnLCERLWc8F6LmDM9vLrTiPBvsGn1eX09fvVRtw40Wo0VSgklm9+REQtjefBhXuVxVDR3DXNzPliLNL3MOrvlcweDU8uxFVuml23FBBkJypJLJwbWvxwFQk1OWjiQ9jPel9+9gYu1xHg/dKeEsDf8GyMVJz60BDEQH5YnEwI1R6rPnJLZnmZVhRtoxmKRE8/oE3lP30tvOVKwRL/f3rXCyB1PYDR/2NsWtj61inzgn1BuMDl/tHDKjGhsCu2+ECThD756p6cjQnGftSSoGlwhmMCqA8I+6WUjMPqEsqietWKbMCRk000ufLp57wU4i7BvUVPSpiVO58lf6n/zs6hHBYDWNfoxEa2gha4csmCTkhCrzf+N+UJQmoMDOIQe/gSqPNcwcW/+q8K8zr2b4cMIyLbwZIyiez0RvPRnM/KuM9UmFAKfW1xdtyxalNLMOBjpqeZFnCIPFRQy6COP/dZNA9xUQX4TXXsL+qTk0vlU41/gcORgw9HQFRkLiRtrnqvRUMXipIDU6SIRipbBJ1GawH5nPTdHdbjdr7Wh0s2RUjIzLNvT0Lz0VRii2dlVRdURbtFl8R8frijuE9LG9xKY2Z4f/ojo71h6HcJvrvMOGm4iE8V5+cdthlwn4Hpyl8nLinJWLyVjhc4bzmIlo+VZvbq9o/keSfCb3wrI39L3Wy/bpoKzXx/U3eTVIIXRDrBpK+4fmYeSEi2JRJjAHHvs8De7c0wKLq9qOctIFvELwueisDIkyNGYykX1+JXa4YbuTYaOSLJNbk9B6UE17Vbr6FD9N/8n5JveXzfd2dTROQBbWmHyKa0qR7MC9foPdyUVImj1Z06w91OwRyAi+/GBiUE8ivKD9AmnTjrvX1uZhp9qGSzP9f3RkCd6lFDQ0YhSCVHQAYOrDsdBX50zqqmZEPPevIXXPnaJpSgIaOCamBLIHqrxX9KOFM3ApicE2BhXX4xN2QWFl8HGu2cVD1e9bINKrpLGqR+rQQE822OQqMqw3kZsi7lo46YPOrpXLsetxPJlLftQ7bdaCPONZk3M18xvxESYclDeGk7LJ8Lp5CjtBterF3Ka/0r7cY4OxWusTypOY3jmVDILZTMlVOKoYiJgjIHxwXFdoqbtGMeGi83CY2/SfoHn7BSrU2669O3liX0L7hzGOLerkiVozbmC3TPq+JxGNw7dA79mXCqRDQEeqAjeha6J3wmvTvdmkuPIbO7ryFtDg9VwbULfgl07WzkES5nVyQDeamKv76ERRF/EIjQGgiWRY5MDyXAhVnzy9ATEbOnBTXeks6863CTKa/3lhhtDUGeo+9tTXcEUqLPkq1jh9xYo80OUp99J1483n5iE7EEBfB13McRwsJPufViyMWIQX9KDmRZmbCjhe/Jrj6joapQcY1Fw5MaZJgfIIfzz83GnekgTG9sz7Vfq2p4qiPDONLhY6qL9ENl8Cm/tWq/Xjvk+v/gaT65L9uvGLtkYKv7OpbPtTzieGHtYmddLZNr34Ly06PHxajLL+X6LeB5wmeC7jvanLh9vW6VqwNRn6kstS9reM4kHRhYTNKlDB9pOOz/cK0p0wcNzVvqetoYtZAX9TgPcYumhZQUPsXYsuC2Gdo88seihGKND9jtepvu2OwKRT3raSpsG+rum86Q95ulRsE/rhg4BvBXs8U62E16gL1KamtRkPcjXT/2aO1t0gh6JiBbQAYemnOaJCanycbnQcZal5oSvqPLAxzFpcjTkPNVFhYyiGe0ekcaYkCs9PYuM3hqP+qNAwjQmK7bfpK+eZNGT1gwikmlUtokTvRvQjmFynfbQ5oUoOHQuxuqWOa8KLi2NwMMmaJ4LXuzozUY8KD2EXmlmrxteCZBtQNfa7BxulUMlPRHG5bqKsBc9l380I1XU+aR2qmqHxdrd9698sIXxVK5zM1vhwI0fb6V61u86iYIAdSoitzhRQu8lK8prJXxcZWOQWehV03wvDnqkU4F2Qm/XYCXJgTx8PJyGKP2w90K7TtkW1YMWrBXmnwr,iv:av1JOLQd7WoZr4O8e9PU8nEZlaf+Bs7LLbGZs/4hm88=,tag:sZ8zyjRmhyYRdwntXTOfPg==,type:str] jwk_rs256_public: ENC[AES256_GCM,data:2IP+w7X5ZfYUH+upr7qtjno45RXXUT71hG9Xs/VMY9aWOv6KGLpK17OEo7hVUaXwmOWOzAQl0V79bGZjIowabAoba15+IEi38L973i6F44kM9pb4N29Z2LjmLoZ/blNpl82C0ZIO/+OQ+OCRxVV8b9eZ+E1ckpvEk24NmOV2o9H2E1EU6QBOQGmYt39mnD/tQKNtE0/Q0+HbW2bz9dWqTScjH6CcwBwhtn3dhU/kbXjLc27XG6pxMKInKLo6GZmjLA3PDRQY9O2cYnFN1nDRKdOqHpbEnAPU4DiUSronJZGq6R6Z+3JGkWXH2OUTXaP8itLpXYBioY8BKH02QA+YVxnzxC0cBooNW9BxFb/GQdeLqFawNZ2lH9+743svXIRjjKp3jyn1Bn0TnDxgirZSfmvBdrgOy1fKl0qpMBYrBVLaAP1+T4MxNNa1kQqnmt9wke7aYrjVroi3/33TE9oU/xW6SW6Y+3lLEB6kJIGYb/m+qhwksOrqXRDom8G5hocUqMX3Q1fkiv8Jt/qvmuz8oSFNsjVF/4AZFESacfAF9pyerPUemyti6Y+rVjeY1yUv3+BHGgu8OiF7lqlIUm5O7NGhNQ==,iv:41P+xnnSOQK4H1dm5EohB5FmXsuaKXfSOFr+ROgoTWw=,tag:7a0+DsaDDK1DlkggQQd6vQ==,type:str] @@ -248,18 +249,6 @@ immich: #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # -#ENC[AES256_GCM,data:McPUAbIUvtC1gdPaxTgAxAMCMWcLfg==,iv:Tp6idRf7he3sYzo8LW596C905JAaoTIhIoDUzSyRT0k=,tag:4mZQ0Swu1X9uuwjsRNhr2A==,type:comment] -paperless: - session_secret: ENC[AES256_GCM,data:siwCs2noeVpg9DCEZybnmo/oz11BdrHSTnHciMOu/6g=,iv:XVjhu10TIujIdUopN9+TVVqRade9EvItDWxym6YXnZs=,tag:TxLYm+4Bo7IMaTQBtMg9pQ==,type:str] - admin: - id: ENC[AES256_GCM,data:8uo=,iv:f/qtpDb1Xe0M6Gd2VFP2vXULCd7WOn7TCxLlaK1dDbA=,tag:eNRMPJtLlA3Vjj79bSFxuA==,type:str] - password: ENC[AES256_GCM,data:TAA+TRTYRUSw47nE7oPDQmOtCBZpf2WyxUIIzftWq16wQhseODhH0R74X+Q=,iv:V0tzSvW5iYzEUgcxempmyD9ermVAqg673liiysT4jJ4=,tag:GuxtZ7EvgwvlFWwg+C6Jsg==,type:str] - oidc: - secret: ENC[AES256_GCM,data:wjRDVCJsINM4z5946a6uZD+6bhN5BChLMdRzgMEJFGRGFNcXd7A1p2Iqn4I=,iv:Y4QDA09L8ULKr4hhvoiduzCD8Hifo1gAnpzjCr8e520=,tag:R0RvGxYnXo3zwykXJykRug==,type:str] - hash: ENC[AES256_GCM,data:pali6WwPNhJA+6QL4O+tKv42PnpGqmojb8JQUZLqxGizv1bJSCgdUN8upCy5Ke0DYZs5P+JY5vh23xfMZFnHduGxGwOuPX6J5lYgvJRV58LqS3/+yIBBprTJyro3MwsurTTEWesgKMr8/2H9lirhaLjWUOSPxAmQ6e4wPNpHycDVyj4=,iv:cg2trI7t1MfIcMo1/M+IY6JEl2msDoKRGgAx/Y5nyGk=,tag:gnOq7sBq9z5zrRY0yhIabg==,type:str] -#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] -# -# #ENC[AES256_GCM,data:PZS7EbvMHqHGorNUGAWj4dk1,iv:vOE+djRAvBTMM51kHi6kG5Arw3uPXlJt1d/BpcEaD0c=,tag:AuoCHLQz42CYvVVdKFWu1Q==,type:comment] nextcloud: admin: @@ -273,7 +262,7 @@ nextcloud: # #ENC[AES256_GCM,data:Fsqc2JDp9dvfgiCjdQ==,iv:3DALKKEXaP8hzXRvxD4CgfFpOiPPsOa16OB94n8WKp8=,tag:K+FF3zGrc0YLXWK/R2L3Ow==,type:comment] sure: - secret_key: ENC[AES256_GCM,data:jZLkIoLG7Y5JaMlfG7XTVnrgmM3JiJUfYEXzi4XHukE=,iv:VkgZbr9GDMQuAA4xvls9eNp479ylus0LHzLqArKt1Bk=,tag:7+g7JOEQUzZaKc1JYeCwmA==,type:str] + secret_key: ENC[AES256_GCM,data:95YY5PQXvNkHLhzpgO+av+J+f+ttN/adfRr5kspNXy5CNl44sh7F9OcHmXM=,iv:c25O1a4yrPaPQ9CTCS74UWGAAiijkAASKoaPQBOBstY=,tag:LHQilc4NfLjLTVytGM+pUQ==,type:str] oidc: secret: ENC[AES256_GCM,data:9BSvpcU9BJctSN9bkPIAsRxg8JNHTWvOKdpJFhm//CUDm/Xc7oC/ANHf5no=,iv:JVQLl/rp65kZSK/4SpVXxtiac3Z35XNkxWm2+lEdq/c=,tag:WgfaORiNlrO+wHSdnl4CWQ==,type:str] hash: ENC[AES256_GCM,data:EjJ+1fP7/9wG2jG0Jv2hxMLtErqxjHBstRjru79dd5ZXhqwT7S+jpLfl9WpZU9qi20ps9YP4qe7G08p6NJNXjYhQj852GQxEORRh/9StAZsPt3p8w+ePZSVbivPQH+FpPKWYxoH0VR7y3TnL66R0tKRLh1fNTc5jRy5rU5r1bfs1jZ0=,iv:0y9FxW4QdD7qHz3bPRWlwHFpvOsvlYhVrOItB6BzaE8=,tag:Wc7MZhP3QRYmvZcjpoEWtQ==,type:str] @@ -332,6 +321,33 @@ mastodon: #ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] # # +#ENC[AES256_GCM,data:I6O+AZ4EGMl6dXBowhMBG+c=,iv:bQ5mvEfVHDyTwNI+5hCsUkWEowP8fqir6lqLYPmsnbY=,tag:ta69wItzleRbky3vjU4+WA==,type:comment] +booklogr: + secret_key: ENC[AES256_GCM,data:yImb0UKqDIeyCmThb3upJPIxZVF7wiaLboP8PO/K2yjCjI0qXOgSkXJbrlw=,iv:yQjqT4831bqbRYgjQ72Vcg6omweWWPTgj6ZUHeaSGWA=,tag:y0BQxLTQklFP5hmJjBNAMw==,type:str] + oidc: + secret: ENC[AES256_GCM,data:iLFtafeePxJuC2NyV9hPzkxqgR+67k23Szgl1SPZtuOE1wS6a9YVroTQKMo=,iv:1w0Jy0N4pu5O1h+DYtzAUv0Qd75pRNxV5l38fyrDIWI=,tag:mZ11TOrJzC1sxBxjcOEagQ==,type:str] + hash: ENC[AES256_GCM,data:0ShwGvr69/J+mNZ8+ALL4hEbouABbWXcj1kMV9M8jbyhe48FwikaeKY1SKTQRDB3XFMdRhOR+NFRDQhLvNN/7RW16ZPF2jkj/am4MEUQ0z8jd+imK98cpb7XuXb6DuU1cqtuKhG0lJcFQaHOCNXqSWoPi8oRUwHIpvGjXa3Wr4hC/pw=,iv:iPXaqonARTWV19E0RUIPt05DJ9iDKSS8skcT4xf8wYc=,tag:n6S6x5Qeh+M3itRopj9SbQ==,type:str] +#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] +# +# +#ENC[AES256_GCM,data:BmZppA02YtiTykTtUnk=,iv:zV2x6W1yWhMegE+h7zdQNRxVrN7C16Lq4xpDkkZPcBw=,tag:MPGH1vjS1IYuZo2qf30qwA==,type:comment] +papra: + secret_key: ENC[AES256_GCM,data:AoqqUS2OUb11UwWNteZHqU6FF1eJVbmvANfZRvcwnvU+/cbYtPC8cHyRcBQ=,iv:6eWM/9NDApVMpTviYw2X1p91VItG3qlkGocjrkLyBq8=,tag:STXAVUqQEHGW+ijRQOZLrA==,type:str] + oidc: + secret: ENC[AES256_GCM,data:a019OZyi6HSP4t4IcDVOATKnrwIHooq9nh3g5mzq4x9fpcbM5o6UW3kCNlE=,iv:7ibp5/fCq/qGM3RcVbpl94OjGjuXL+Y1qvsx9t693aw=,tag:Sv2K89OBPGQFkag0vXenxw==,type:str] + hash: ENC[AES256_GCM,data:bHVmhaBo49OHH7ZGRdZvRcOyRZrorTFk+63H5esDwUAqZfXJfTVML9naOtoeYElPnh+Zx0EX06ozHwuqgK9ZcGVAi3hoxnWQeImKfVvlifazGIcVK8omNguPd8n5PhooqS6AiFixiizvTM6iLp1TvKLTCb8QywOVq7RxykJPu3XXDAA=,iv:/EsPo3bMtVYoGtyHydW96o404ZRI2Tqop76XozEZlhw=,tag:HlWGa096oG61Ua7Fr1OjPA==,type:str] +#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] +# +# +#ENC[AES256_GCM,data:5KE0gQZnd8NY49wg9A==,iv:L5SdkwVrYxujpgHMHgL6U8fNP7v+qMHBpfRyDAa8/Ds=,tag:mWwHMiR48Shu5DOssdMuNA==,type:comment] +romm: + secret_key: ENC[AES256_GCM,data:i+Dltol9oS9r+kIFoPUJ8sJsIkgDyW3W9C9SVWetZoFhC3b2UWwBpyJO+ZM=,iv:TDkP+D0jWkh/d6wgsOcyOfyCOvGud8L8hIEKtpvMThY=,tag:n/Hl0KEbauZVpixrfrTakg==,type:str] + oidc: + secret: ENC[AES256_GCM,data:G0knyOyj3oNuUCIQF0cb+/UkiZlk7/7FxABuqdbybXgJoC2xYkbGgesedaw=,iv:Wd3+phjBsdYSYV4+y+KPIFlX6sWVFGvJCdkyiIa0tII=,tag:1joAPQUXiKfvhyhj+DM+Uw==,type:str] + hash: ENC[AES256_GCM,data:Z36CsPTYBLC/qM3uiSkSjll2CL3+HwHxyu3k/JWpg/cquz1kkYE6jHzdSSPYR28uQiAxB28CVRV0zhgAFjhsloIV3MTzkTKRkTwHibJmkFUxRjkmynhUTOFqkKH88s+WMM3bZvIwrauvYnVtSknxg6lwmmUOaAdXZh/bdbW3bVChHXE=,iv:5p55hVvFrdWFjbjVjooLWhFSqSVInTAsfQlgyOvF4ls=,tag:YW6SSxEvQOOkLbaKNmGPvQ==,type:str] +#ENC[AES256_GCM,data:ODXFUxxxdQ==,iv:s9zJVx6wo6x517tbNvC+FZ0dFzqbjqeLI6rXBq72hQA=,tag:bXoV2I3LbpmQyddJrtS3Qg==,type:comment] +# +# #ENC[AES256_GCM,data:T4Wtn49AAxPd2QUFTR+q,iv:bH5goGWBDqumAat9dUv2OwfCUJUpuVqncTMqMBZUXhI=,tag:G+W6hHA+yftQ+4RJpXrxHg==,type:comment] switch: admin: @@ -348,7 +364,7 @@ sops: k+uyWPPi+enFkVaceSwMFrA66uaWWrwAj11sXEB7yzvGFPrnAGezjQ== -----END AGE ENCRYPTED FILE----- recipient: age120wuwcmsm845ztsvsz46pswj5je53uc2n35vadklrfqudu6cxuusxetk7y - lastmodified: "2026-07-05T12:35:46Z" - mac: ENC[AES256_GCM,data:fTpqEj1RulJgsW1ueIvSaZIabHGjG/bCk7KutUW+mYXjL2OUskFejnWIbw/vCVaqv/eQsDHRQWL7SSt5SVFOUwv9v6vqXdj/ZIEMIcF3hE3T65vt2Rr9kuB5kjaOwWNzKDUY32Gb1v4MmP2aEG4D7Lj0dVVrej85T6Nizpg0uc8=,iv:8+jqnFxmLSTi247Tu1HXzXUPWqnty0QVn1mSSLk4z9E=,tag:vct9Gq7kMwo+TwghBWKhkA==,type:str] + lastmodified: "2026-07-08T07:31:10Z" + mac: ENC[AES256_GCM,data:QjNInc+F1ZpvXP1LHlfJ6JgVpNcZHEX0072glJKAHAxx1DmRrgnHATFHBIIuPM6Y4WM+fLewrnu9Mlp7L7KKM5NKeq7aryEaT6cfoN69YG/pOpq2FlXA3WIBcdja7VA17dYeqJz8z7sN5qHTq2mY1KG1Z5ogg+F64TIOpYRFLNI=,iv:iTKy/SAaY66GnYxc0PXyDQH3C57fbi+DiMOkKBgPpjA=,tag:e+IpJ1hksr0HvvIpJo2tzA==,type:str] unencrypted_suffix: _unencrypted version: 3.13.2 diff --git a/config/services/containers/app/booklogr/booklogr-api.container.j2 b/config/services/containers/app/booklogr/booklogr-api.container.j2 new file mode 100644 index 0000000..b9fd01e --- /dev/null +++ b/config/services/containers/app/booklogr/booklogr-api.container.j2 @@ -0,0 +1,51 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=BookLogr API + +After=network-online.target +Wants=network-online.target + +[Container] +Image=docker.io/mozzo/booklogr:{{ version['containers']['booklogr'] }} +ContainerName=booklogr-api +HostName=booklogr-api + +PublishPort={{ services['booklogr']['ports']['api'] }}:5000/tcp + +Volume=%h/data/containers/booklogr/profile_pictures:/app/profile_pictures:rw +Volume=%h/data/containers/booklogr/export_data:/app/export_data:rw +Volume=%h/containers/booklogr/ssl:/etc/ssl/booklogr:ro + + +# General +Environment="TZ={{ timezone }}" +Environment="SINGLE_USER_MODE=false" +Environment="EXPORT_FOLDER=/app/export_data" + +# PostgreSQL +Environment="DATABASE_URL=postgresql://booklogr@{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}:{{ services['postgresql']['ports']['tcp'] }}/booklogr_db" +Environment="PGSSLMODE=verify-full" +Environment="PGSSLROOTCERT=/etc/ssl/booklogr/{{ root_cert_filename }}" +Secret=BOOKLOGR_POSTGRES_PASSWORD,type=env,target=PGPASSWORD + +# Authentication +Environment="AUTH_ALLOW_REGISTRATION=true" +Environment="AUTH_REQUIRE_VERIFICATION=false" +Secret=BOOKLOGR_AUTH_SECRET_KEY,type=env,target=AUTH_SECRET_KEY + +# OIDC - Authelia +Environment="OIDC_CLIENT_ID=booklogr" +Environment="OIDC_REDIRECT_URI=https://{{ services['booklogr']['domain']['public'] }}.{{ domain['public'] }}/callback" +Environment="OIDC_DISCOVERY_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}/.well-known/openid-configuration" +Secret=BOOKLOGR_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET + +[Service] +ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }} +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/app/booklogr/booklogr-web.container.j2 b/config/services/containers/app/booklogr/booklogr-web.container.j2 new file mode 100644 index 0000000..c337171 --- /dev/null +++ b/config/services/containers/app/booklogr/booklogr-web.container.j2 @@ -0,0 +1,42 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=BookLogr Web + +After=network-online.target booklogr-api.service +Wants=network-online.target booklogr-api.service + +[Container] +Image=docker.io/mozzo/booklogr-web:{{ version['containers']['booklogr'] }} +ContainerName=booklogr-web +HostName=booklogr-web + +PublishPort={{ services['booklogr']['ports']['http'] }}:80/tcp + + +# General +Environment="TZ={{ timezone }}" +Environment="BL_DEMO_MODE=false" +Environment="BL_DEBUG=false" +Environment="BL_CHECK_UPDATES=true" + +# API +Environment="BL_API_ENDPOINT=https://{{ services['booklogr']['domain']['public'] }}.{{ domain['public'] }}" + +# Authentication UI +Environment="BL_SINGLE_USER_MODE=false" +Environment="BL_ALLOW_MANUAL_LOGIN=false" +Environment="BL_ALLOW_REGISTRATION=false" + +# OIDC - Authelia +Environment="BL_OIDC_AUTHORITY=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" +Environment="BL_OIDC_CLIENT_ID=booklogr" + +[Service] +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/app/homepage/config/bookmarks.yaml.j2 b/config/services/containers/app/homepage/config/bookmarks.yaml.j2 index 19a322e..c011130 100644 --- a/config/services/containers/app/homepage/config/bookmarks.yaml.j2 +++ b/config/services/containers/app/homepage/config/bookmarks.yaml.j2 @@ -7,6 +7,9 @@ href: https://chatgpt.com - School: + - Job Schedule: + - abbr: LIB + href: https://docs.google.com/spreadsheets/d/1yaTmzANAKL5edrunH_G5k8cpNRHoLoAAyn5AplI_Y0I/htmlview#gid=0 - Portal: - abbr: SJU href: https://portal.sejong.ac.kr/ diff --git a/config/services/containers/app/mastodon/mastodon-streaming.container.j2 b/config/services/containers/app/mastodon/mastodon-streaming.container.j2 index 020c6e8..0046b19 100644 --- a/config/services/containers/app/mastodon/mastodon-streaming.container.j2 +++ b/config/services/containers/app/mastodon/mastodon-streaming.container.j2 @@ -12,7 +12,7 @@ Image=ghcr.io/mastodon/mastodon-streaming:{{ version['containers']['mastodon'] } ContainerName=mastodon-streaming HostName=mastodon-streaming -PublishPort={{ services['mastodon']['ports']['streaming'] }}:4000/tcp +PublishPort={{ services['mastodon']['ports']['api'] }}:4000/tcp Volume=%h/containers/mastodon/ssl:/etc/ssl/mastodon:ro diff --git a/config/services/containers/app/nextcloud/systemd/nextcloud-cron.service b/config/services/containers/app/nextcloud/services/nextcloud-cron.service similarity index 100% rename from config/services/containers/app/nextcloud/systemd/nextcloud-cron.service rename to config/services/containers/app/nextcloud/services/nextcloud-cron.service diff --git a/config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer b/config/services/containers/app/nextcloud/services/nextcloud-cron.timer similarity index 100% rename from config/services/containers/app/nextcloud/systemd/nextcloud-cron.timer rename to config/services/containers/app/nextcloud/services/nextcloud-cron.timer diff --git a/config/services/containers/app/papra/papra.container.j2 b/config/services/containers/app/papra/papra.container.j2 new file mode 100644 index 0000000..171d719 --- /dev/null +++ b/config/services/containers/app/papra/papra.container.j2 @@ -0,0 +1,43 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=Papra + +[Container] +Image=ghcr.io/papra-hq/papra:{{ version['containers']['papra'] }} + +ContainerName=papra +HostName=papra + +PublishPort={{ services['papra']['ports']['http'] }}:1221/tcp + +# Volumes +Volume=%h/data/containers/papra/data:/app/app-data:rw +Volume=%h/data/containers/papra/backup:/app/backup:rw + +# General +Environment="TZ={{ timezone }}" +Environment="APP_BASE_URL=https://{{ services['papra']['domain']['public'] }}.{{ domain['public'] }}" + +# Authentication +Environment="AUTH_PROVIDERS_EMAIL_IS_ENABLED=false" +Environment="AUTH_IS_PASSWORD_RESET_ENABLED=false" +Environment="AUTH_SHOW_LEGAL_LINKS=false" +Secret=PAPRA_AUTH_SECRET,type=env,target=AUTH_SECRET + +# OIDC +Secret=PAPRA_AUTH_PROVIDERS_CUSTOMS,type=env,target=AUTH_PROVIDERS_CUSTOMS + +# Documents +Environment="DOCUMENTS_CONTENT_EXTRACTION_ENABLED=true" +Environment="DOCUMENTS_OCR_LANGUAGES=eng,kor" +Environment="DOCUMENT_STORAGE_MAX_UPLOAD_SIZE=0" + +[Service] +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/app/papra/script/papra-exporter.sh b/config/services/containers/app/papra/script/papra-exporter.sh new file mode 100644 index 0000000..d1f26f6 --- /dev/null +++ b/config/services/containers/app/papra/script/papra-exporter.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +set -Eeuo pipefail + +URL="" +ORG_ID="" +TOKEN="" + +output_dir="$PWD/papra-export" +page_size=100 +page_index=0 +downloaded=0 + +# usage() function +usage() { + echo "Usage: $0 -u \"URL\" -o \"ORG_ID\" -t \"TOKEN\"" + echo "-u : Specify the URL of Papra, including https://" + echo "-o : Specify the organization ID of Papra" + echo "-t : Specify the token to access the Papra API" + echo "Token requires READ permissions" + exit 1 +} + +# Log function +log() { + local timestamp + local level="$1" + local msg="$2" + + timestamp="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + echo "time=\"$timestamp\" level=\"$level\" msg=\"$msg\" source=\"papra-exporter.sh\"" >&2 +} + +# getopts to get arguments +while getopts ":u:o:t:" opt; do + case "$opt" in + u) + URL="$OPTARG" + ;; + o) + ORG_ID="$OPTARG" + ;; + t) + TOKEN="$OPTARG" + ;; + \?) # unknown option + log "error" "Invalid option: -$OPTARG" + usage + ;; + :) # parameter required + log "error" "Option -$OPTARG requires an argument" + usage + ;; + esac +done + +# Validate required arguments +if [[ -z "$URL" || -z "$ORG_ID" || -z "$TOKEN" ]]; then + log "error" "URL, ORG_ID and TOKEN are required" + usage +fi + +# Require an explicit HTTPS URL +if [[ "$URL" != https://* ]]; then + log "error" "URL must start with https://" + exit 1 +fi + +# Remove trailing slash +URL="${URL%/}" + +# Always create a fresh export +rm -rf -- "$output_dir" +mkdir -p -- "$output_dir" + +while true; do + response="$( + curl \ + --fail \ + --silent \ + --show-error \ + --get \ + --header "Authorization: Bearer $TOKEN" \ + --data-urlencode "pageIndex=$page_index" \ + --data-urlencode "pageSize=$page_size" \ + "$URL/api/organizations/$ORG_ID/documents" + )" + + count="$(jq -r '.documents | length' <<<"$response")" + total="$(jq -r '.documentsCount' <<<"$response")" + + while IFS= read -r document; do + id="$(jq -r '.id' <<<"$document")" + name="$(jq -r '.name // empty' <<<"$document")" + + # Prevent document names from creating subdirectories. + name="${name//\//_}" + + if [[ -z "$name" || "$name" == "." || "$name" == ".." ]]; then + name="$id" + fi + + destination="$output_dir/$name" + + # Preserve both files when names collide. + if [[ -e "$destination" ]]; then + if [[ "$name" == *.* && "$name" != .* ]]; then + stem="${name%.*}" + extension=".${name##*.}" + destination="$output_dir/${stem}__${id}${extension}" + else + destination="$output_dir/${name}__${id}" + fi + fi + + temporary="${destination}.part" + rm -f -- "$temporary" + + if ! curl \ + --fail \ + --silent \ + --show-error \ + --location \ + --header "Authorization: Bearer $TOKEN" \ + --output "$temporary" \ + "$URL/api/organizations/$ORG_ID/documents/$id/file" + then + rm -f -- "$temporary" + log "error" "Failed to download document: $id" + exit 1 + fi + + mv -f -- "$temporary" "$destination" + + downloaded=$((downloaded + 1)) + + printf '[%d/%d] %s\n' \ + "$downloaded" \ + "$total" \ + "${destination#"$output_dir"/}" + + done < <(jq -c '.documents[]' <<<"$response") + + if (( count == 0 || downloaded >= total )); then + break + fi + + page_index=$((page_index + 1)) +done + +log "info" "Export completed: $downloaded document(s)" diff --git a/config/services/containers/app/papra/services/papra-backup.service b/config/services/containers/app/papra/services/papra-backup.service new file mode 100644 index 0000000..5ffe7eb --- /dev/null +++ b/config/services/containers/app/papra/services/papra-backup.service @@ -0,0 +1,68 @@ +[Unit] +Description=Papra sqlite backup +Requires=papra.service +After=papra.service + +[Service] +Type=oneshot +# Create a consistent SQLite snapshot and atomically publish it. +ExecStart=/usr/bin/podman exec -u nonroot papra node --input-type=module -e '\ + import { createClient } from "@libsql/client"; \ + import { rename, rm } from "node:fs/promises"; \ + \ + /* Paths */ \ + const databaseUrl = "file:/app/app-data/db/db.sqlite"; \ + const backupDir = "/app/backup"; \ + \ + /* Build the backup name from the container-local date. */ \ + const now = new Date(); \ + const date = [ \ + now.getFullYear(), \ + String(now.getMonth() + 1).padStart(2, "0"), \ + String(now.getDate()).padStart(2, "0"), \ + ].join("-"); \ + \ + const dst = backupDir + "/db_" + date + ".sqlite"; \ + const tmp = dst + ".tmp"; \ + \ + /* Never expose a partially-created backup as a completed one. */ \ + let completed = false; \ + try { \ + await rm(tmp, { force: true }); \ + \ + /* Create a transactionally consistent SQLite snapshot. */ \ + const db = createClient({ url: databaseUrl }); \ + try { \ + await db.execute({ \ + sql: "VACUUM INTO ?", \ + args: [tmp], \ + }); \ + } finally { \ + db.close(); \ + } \ + \ + /* Publish only after VACUUM INTO has completed successfully. */ \ + await rename(tmp, dst); \ + completed = true; \ + } finally { \ + if (!completed) { \ + await rm(tmp, { force: true }); \ + } \ + }' +ExecStart=/usr/bin/podman exec -u nonroot papra sh -c "find /app/backup/ -maxdepth 1 -type f -name 'db_*.sqlite' -mtime +7 -delete" + +# Verify +#podman exec -u nonroot -w /app papra \ +# node --input-type=module -e ' +# const m=await import(process.argv[1]); +# const db=m.createClient({url:process.argv[2]}); +# try { +# const r=await db.execute("PRAGMA quick_check"); +# console.log(r.rows); +# } finally { +# db.close(); +# } +# ' \ +# "@libsql/client" \ +# "file:/app/backup/test-backup.sqlite" +# > Result: [ { quick_check: 'ok' } ] diff --git a/config/services/containers/app/papra/services/papra-backup.timer b/config/services/containers/app/papra/services/papra-backup.timer new file mode 100644 index 0000000..a28e06d --- /dev/null +++ b/config/services/containers/app/papra/services/papra-backup.timer @@ -0,0 +1,14 @@ +[Unit] +Description=Run Papra Data Backup service every day + +[Timer] +# Execute service after 1 min on booting +OnBootSec=1min + +OnCalendar=*-*-* 23:50:00 + +# When timer is activated, Service also starts. +Persistent=true + +[Install] +WantedBy=timers.target diff --git a/config/services/containers/app/romm/romm.container.j2 b/config/services/containers/app/romm/romm.container.j2 new file mode 100644 index 0000000..dd9d2a4 --- /dev/null +++ b/config/services/containers/app/romm/romm.container.j2 @@ -0,0 +1,80 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=RomM + +After=redis_romm.service +Wants=redis_romm.service + +[Container] +Image=docker.io/rommapp/romm:{{ version['containers']['romm'] }} +ContainerName=romm +HostName=romm + +PublishPort={{ services['romm']['ports']['http'] }}:8080/tcp + +# Volumes +Volume=%h/data/containers/romm/library:/romm/library:rw +Volume=%h/data/containers/romm/resources:/romm/resources:rw +Volume=%h/data/containers/romm/assets:/romm/assets:rw +Volume=%h/containers/romm/ssl:/etc/ssl/romm:ro + +# General +Environment="TZ={{ timezone }}" +Environment="ROMM_BASE_URL=https://{{ services['romm']['domain']['public'] }}.{{ domain['public'] }}" +Environment="DISABLE_SETUP_WIZARD=true" +Environment="DISABLE_USERPASS_LOGIN=true" + +# Secrets +Secret=ROMM_AUTH_SECRET_KEY,type=env,target=ROMM_AUTH_SECRET_KEY + +# Database +Environment="ROMM_DB_DRIVER=postgresql" +Environment="DB_HOST={{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}" +Environment="DB_PORT={{ services['postgresql']['ports']['tcp'] }}" +Environment="DB_NAME=romm_db" +Environment="DB_USER=romm" +Environment="PGSSLMODE=verify-full" +Environment="PGSSLROOTCERT=/etc/ssl/romm/{{ root_cert_filename }}" +Secret=ROMM_DATABASE_PASSWORD,type=env,target=DB_PASSWD + +# Redis +Environment="REDIS_HOST=host.containers.internal" +Environment="REDIS_PORT={{ services['romm']['ports']['redis'] }}" + +# OIDC +Environment="OIDC_ENABLED=true" +Environment="OIDC_PROVIDER=authelia" +Environment="OIDC_CLIENT_ID=romm" +Environment="OIDC_REDIRECT_URI=https://{{ services['romm']['domain']['public'] }}.{{ domain['public'] }}/api/oauth/openid" +Environment="OIDC_SERVER_APPLICATION_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" +Environment="OIDC_USERNAME_ATTRIBUTE=preferred_username" +Environment="OIDC_ALLOW_REGISTRATION=true" +Environment="OIDC_CLAIM_ROLES=groups" +Environment="OIDC_ROLE_ADMIN=admins" +Environment="OIDC_ROLE_VIEWER=users" +Environment="OIDC_AUTOLOGIN=false" +Secret=ROMM_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET + +# Players +Environment="DISABLE_EMULATOR_JS=false" +Environment="DISABLE_RUFFLE_RS=false" + +# Metadata +Environment="HASHEOUS_API_ENABLED=true" +#Environment="PLAYMATCH_API_ENABLED=true" + +# Optional +Environment="LOGLEVEL=INFO" +Environment="DISABLE_LOGS_VIEWER=true" + +[Service] +ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }} + +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/config/services/containers/auth/authelia/config/authelia.yaml.j2 b/config/services/containers/auth/authelia/config/authelia.yaml.j2 index 553a1d5..a9dad25 100644 --- a/config/services/containers/auth/authelia/config/authelia.yaml.j2 +++ b/config/services/containers/auth/authelia/config/authelia.yaml.j2 @@ -55,7 +55,7 @@ access_control: # authelia portal - domain: '{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}' policy: 'bypass' -# - domain: 'test.ilnmors.com' +# - domain: 'test.example.com' # policy: 'one_factor' # subject: # - 'group:admins' @@ -118,6 +118,23 @@ identity_providers: access_token: '1h' id_token: '1h' refresh_token: '720h' + cors: + # booklogr cors configuration + endpoints: + - 'authorization' + - 'token' + - 'userinfo' + allowed_origins: + - 'https://{{ services['booklogr']['domain']['public'] }}.{{ domain['public'] }}' + claims_policies: + romm: + id_token: + - 'email' + - 'email_verified' + - 'alt_emails' + - 'preferred_username' + - 'name' + - 'groups' clients: # https://www.authelia.com/integration/openid-connect/clients/gitea/ - client_id: 'gitea' @@ -165,29 +182,6 @@ identity_providers: access_token_signed_response_alg: 'none' userinfo_signed_response_alg: 'none' token_endpoint_auth_method: 'client_secret_post' - # https://www.authelia.com/integration/openid-connect/clients/paperless/ - - client_id: 'paperless' - client_name: 'Paperless' - consent_mode: 'implicit' - client_secret: '{{ hostvars['console']['paperless']['oidc']['hash'] }}' - public: false - authorization_policy: 'one_factor' - require_pkce: true - pkce_challenge_method: 'S256' - redirect_uris: - - 'https://{{ services['paperless']['domain']['public'] }}.{{ domain['public'] }}/accounts/oidc/authelia/login/callback/' - scopes: - - 'openid' - - 'profile' - - 'email' - - 'groups' - response_types: - - 'code' - grant_types: - - 'authorization_code' - access_token_signed_response_alg: 'none' - userinfo_signed_response_alg: 'none' - token_endpoint_auth_method: 'client_secret_post' # https://www.authelia.com/integration/openid-connect/clients/nextcloud/#openid-connect-user-backend-app - client_id: 'nextcloud' client_name: 'Nextcloud' @@ -345,3 +339,71 @@ identity_providers: grant_types: - 'authorization_code' token_endpoint_auth_method: 'client_secret_basic' + # https://booklogr.app/docs/Configuration/OIDC-sign-in + - client_id: 'booklogr' + client_name: 'booklogr' + consent_mode: 'implicit' + client_secret: '{{ hostvars['console']['booklogr']['oidc']['hash'] }}' + public: false + authorization_policy: 'one_factor' + require_pkce: false + pkce_challenge_method: '' + redirect_uris: + - 'https://{{ services['booklogr']['domain']['public'] }}.{{ domain['public'] }}/callback' + scopes: + - 'openid' + - 'profile' + - 'email' + response_types: + - 'code' + grant_types: + - 'authorization_code' + access_token_signed_response_alg: 'none' + userinfo_signed_response_alg: 'none' + token_endpoint_auth_method: 'client_secret_post' + # https://www.authelia.com/integration/openid-connect/clients/papra/ + - client_id: 'papra' + client_name: 'papra' + consent_mode: 'implicit' + client_secret: '{{ hostvars['console']['papra']['oidc']['hash'] }}' + public: false + authorization_policy: 'one_factor' + require_pkce: false + pkce_challenge_method: '' + redirect_uris: + - 'https://{{ services['papra']['domain']['public'] }}.{{ domain['public'] }}/api/auth/oauth2/callback/authelia' + scopes: + - 'openid' + - 'profile' + - 'email' + response_types: + - 'code' + grant_types: + - 'authorization_code' + access_token_signed_response_alg: 'none' + userinfo_signed_response_alg: 'none' + token_endpoint_auth_method: 'client_secret_post' + # https://www.authelia.com/integration/openid-connect/clients/romm/ + - client_id: 'romm' + claims_policy: 'romm' + client_name: 'romm' + consent_mode: 'implicit' + client_secret: '{{ hostvars['console']['romm']['oidc']['hash'] }}' + public: false + authorization_policy: 'one_factor' + require_pkce: false + pkce_challenge_method: '' + redirect_uris: + - 'https://{{ services['romm']['domain']['public'] }}.{{ domain['public'] }}/api/oauth/openid' + scopes: + - 'openid' + - 'groups' + - 'profile' + - 'email' + response_types: + - 'code' + grant_types: + - 'authorization_code' + access_token_signed_response_alg: 'none' + userinfo_signed_response_alg: 'none' + token_endpoint_auth_method: 'client_secret_basic' diff --git a/config/services/containers/common/caddy/etc/Caddyfile.j2 b/config/services/containers/common/caddy/etc/Caddyfile.j2 index 02f74f7..d9f3765 100644 --- a/config/services/containers/common/caddy/etc/Caddyfile.j2 +++ b/config/services/containers/common/caddy/etc/Caddyfile.j2 @@ -23,7 +23,6 @@ {% if node['name'] == 'app' %} { servers { - # Only accept packets from auth main caddy trusted_proxies static {{ hostvars['fw']['network']['auth']['ipv4']['server'] }} {{ hostvars['fw']['network']['auth']['ipv6']['server'] }} } } @@ -84,9 +83,13 @@ {% elif node['name'] == 'app' %} {{ service['domain']['internal'] }}.{{ domain['internal'] }} { import private_tls -{% if service['ports']['streaming'] is defined %} - @streaming path /api/v1/streaming* - reverse_proxy @streaming host.containers.internal:{{ service['ports']['streaming'] }} { +{% if service['route'] is defined and service['route']['deny'] is defined %} + @deny path {{ service['route']['deny'] }} + respond @deny 404 +{% endif %} +{% if service['route'] is defined and service['route']['api'] is defined %} + @api path {{ service['route']['api'] }} + reverse_proxy @api host.containers.internal:{{ service['ports']['api'] }} { header_up Host {http.request.header.X-Forwarded-Host} } {% endif %} diff --git a/config/services/systemd/common/kopia/kopia-backup.timer.j2 b/config/services/systemd/common/kopia/kopia-backup.timer.j2 index 909a5cc..28bc9b7 100644 --- a/config/services/systemd/common/kopia/kopia-backup.timer.j2 +++ b/config/services/systemd/common/kopia/kopia-backup.timer.j2 @@ -2,7 +2,11 @@ Description=Daily Kopia backup timer [Timer] -OnCalendar=*-*-* 03:00:00 +{% if node['name'] == "app" %} +OnCalendar=*-*-* 00:00:00 +{% elif node['name'] == "infra" %} +OnCalendar=*-*-* 01:00:00 +{% endif %} Persistent=true RandomizedDelaySec=300 diff --git a/config/services/systemd/fw/blocky/etc/config.yaml.j2 b/config/services/systemd/fw/blocky/etc/config.yaml.j2 index c47c360..49a10ff 100644 --- a/config/services/systemd/fw/blocky/etc/config.yaml.j2 +++ b/config/services/systemd/fw/blocky/etc/config.yaml.j2 @@ -37,8 +37,7 @@ conditional: {{ network['reverse_zone']['ipv4']['server'] }}: "{{ hostvars['fw']['network']['bind']['ipv4']['server'] }}, {{ hostvars['fw']['network']['bind']['ipv6']['server'] }}" {{ network['reverse_zone']['ipv6']['client'] }}: "{{ hostvars['fw']['network']['bind']['ipv4']['server'] }}, {{ hostvars['fw']['network']['bind']['ipv6']['server'] }}" {{ network['reverse_zone']['ipv6']['server'] }}: "{{ hostvars['fw']['network']['bind']['ipv4']['server'] }}, {{ hostvars['fw']['network']['bind']['ipv6']['server'] }}" - vpn.{{ domain['public'] }}: "tcp-tls:1.1.1.1:853, tcp-tls:1.0.0.1:853, tcp-tls:[2606:4700:4700::1111]:853, tcp-tls:[2606:4700:4700::1001]:853" - + {{ services['wireguard']['domain']['public'] }}.{{ domain['public'] }}: "tcp-tls:1.1.1.1:853, tcp-tls:1.0.0.1:853, tcp-tls:[2606:4700:4700::1111]:853, tcp-tls:[2606:4700:4700::1001]:853" blocking: blockType: nxDomain denylists: diff --git a/docs/archives/services/app/ezbookkeeping/ezbookkeeping.md b/docs/archives/services/app/ezbookkeeping/ezbookkeeping.md index b7b0f13..6377e1e 100644 --- a/docs/archives/services/app/ezbookkeeping/ezbookkeeping.md +++ b/docs/archives/services/app/ezbookkeeping/ezbookkeeping.md @@ -6,7 +6,7 @@ - Create the password with `openssl rand -base64 32` - Save this value in secrets.yaml in `postgresql.password.ezbookkeeping` - - Access infra server to create paperless_db with `podman exec -it postgresql psql -U postgres` + - Access infra server to create ezbookkeeping_db with `podman exec -it postgresql psql -U postgres` ```SQL CREATE USER ezbookkeeping WITH PASSWORD 'postgresql.password.ezbookkeeping'; diff --git a/docs/archives/services/app/paperless/authelia.yaml b/docs/archives/services/app/paperless/authelia.yaml new file mode 100644 index 0000000..ebd5acd --- /dev/null +++ b/docs/archives/services/app/paperless/authelia.yaml @@ -0,0 +1,23 @@ +# https://www.authelia.com/integration/openid-connect/clients/paperless/ + - client_id: 'paperless' + client_name: 'Paperless' + consent_mode: 'implicit' + client_secret: '' + public: false + authorization_policy: 'one_factor' + require_pkce: true + pkce_challenge_method: 'S256' + redirect_uris: + - 'https://paperless.example.com/accounts/oidc/authelia/login/callback/' + scopes: + - 'openid' + - 'profile' + - 'email' + - 'groups' + response_types: + - 'code' + grant_types: + - 'authorization_code' + access_token_signed_response_alg: 'none' + userinfo_signed_response_alg: 'none' + token_endpoint_auth_method: 'client_secret_post' \ No newline at end of file diff --git a/config/services/containers/app/paperless/build/paperless.containerfile.j2 b/docs/archives/services/app/paperless/build/paperless.containerfile.j2 similarity index 100% rename from config/services/containers/app/paperless/build/paperless.containerfile.j2 rename to docs/archives/services/app/paperless/build/paperless.containerfile.j2 diff --git a/docs/archives/services/app/paperless/group_vars.yaml b/docs/archives/services/app/paperless/group_vars.yaml new file mode 100644 index 0000000..3ec8a9d --- /dev/null +++ b/docs/archives/services/app/paperless/group_vars.yaml @@ -0,0 +1,14 @@ +services: + paperless: + domain: + public: "" + internal: "" + ports: + http: "" + redis: "" + subuid: "100999" + dependency: + web: "" + db: "" +version: + paperless: "2.20.15" diff --git a/config/services/containers/app/paperless/paperless.container.j2 b/docs/archives/services/app/paperless/paperless.container.j2 similarity index 100% rename from config/services/containers/app/paperless/paperless.container.j2 rename to docs/archives/services/app/paperless/paperless.container.j2 diff --git a/docs/archives/services/app/paperless/secret.example.yaml b/docs/archives/services/app/paperless/secret.example.yaml new file mode 100644 index 0000000..1d16f4d --- /dev/null +++ b/docs/archives/services/app/paperless/secret.example.yaml @@ -0,0 +1,13 @@ +--- +postgresql: + password: + paperless: "" + +paperless: + session_secret: "" + admin: + id: "" + password: "" + oidc: + secret: "" + hash: "" diff --git a/ansible/roles/app/tasks/services/set_paperless.yaml b/docs/archives/services/app/paperless/set_paperless.yaml similarity index 100% rename from ansible/roles/app/tasks/services/set_paperless.yaml rename to docs/archives/services/app/paperless/set_paperless.yaml diff --git a/docs/archives/services/app/readur/authelia.yaml b/docs/archives/services/app/readur/authelia.yaml new file mode 100644 index 0000000..1ba4cc0 --- /dev/null +++ b/docs/archives/services/app/readur/authelia.yaml @@ -0,0 +1,21 @@ +--- +identity_providers: + oidc: + # readur + - client_id: 'readur' + client_name: 'readur' + consent_mode: 'implicit' + client_secret: '' + public: false + authorization_policy: 'one_factor' + redirect_uris: + - 'https://readur.example.com/api/auth/oidc/callback' + scopes: + - 'openid' + - 'profile' + - 'email' + response_types: + - 'code' + grant_types: + - 'authorization_code' + token_endpoint_auth_method: 'client_secret_basic' diff --git a/docs/archives/services/app/readur/group_vars.yaml b/docs/archives/services/app/readur/group_vars.yaml new file mode 100644 index 0000000..cd715e1 --- /dev/null +++ b/docs/archives/services/app/readur/group_vars.yaml @@ -0,0 +1,13 @@ +services: + readur: + domain: + public: "" + internal: "" + ports: + http: "" + redis: "" + dependency: + web: "" + db: "" +version: + readur: "2.9.1" diff --git a/docs/archives/services/app/readur/readur.container.j2 b/docs/archives/services/app/readur/readur.container.j2 new file mode 100644 index 0000000..a85d9a0 --- /dev/null +++ b/docs/archives/services/app/readur/readur.container.j2 @@ -0,0 +1,47 @@ +[Quadlet] +DefaultDependencies=false + +[Unit] +Description=Readur + +[Container] +Image=ghcr.io/readur/readur:{{ version['containers']['readur'] }} + +ContainerName=readur +HostName=readur + +PublishPort={{ services['readur']['ports']['http'] }}:8000/tcp + +# Volumes +Volume=%h/data/containers/readur/uploads:/app/uploads:rw +Volume=%h/data/containers/readur/watch:/app/watch:rw +Volume=%h/containers/readur/ssl:/etc/ssl/readur:ro + +# General +Environment="TZ={{ timezone }}" +Environment="ALLOW_LOCAL_AUTH=false" +Environment="OCR_LANGUAGE=eng+kor" +Secret=READUR_JWT_SECRET,type=env,target=JWT_SECRET + +# Database +Secret=READUR_DATABASE_URL,type=env,target=DATABASE_URL + +# OIDC +Environment="OIDC_ENABLED=true" +Environment="OIDC_CLIENT_ID=readur" +Environment="OIDC_ISSUER_URL=https://{{ services['authelia']['domain']['public'] }}.{{ domain['public'] }}" +Environment="OIDC_REDIRECT_URI=https://{{ services['readur']['domain']['public'] }}.{{ domain['public'] }}/api/auth/oidc/callback" +Environment="OIDC_AUTO_REGISTER=true" +Secret=READUR_OIDC_CLIENT_SECRET,type=env,target=OIDC_CLIENT_SECRET + +# Logging +Environment="RUST_LOG=warn" + +[Service] +ExecStartPre=/usr/bin/nc -zv {{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }} {{ services['postgresql']['ports']['tcp'] }} +Restart=always +RestartSec=10s +TimeoutStopSec=120 + +[Install] +WantedBy=default.target diff --git a/docs/archives/services/app/readur/secret.example.yaml b/docs/archives/services/app/readur/secret.example.yaml new file mode 100644 index 0000000..f8816e5 --- /dev/null +++ b/docs/archives/services/app/readur/secret.example.yaml @@ -0,0 +1,10 @@ +--- +postgresql: + password: + readur: "" + +readur: + jwt_secret: "" + oidc: + secret: "" + hash: "" diff --git a/docs/archives/services/app/readur/set_readur.yaml b/docs/archives/services/app/readur/set_readur.yaml new file mode 100644 index 0000000..d90edbb --- /dev/null +++ b/docs/archives/services/app/readur/set_readur.yaml @@ -0,0 +1,148 @@ +--- +- name: Set redis service name + ansible.builtin.set_fact: + redis_service: "readur" + +- name: Create redis_readur directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ services['redis']['subuid'] }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "containers/redis" + - "containers/redis/{{ redis_service }}" + - "containers/redis/{{ redis_service }}/data" + become: true + +- name: Deploy redis config file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.conf.j2" + dest: "{{ node['home_path'] }}/containers/redis/{{ redis_service }}/redis.conf" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + register: "is_redis_conf" + +- name: Deploy redis container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/redis/redis.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/redis_{{ redis_service }}.container" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + register: "is_redis_containerfile" + +- name: Enable (Restart) redis service + ansible.builtin.systemd: + name: "redis_{{ redis_service }}.service" + state: "restarted" + enabled: true + daemon_reload: true + scope: "user" + when: is_redis_conf.changed or is_redis_containerfile.changed # noqa: no-handler + +- name: Create readur directory + ansible.builtin.file: + path: "{{ node['home_path'] }}/{{ item }}" + state: "directory" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0770" + loop: + - "data/containers/readur" + - "data/containers/readur/uploads" + - "data/containers/readur/watch" + - "containers/readur" + - "containers/readur/ssl" + become: true + +- name: Deploy root certificate + ansible.builtin.copy: + content: | + {{ hostvars['console']['ca']['root']['crt'] }} + dest: "{{ node['home_path'] }}/containers/readur/ssl/{{ root_cert_filename }}" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0440" + become: true + notify: "notification_restart_readur" + no_log: true + +- name: Pull image + containers.podman.podman_image: + name: "ghcr.io/readur/readur" + tag: "{{ version['containers']['readur'] }}" + state: "present" + pull: true + +- name: Register secret value to podman secret + containers.podman.podman_secret: + name: "{{ item.name }}" + data: "{{ item.value }}" + state: "present" + force: true + loop: + - name: "READUR_JWT_SECRET" + value: "{{ hostvars['console']['readur']['jwt_secret'] }}" + - name: "READUR_DATABASE_URL" + value: "postgresql://readur:{{ hostvars['console']['postgresql']['password']['readur'] | urlencode | replace('/', '%2F') }}\ + @{{ services['postgresql']['domain']['internal'] }}.{{ domain['internal'] }}/readur_db?sslmode=verify-full&\ + sslrootcert=/etc/ssl/readur/{{ root_cert_filename }}" + - name: "READUR_OIDC_CLIENT_SECRET" + value: "{{ hostvars['console']['readur']['oidc']['secret'] }}" + notify: "notification_restart_readur" + no_log: true + +- name: Deploy readur.container file + ansible.builtin.template: + src: "{{ hostvars['console']['node']['config_path'] }}/services/containers/app/readur/readur.container.j2" + dest: "{{ node['home_path'] }}/.config/containers/systemd/readur.container" + owner: "{{ ansible_user }}" + group: "{{ node['group'] }}" + mode: "0644" + notify: "notification_restart_readur" + +- name: Enable readur.service + ansible.builtin.systemd: + name: "readur.service" + state: "started" + enabled: true + daemon_reload: true + scope: "user" + +- name: Check container archive images + ansible.builtin.stat: + path: "{{ node['home_path'] }}/archives/containers/{{ item.file }}.tar" + loop: + - name: "redis" + image: "docker.io/library/redis:{{ version['containers']['redis'] }}" + file: "docker.io_library_redis_{{ version['containers']['redis'] }}" + - name: "readur" + image: "ghcr.io/readur/readur:{{ version['containers']['readur'] }}" + file: "ghcr.io_readur_readur_{{ version['containers']['readur'] }}" + loop_control: + label: "{{ item.name }}" + register: container_archive_images + +- name: Save container archive images + containers.podman.podman_save: + image: + - "{{ item.item.image }}" + dest: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + format: "oci-archive" + force: false + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}" + when: not item.stat.exists + +- name: Fetch container archive images + ansible.builtin.fetch: + src: "{{ node['home_path'] }}/archives/containers/{{ item.item.file }}.tar" + dest: "{{ hostvars['console']['node']['data_path'] }}/images/containers/" + flat: true + loop: "{{ container_archive_images.results }}" + loop_control: + label: "{{ item.item.file }}"