diff --git a/roles/web-app-bookwyrm/TODO.md b/roles/web-app-bookwyrm/TODO.md deleted file mode 100644 index f8e5923c..00000000 --- a/roles/web-app-bookwyrm/TODO.md +++ /dev/null @@ -1,2 +0,0 @@ -# Todo -- Implement https://joinbookwyrm.com/de/ \ No newline at end of file diff --git a/roles/web-app-bookwyrm/config/main.yml b/roles/web-app-bookwyrm/config/main.yml new file mode 100644 index 00000000..b2969e08 --- /dev/null +++ b/roles/web-app-bookwyrm/config/main.yml @@ -0,0 +1,37 @@ +credentials: {} +docker: + services: + database: + enabled: true + redis: + enabled: true + application: + image: bookwyrm/bookwyrm + version: latest + name: bookwyrm + worker: + enabled: true + volumes: + data: "bookwyrm_data" + media: "bookwyrm_media" +features: + matomo: true + css: true + desktop: true + central_database: true + logout: true + oidc: false + ldap: false +server: + csp: + whitelist: {} + flags: {} + domains: + canonical: + - "book.{{ PRIMARY_DOMAIN }}" + aliases: + - "bookwyrm.{{ PRIMARY_DOMAIN }}" +rbac: + roles: {} +registration_open: false +allow_invite_request: false diff --git a/roles/web-app-bookwyrm/meta/main.yml b/roles/web-app-bookwyrm/meta/main.yml index 356fef42..8474b3ff 100644 --- a/roles/web-app-bookwyrm/meta/main.yml +++ b/roles/web-app-bookwyrm/meta/main.yml @@ -15,8 +15,10 @@ galaxy_info: repository: "https://s.infinito.nexus/code" issue_tracker_url: "https://s.infinito.nexus/issues" documentation: "https://s.infinito.nexus/code/tree/main/roles/web-app-bookwyrm" - min_ansible_version: "2.9" - platforms: - - name: Any - versions: - - all + logo: + class: "fas fa-book" + run_after: + - web-app-matomo + - web-app-keycloak + - web-app-mailu +dependencies: [] diff --git a/roles/web-app-bookwyrm/schema/main.yml b/roles/web-app-bookwyrm/schema/main.yml new file mode 100644 index 00000000..81c351b9 --- /dev/null +++ b/roles/web-app-bookwyrm/schema/main.yml @@ -0,0 +1,6 @@ +credentials: + secret_key: + description: "Django SECRET_KEY for BookWyrm" + algorithm: "alphanumeric" # uses generate_value('alphanumeric') → 64 random a-zA-Z0-9 + validation: + min_length: 50 # Django recommends ≥50 characters diff --git a/roles/web-app-bookwyrm/tasks/main.yml b/roles/web-app-bookwyrm/tasks/main.yml new file mode 100644 index 00000000..4c32e189 --- /dev/null +++ b/roles/web-app-bookwyrm/tasks/main.yml @@ -0,0 +1,7 @@ +--- +- block: + - name: "load docker, db/redis and proxy for {{ application_id }}" + include_role: + name: sys-stk-full-stateful + - include_tasks: utils/run_once.yml + when: run_once_web_app_bookwyrm is not defined \ No newline at end of file diff --git a/roles/web-app-bookwyrm/templates/Dockerfile.j2 b/roles/web-app-bookwyrm/templates/Dockerfile.j2 new file mode 100644 index 00000000..48f5aa01 --- /dev/null +++ b/roles/web-app-bookwyrm/templates/Dockerfile.j2 @@ -0,0 +1,7 @@ +FROM "{{ BOOKWYRM_IMAGE }}:{{ BOOKWYRM_VERSION }}" + +# Place for optional plugins/patches +# COPY ./patches/ /app/patches/ + +# Ensure media/data exist (UID/GID depend on upstream; keep generic) +RUN mkdir -p /app/data /app/media && chown -R 1000:1000 /app/data /app/media diff --git a/roles/web-app-bookwyrm/templates/docker-compose.yml.j2 b/roles/web-app-bookwyrm/templates/docker-compose.yml.j2 new file mode 100644 index 00000000..ab188e8a --- /dev/null +++ b/roles/web-app-bookwyrm/templates/docker-compose.yml.j2 @@ -0,0 +1,40 @@ +{% include 'roles/docker-compose/templates/base.yml.j2' %} + + application: + build: + context: . + dockerfile: Dockerfile + image: "{{ BOOKWYRM_CUSTOM_IMAGE }}" + container_name: "{{ BOOKWYRM_CONTAINER }}" + hostname: "{{ BOOKWYRM_HOSTNAME }}" + ports: + - "127.0.0.1:{{ ports.localhost.http[application_id] }}:{{ container_port }}" + environment: +{% include 'roles/web-app-bookwyrm/templates/env.j2' %} + volumes: + - 'data:/app/data' + - 'media:/app/media' +{% include 'roles/docker-container/templates/healthcheck/curl.yml.j2' %} +{% include 'roles/docker-container/templates/base.yml.j2' %} +{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %} +{% include 'roles/docker-container/templates/networks.yml.j2' %} + + worker: + image: "{{ BOOKWYRM_CUSTOM_IMAGE }}" + container_name: "{{ BOOKWYRM_CONTAINER }}-worker" + command: "bash -lc 'celery -A celerywyrm worker -l INFO'" + environment: +{% include 'roles/web-app-bookwyrm/templates/env.j2' %} + volumes: + - 'data:/app/data' + - 'media:/app/media' +{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %} +{% include 'roles/docker-container/templates/networks.yml.j2' %} + +{% include 'roles/docker-compose/templates/volumes.yml.j2' %} + data: + name: {{ BOOKWYRM_DATA_VOLUME }} + media: + name: {{ BOOKWYRM_MEDIA_VOLUME }} + +{% include 'roles/docker-compose/templates/networks.yml.j2' %} diff --git a/roles/web-app-bookwyrm/templates/env.j2 b/roles/web-app-bookwyrm/templates/env.j2 new file mode 100644 index 00000000..bfc8cf1e --- /dev/null +++ b/roles/web-app-bookwyrm/templates/env.j2 @@ -0,0 +1,40 @@ +# Core +BOOKWYRM_URL="{{ BOOKWYRM_URL }}" +DOMAIN="{{ BOOKWYRM_HOSTNAME }}" +PORT="{{ WEB_PORT }}" +WEB_PROTOCOL="{{ WEB_PROTOCOL }}" +MEDIA_ROOT="/app/media" +DATA_ROOT="/app/data" +REGISTRATION_OPEN={{ BOOKWYRM_REGISTRATION_OPEN }} +ALLOW_INVITE_REQUESTS={{ BOOKWYRM_ALLOW_INVITE_REQUESTS }} + +# Django/Secrets (provide via vault/env in production) +SECRET_KEY="{{ BOOKWYRM_SECRET_KEY }}" +EMAIL="{{ users['no-reply'].email }}" + +# Database +DATABASE_URL="postgres://{{ database_username }}:{{ database_password }}@{{ database_host }}:{{ database_port }}/{{ database_name }}" + +# Redis / Celery +REDIS_BROKER_URL="redis://{{ BOOKWYRM_REDIS_HOST }}:{{ BOOKWYRM_REDIS_PORT }}/0" +REDIS_CACHE_URL="redis://{{ BOOKWYRM_REDIS_HOST }}:{{ BOOKWYRM_REDIS_PORT }}/1" + +# Proxy (if BookWyrm sits behind reverse proxy) +FORWARDED_ALLOW_IPS="*" +USE_X_FORWARDED_HOST="true" +SECURE_PROXY_SSL_HEADER="HTTP_X_FORWARDED_PROTO,{{ WEB_PROTOCOL }}" + +# OIDC (optional – only if BOOKWYRM_OIDC_ENABLED) +{% if BOOKWYRM_OIDC_ENABLED %} +OIDC_TITLE="{{ BOOKWYRM_OIDC_LABEL | replace('\"','\\\"') }}" +OIDC_ISSUER="{{ BOOKWYRM_OIDC_ISSUER }}" +OIDC_AUTHORIZATION_ENDPOINT="{{ BOOKWYRM_OIDC_AUTH_URL }}" +OIDC_TOKEN_ENDPOINT="{{ BOOKWYRM_OIDC_TOKEN_URL }}" +OIDC_USERINFO_ENDPOINT="{{ BOOKWYRM_OIDC_USERINFO_URL }}" +OIDC_END_SESSION_ENDPOINT="{{ BOOKWYRM_OIDC_LOGOUT_URL }}" +OIDC_JWKS_URI="{{ BOOKWYRM_OIDC_JWKS_URL }}" +OIDC_CLIENT_ID="{{ BOOKWYRM_OIDC_CLIENT_ID }}" +OIDC_CLIENT_SECRET="{{ BOOKWYRM_OIDC_CLIENT_SECRET }}" +OIDC_SCOPES="{{ BOOKWYRM_OIDC_SCOPES }}" +OIDC_UNIQUE_ATTRIBUTE="{{ BOOKWYRM_OIDC_UNIQUE_ATTRIBUTE }}" +{% endif %} diff --git a/roles/web-app-bookwyrm/vars/main.yml b/roles/web-app-bookwyrm/vars/main.yml index 06c2a214..5b9d0b88 100644 --- a/roles/web-app-bookwyrm/vars/main.yml +++ b/roles/web-app-bookwyrm/vars/main.yml @@ -1 +1,45 @@ -application_id: web-app-bookwyrm \ No newline at end of file +# General +application_id: "web-app-bookwyrm" +database_type: "postgres" + +# Container +container_port: 8000 +container_hostname: "{{ domains | get_domain(application_id) }}" + +# BookWyrm + +BOOKWYRM_REGISTRATION_OPEN: "{{ applications | get_app_conf(application_id, 'registration_open') | string | lower }}" +BOOKWYRM_ALLOW_INVITE_REQUESTS: "{{ applications | get_app_conf(application_id, 'allow_invite_request') | string | lower }}" + +## Credentrials +BOOKWYRM_SECRET_KEY: "{{ applications | get_app_conf(application_id, 'credentials.secret_key') }}" + +## URLs +BOOKWYRM_URL: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" +BOOKWYRM_HOSTNAME: "{{ container_hostname }}" + +## OIDC (optional; can be fronted by oauth2-proxy or native if you wire it) +BOOKWYRM_OIDC_ENABLED: "{{ applications | get_app_conf(application_id, 'features.oidc') }}" +BOOKWYRM_OIDC_LABEL: "{{ OIDC.BUTTON_TEXT }}" +BOOKWYRM_OIDC_CLIENT_ID: "{{ OIDC.CLIENT.ID }}" +BOOKWYRM_OIDC_CLIENT_SECRET: "{{ OIDC.CLIENT.SECRET }}" +BOOKWYRM_OIDC_ISSUER: "{{ OIDC.CLIENT.ISSUER_URL }}" +BOOKWYRM_OIDC_AUTH_URL: "{{ OIDC.CLIENT.AUTHORIZE_URL }}" +BOOKWYRM_OIDC_TOKEN_URL: "{{ OIDC.CLIENT.TOKEN_URL }}" +BOOKWYRM_OIDC_USERINFO_URL: "{{ OIDC.CLIENT.USER_INFO_URL }}" +BOOKWYRM_OIDC_LOGOUT_URL: "{{ OIDC.CLIENT.LOGOUT_URL }}" +BOOKWYRM_OIDC_JWKS_URL: "{{ OIDC.CLIENT.CERTS }}" +BOOKWYRM_OIDC_SCOPES: "openid,email,profile" +BOOKWYRM_OIDC_UNIQUE_ATTRIBUTE: "{{ OIDC.ATTRIBUTES.USERNAME }}" + +## Docker +BOOKWYRM_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.application.version') }}" +BOOKWYRM_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.application.image') | default('bookwyrm/bookwyrm') }}" +BOOKWYRM_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.application.name') | default('bookwyrm') }}" +BOOKWYRM_DATA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.data') | default('bookwyrm_data') }}" +BOOKWYRM_MEDIA_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.media') | default('bookwyrm_media') }}" +BOOKWYRM_CUSTOM_IMAGE: "{{ BOOKWYRM_IMAGE }}_custom" + +## Redis +BOOKWYRM_REDIS_HOST: "redis" +BOOKWYRM_REDIS_PORT: 6379