From 7b41ed51481ceaad6c9004b6a8c3c2e4ae475405 Mon Sep 17 00:00:00 2001 From: Paul Payne Date: Fri, 19 Jun 2026 21:32:56 +0000 Subject: [PATCH] Adds bookwyrm app. --- bookwyrm/app.yaml | 5 + bookwyrm/versions/0/README.md | 35 +++++ bookwyrm/versions/0/db-init-job.yaml | 83 ++++++++++++ bookwyrm/versions/0/deployment-worker.yaml | 120 +++++++++++++++++ bookwyrm/versions/0/deployment.yaml | 149 +++++++++++++++++++++ bookwyrm/versions/0/ingress.yaml | 26 ++++ bookwyrm/versions/0/kustomization.yaml | 17 +++ bookwyrm/versions/0/manifest.yaml | 30 +++++ bookwyrm/versions/0/namespace.yaml | 4 + bookwyrm/versions/0/pvc.yaml | 11 ++ bookwyrm/versions/0/service.yaml | 13 ++ 11 files changed, 493 insertions(+) create mode 100644 bookwyrm/app.yaml create mode 100644 bookwyrm/versions/0/README.md create mode 100644 bookwyrm/versions/0/db-init-job.yaml create mode 100644 bookwyrm/versions/0/deployment-worker.yaml create mode 100644 bookwyrm/versions/0/deployment.yaml create mode 100644 bookwyrm/versions/0/ingress.yaml create mode 100644 bookwyrm/versions/0/kustomization.yaml create mode 100644 bookwyrm/versions/0/manifest.yaml create mode 100644 bookwyrm/versions/0/namespace.yaml create mode 100644 bookwyrm/versions/0/pvc.yaml create mode 100644 bookwyrm/versions/0/service.yaml diff --git a/bookwyrm/app.yaml b/bookwyrm/app.yaml new file mode 100644 index 0000000..9144ca3 --- /dev/null +++ b/bookwyrm/app.yaml @@ -0,0 +1,5 @@ +name: bookwyrm +is: bookwyrm +description: BookWyrm is a federated social reading platform using ActivityPub. Track your reading, write reviews, and connect with readers across the fediverse. +icon: https://raw.githubusercontent.com/bookwyrm-social/bookwyrm/main/bookwyrm/static/images/logo.png +latest: "0" diff --git a/bookwyrm/versions/0/README.md b/bookwyrm/versions/0/README.md new file mode 100644 index 0000000..2e281e2 --- /dev/null +++ b/bookwyrm/versions/0/README.md @@ -0,0 +1,35 @@ +# BookWyrm + +BookWyrm is a federated social reading platform — a self-hosted alternative to Goodreads, connected to the ActivityPub network. + +## Dependencies + +- **PostgreSQL** - Database for storing books, reviews, and user data +- **Redis** - Used for caching and background job queuing +- **SMTP** - For account verification and notifications + +## Configuration + +Key settings in `config.yaml`: + +- **domain** - Where BookWyrm will be accessible +- **storage** - Persistent volume size (default: `2Gi`) +- **smtp** - Email settings inherited from your Wild Cloud SMTP service + +## First-Time Setup + +1. Add and deploy the app: + ```bash + wild app add bookwyrm + wild app deploy bookwyrm + ``` + +2. Visit the app URL — on first load, a setup wizard will guide you through creating the admin account and configuring the instance. + +3. After setup, log in and configure the instance settings (name, description, registration policy) from the admin panel at `/settings/`. + +## Notes + +- BookWyrm federates with other BookWyrm instances and Mastodon via ActivityPub — users on other instances can follow and interact with your users +- Book metadata is imported from OpenLibrary and Inventaire — internet access is required for book search to work +- The `secretKey` is auto-generated in `secrets.yaml` diff --git a/bookwyrm/versions/0/db-init-job.yaml b/bookwyrm/versions/0/db-init-job.yaml new file mode 100644 index 0000000..236d03e --- /dev/null +++ b/bookwyrm/versions/0/db-init-job.yaml @@ -0,0 +1,83 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: bookwyrm-db-init + namespace: bookwyrm +spec: + template: + metadata: + labels: + component: db-init + spec: + securityContext: + runAsNonRoot: true + runAsUser: 999 + runAsGroup: 999 + seccompProfile: + type: RuntimeDefault + restartPolicy: OnFailure + containers: + - name: db-init + image: postgres:16-alpine + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + env: + - name: PGHOST + value: "{{ .db.host }}" + - name: PGPORT + value: "{{ .db.port }}" + - name: PGUSER + value: postgres + - name: PGPASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: postgres.password + - name: BOOKWYRM_DB_USER + value: "{{ .db.user }}" + - name: BOOKWYRM_DB_NAME + value: "{{ .db.name }}" + - name: BOOKWYRM_DB_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: dbPassword + command: + - /bin/sh + - -c + - | + echo "Initializing BookWyrm database..." + + # Create user if it doesn't exist + psql -c " + DO \$\$ + BEGIN + IF NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '$BOOKWYRM_DB_USER') THEN + CREATE USER \"$BOOKWYRM_DB_USER\" WITH PASSWORD '$BOOKWYRM_DB_PASSWORD'; + ELSE + ALTER USER \"$BOOKWYRM_DB_USER\" WITH PASSWORD '$BOOKWYRM_DB_PASSWORD'; + END IF; + END + \$\$; + " + + # Create database if it doesn't exist + if ! psql -lqt | cut -d \| -f 1 | grep -qw "$BOOKWYRM_DB_NAME"; then + echo "Creating database $BOOKWYRM_DB_NAME..." + createdb -O "$BOOKWYRM_DB_USER" "$BOOKWYRM_DB_NAME" + else + echo "Database $BOOKWYRM_DB_NAME already exists." + fi + + # Grant privileges + psql -d "$BOOKWYRM_DB_NAME" -c " + GRANT ALL PRIVILEGES ON DATABASE \"$BOOKWYRM_DB_NAME\" TO \"$BOOKWYRM_DB_USER\"; + GRANT ALL ON SCHEMA public TO \"$BOOKWYRM_DB_USER\"; + GRANT USAGE ON SCHEMA public TO \"$BOOKWYRM_DB_USER\"; + " + + echo "Database initialization completed." diff --git a/bookwyrm/versions/0/deployment-worker.yaml b/bookwyrm/versions/0/deployment-worker.yaml new file mode 100644 index 0000000..885e2fb --- /dev/null +++ b/bookwyrm/versions/0/deployment-worker.yaml @@ -0,0 +1,120 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bookwyrm-worker + namespace: bookwyrm +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + component: worker + template: + metadata: + labels: + component: worker + spec: + securityContext: + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + containers: + - name: celery-worker + image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.6 + command: + - celery + - -A + - celerywyrm + - worker + - --pool=threads + - --concurrency=10 + - -l + - info + - -Q + - high_priority,medium_priority,low_priority,streams,images,suggested_users,email,connectors,lists,inbox,imports,import_triggered,broadcast,misc + env: + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: secretKey + - name: DEBUG + value: "false" + - name: DOMAIN + value: {{ .domain }} + - name: ALLOWED_HOSTS + value: {{ .domain }} + - name: EMAIL + value: {{ .smtp.from }} + - name: POSTGRES_HOST + value: {{ .db.host }} + - name: PGPORT + value: "{{ .db.port }}" + - name: POSTGRES_DB + value: {{ .db.name }} + - name: POSTGRES_USER + value: {{ .db.user }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: dbPassword + - name: REDIS_ACTIVITY_HOST + value: {{ .redis.host }} + - name: REDIS_ACTIVITY_PORT + value: "6379" + - name: REDIS_ACTIVITY_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: redis.password + - name: REDIS_ACTIVITY_URL + value: redis://:$(REDIS_ACTIVITY_PASSWORD)@{{ .redis.host }}:6379/0 + - name: REDIS_BROKER_HOST + value: {{ .redis.host }} + - name: REDIS_BROKER_PORT + value: "6379" + - name: REDIS_BROKER_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: redis.password + - name: REDIS_BROKER_URL + value: redis://:$(REDIS_BROKER_PASSWORD)@{{ .redis.host }}:6379/1 + - name: EMAIL_HOST + value: {{ .smtp.host }} + - name: EMAIL_PORT + value: "{{ .smtp.port }}" + - name: EMAIL_HOST_USER + value: {{ .smtp.user }} + - name: EMAIL_HOST_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: smtpPassword + - name: EMAIL_USE_TLS + value: "true" + - name: EMAIL_USE_SSL + value: "false" + - name: MEDIA_ROOT + value: /app/images + - name: STATIC_ROOT + value: /app/static + resources: + limits: + cpu: 1000m + ephemeral-storage: 1Gi + memory: 1Gi + requests: + cpu: 50m + ephemeral-storage: 50Mi + memory: 256Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + restartPolicy: Always diff --git a/bookwyrm/versions/0/deployment.yaml b/bookwyrm/versions/0/deployment.yaml new file mode 100644 index 0000000..480af40 --- /dev/null +++ b/bookwyrm/versions/0/deployment.yaml @@ -0,0 +1,149 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: bookwyrm + namespace: bookwyrm +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: + component: web + template: + metadata: + labels: + component: web + spec: + securityContext: + runAsNonRoot: false + runAsUser: 0 + seccompProfile: + type: RuntimeDefault + containers: + - name: bookwyrm + image: ghcr.io/bookwyrm-social/bookwyrm:v0.8.6 + command: ["gunicorn", "bookwyrm.wsgi:application"] + ports: + - name: http + containerPort: 8000 + protocol: TCP + env: + - name: SECRET_KEY + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: secretKey + - name: DEBUG + value: "false" + - name: DOMAIN + value: {{ .domain }} + - name: ALLOWED_HOSTS + value: {{ .domain }} + - name: EMAIL + value: {{ .smtp.from }} + - name: POSTGRES_HOST + value: {{ .db.host }} + - name: PGPORT + value: "{{ .db.port }}" + - name: POSTGRES_DB + value: {{ .db.name }} + - name: POSTGRES_USER + value: {{ .db.user }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: dbPassword + - name: REDIS_ACTIVITY_HOST + value: {{ .redis.host }} + - name: REDIS_ACTIVITY_PORT + value: "6379" + - name: REDIS_ACTIVITY_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: redis.password + - name: REDIS_ACTIVITY_URL + value: redis://:$(REDIS_ACTIVITY_PASSWORD)@{{ .redis.host }}:6379/0 + - name: REDIS_BROKER_HOST + value: {{ .redis.host }} + - name: REDIS_BROKER_PORT + value: "6379" + - name: REDIS_BROKER_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: redis.password + - name: REDIS_BROKER_URL + value: redis://:$(REDIS_BROKER_PASSWORD)@{{ .redis.host }}:6379/1 + - name: EMAIL_HOST + value: {{ .smtp.host }} + - name: EMAIL_PORT + value: "{{ .smtp.port }}" + - name: EMAIL_HOST_USER + value: {{ .smtp.user }} + - name: EMAIL_HOST_PASSWORD + valueFrom: + secretKeyRef: + name: bookwyrm-secrets + key: smtpPassword + - name: EMAIL_USE_TLS + value: "true" + - name: EMAIL_USE_SSL + value: "false" + - name: EMAIL_SENDER_NAME + value: BookWyrm + - name: EMAIL_SENDER_DOMAIN + value: {{ .domain }} + - name: MEDIA_ROOT + value: /app/images + - name: STATIC_ROOT + value: /app/static + - name: ENABLE_THUMBNAIL_GENERATION + value: "true" + resources: + limits: + cpu: 1000m + ephemeral-storage: 2Gi + memory: 1Gi + requests: + cpu: 50m + ephemeral-storage: 50Mi + memory: 256Mi + volumeMounts: + - name: bookwyrm-images + mountPath: /app/images + - name: bookwyrm-static + mountPath: /app/static + - name: bookwyrm-exports + mountPath: /app/exports + livenessProbe: + tcpSocket: + port: 8000 + initialDelaySeconds: 120 + timeoutSeconds: 10 + periodSeconds: 30 + failureThreshold: 6 + readinessProbe: + tcpSocket: + port: 8000 + initialDelaySeconds: 60 + timeoutSeconds: 5 + periodSeconds: 15 + failureThreshold: 3 + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + volumes: + - name: bookwyrm-images + persistentVolumeClaim: + claimName: bookwyrm-images + - name: bookwyrm-static + emptyDir: {} + - name: bookwyrm-exports + emptyDir: {} + restartPolicy: Always diff --git a/bookwyrm/versions/0/ingress.yaml b/bookwyrm/versions/0/ingress.yaml new file mode 100644 index 0000000..b40b793 --- /dev/null +++ b/bookwyrm/versions/0/ingress.yaml @@ -0,0 +1,26 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: bookwyrm + namespace: bookwyrm + annotations: + external-dns.alpha.kubernetes.io/target: {{ .externalDnsDomain }} + external-dns.alpha.kubernetes.io/cloudflare-proxied: "false" + external-dns.alpha.kubernetes.io/ttl: "60" +spec: + ingressClassName: traefik + rules: + - host: {{ .domain }} + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: bookwyrm + port: + number: 80 + tls: + - hosts: + - {{ .domain }} + secretName: {{ .tlsSecretName }} diff --git a/bookwyrm/versions/0/kustomization.yaml b/bookwyrm/versions/0/kustomization.yaml new file mode 100644 index 0000000..8a7fe0c --- /dev/null +++ b/bookwyrm/versions/0/kustomization.yaml @@ -0,0 +1,17 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +namespace: {{ .namespace }} +labels: + - includeSelectors: true + pairs: + app: bookwyrm + managedBy: kustomize + partOf: wild-cloud +resources: + - namespace.yaml + - deployment.yaml + - deployment-worker.yaml + - service.yaml + - ingress.yaml + - pvc.yaml + - db-init-job.yaml diff --git a/bookwyrm/versions/0/manifest.yaml b/bookwyrm/versions/0/manifest.yaml new file mode 100644 index 0000000..c516c10 --- /dev/null +++ b/bookwyrm/versions/0/manifest.yaml @@ -0,0 +1,30 @@ +version: 0.8.7-1 +requires: + - name: postgres + - name: redis + - name: smtp +defaultConfig: + namespace: bookwyrm + externalDnsDomain: '{{ .cloud.domain }}' + domain: bookwyrm.{{ .cloud.domain }} + tlsSecretName: wildcard-wild-cloud-tls + storage: 2Gi + db: + host: '{{ .apps.postgres.host }}' + port: '{{ .apps.postgres.port }}' + name: bookwyrm + user: bookwyrm + redis: + host: '{{ .apps.redis.host }}' + smtp: + host: '{{ .apps.smtp.host }}' + port: '{{ .apps.smtp.port }}' + from: '{{ .apps.smtp.from }}' + user: '{{ .apps.smtp.user }}' +defaultSecrets: + - key: secretKey + - key: dbPassword + - key: smtpPassword +requiredSecrets: + - postgres.password + - redis.password diff --git a/bookwyrm/versions/0/namespace.yaml b/bookwyrm/versions/0/namespace.yaml new file mode 100644 index 0000000..054927e --- /dev/null +++ b/bookwyrm/versions/0/namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: {{ .namespace }} diff --git a/bookwyrm/versions/0/pvc.yaml b/bookwyrm/versions/0/pvc.yaml new file mode 100644 index 0000000..aa29d3a --- /dev/null +++ b/bookwyrm/versions/0/pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: bookwyrm-images + namespace: bookwyrm +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .storage }} diff --git a/bookwyrm/versions/0/service.yaml b/bookwyrm/versions/0/service.yaml new file mode 100644 index 0000000..f5e2c0a --- /dev/null +++ b/bookwyrm/versions/0/service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: bookwyrm + namespace: bookwyrm +spec: + selector: + component: web + ports: + - name: http + port: 80 + targetPort: 8000 + protocol: TCP