From 7c4a8aa7c9028a370bd9a3b88a933d1c42dace24 Mon Sep 17 00:00:00 2001 From: Siebren Bakker Date: Thu, 18 Jun 2026 12:57:52 -0400 Subject: [PATCH 001/107] feat: add narrator logout and move login to top-level /login/ URL Logout link in header via context processor, clears session and redirects home. Also adds /login// for direct passphrase login via URL. --- config/settings.py | 1 + registration/context_processors.py | 10 ++++++++++ registration/urls.py | 1 + registration/views.py | 5 +++++ templates/base.html | 18 +++++++++++++++--- 5 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 registration/context_processors.py diff --git a/config/settings.py b/config/settings.py index 72b7010..be8881a 100644 --- a/config/settings.py +++ b/config/settings.py @@ -47,6 +47,7 @@ "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", + "registration.context_processors.narrator", ], }, }, diff --git a/registration/context_processors.py b/registration/context_processors.py new file mode 100644 index 0000000..6e872c3 --- /dev/null +++ b/registration/context_processors.py @@ -0,0 +1,10 @@ +from books.models import Narrator + + +def narrator(request): + narrator_id = request.session.get("narrator_id") + if narrator_id: + narrator = Narrator.objects.filter(id=narrator_id).first() + if narrator: + return {"narrator_name": narrator.name} + return {} diff --git a/registration/urls.py b/registration/urls.py index 378793a..b97625d 100644 --- a/registration/urls.py +++ b/registration/urls.py @@ -6,6 +6,7 @@ urlpatterns = [ path("event/", views.register_event, name="event"), path("invite//", views.register_invite, name="invite"), + path("logout/", views.logout, name="logout"), path("welcome/", views.welcome, name="welcome"), ] diff --git a/registration/views.py b/registration/views.py index 6ea160c..b4cfcbf 100644 --- a/registration/views.py +++ b/registration/views.py @@ -76,6 +76,11 @@ def login_with_passphrase(request, passphrase): return redirect("registration:welcome") +def logout(request): + request.session.pop("narrator_id", None) + return redirect("home") + + def welcome(request): narrator_id = request.session.get("narrator_id") if not narrator_id: diff --git a/templates/base.html b/templates/base.html index 733b41f..20968c0 100644 --- a/templates/base.html +++ b/templates/base.html @@ -55,6 +55,13 @@ display: flex; align-items: center; justify-content: space-between; + gap: 1rem; + } + + .nav-links { + display: flex; + align-items: center; + gap: 1rem; } header a { @@ -109,9 +116,14 @@

Fragforce Reads

- - - + From 68093f2adedafb688b279a904aca31e756510e7c Mon Sep 17 00:00:00 2001 From: Siebren Bakker Date: Thu, 18 Jun 2026 12:58:01 -0400 Subject: [PATCH 002/107] ci: add GitHub Actions workflows for dev and prod Docker image builds Dev image builds on push to dev, prod image builds on push to main or version tags. Images pushed to GHCR as fragforce-read-dev and fragforce-read-prod. --- .github/workflows/dev-image.yaml | 52 +++++++++++++++++++++++++++++ .github/workflows/prod-image.yaml | 54 +++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 .github/workflows/dev-image.yaml create mode 100644 .github/workflows/prod-image.yaml diff --git a/.github/workflows/dev-image.yaml b/.github/workflows/dev-image.yaml new file mode 100644 index 0000000..a4a4d1b --- /dev/null +++ b/.github/workflows/dev-image.yaml @@ -0,0 +1,52 @@ +name: Dev Image + +on: + push: + branches: + - dev + +jobs: + build: + name: Build & push docker image + if: github.repository == 'fragforce/read' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + env: + IMG_NAME: fragforce-read-dev + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 + with: + buildkitd-flags: --debug + + - name: Docker metadata + id: metadata + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 + with: + images: ghcr.io/${{ github.repository_owner }}/${{ env.IMG_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=ref,event=branch + type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7 + with: + context: . + file: Dockerfile + push: true + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} diff --git a/.github/workflows/prod-image.yaml b/.github/workflows/prod-image.yaml new file mode 100644 index 0000000..456bf8c --- /dev/null +++ b/.github/workflows/prod-image.yaml @@ -0,0 +1,54 @@ +name: Prod Image + +on: + push: + tags: + - v* + branches: + - main + +jobs: + build: + name: Build & push docker image + if: github.repository == 'fragforce/read' + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + env: + IMG_NAME: fragforce-read-prod + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4 + with: + buildkitd-flags: --debug + + - name: Docker metadata + id: metadata + uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6 + with: + images: ghcr.io/${{ github.repository_owner }}/${{ env.IMG_NAME }} + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=ref,event=branch + type=raw,value={{sha}},enable=${{ github.ref_type != 'tag' }} + + - name: Login to GitHub Container Registry + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7 + with: + context: . + file: Dockerfile + push: true + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} From 77042fa4b5c9836daedda28f6a8596d03ae76ea3 Mon Sep 17 00:00:00 2001 From: Siebren Bakker Date: Thu, 18 Jun 2026 13:42:00 -0400 Subject: [PATCH 003/107] docs: document signed URL approach and unified storage for audio delivery Replaces HLS/MSE with session-gated Django views for licensed book audio protection. All recordings stored in the same location regardless of licensing; access control handled at the view layer. Simpler than streaming protocols while still preventing casual download and link sharing. --- REQUIREMENTS.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md index 93b323c..962e273 100644 --- a/REQUIREMENTS.md +++ b/REQUIREMENTS.md @@ -37,8 +37,8 @@ These additional requirements apply only to books where publisher permission has | Requirement | Details | |-------------|---------| | Password gate | Unique password per QR code, server-side validation, short alphanumeric (4-6 chars, no ambiguous characters) | -| Stream-only delivery | HLS or Media Source Extensions - no direct `