Skip to content

US-006: Protected Exercise Start, Audio, And Original Text #46

Description

@Mateus-Mannes

Summary

Keep exercise metadata public and cacheable, but move paid content behind dynamic entitlement checks. Public catalog/detail responses will not expose text or audioFileId. Users start an exercise through a protected begin endpoint that checks Free/Pro access and returns a short-lived audio URL only when allowed. Submissions send propositionId + userText; backend loads the original text from the database.

Key Changes

  • Public metadata API:
    • Change public GET /proposition/{id} to return a safe DTO only: id, title, topic, level, image, duration, source/date, and access metadata.
    • Do not return Text or AudioFileId from public/cached endpoints.
    • Keep public metadata endpoints Cloudflare-cacheable.
  • Protected begin endpoint:
    • Add POST /proposition/{id}/begin with Cache-Control: no-store.
    • Check access using the US-005 rule: latest 18 global exercises are Free; all older exercises require Pro.
    • Treat anonymous or failed session lookup as Free.
    • If access is granted, return { access: "granted", audioUrl, audioExpiresAtUtc, metadata }.
    • If access is blocked, return { access: "pro_required", metadata } without text, audioFileId, or audio URL.
  • Entitlement lookup:
    • Propositions-service forwards incoming cookies to users-service /users/auth/session.
    • isPro=true grants Pro access.
    • Frontend generated API config must send credentials for dynamic begin/submit calls.
  • Audio protection:
    • Make the MinIO propositions bucket private; keep images public.
    • Add a file service method to create presigned GET URLs for audio objects.
    • Audio presigned URLs expire after 1 hour.
    • Purge/avoid Cloudflare caching for old public audio URLs after changing bucket policy.
  • Frontend exercise flow:
    • Exercise page loads public metadata first.
    • Clicking Begin calls /proposition/{id}/begin.
    • Granted response stores audioUrl, transitions to exercise state, and passes audioUrl to the audio component.
    • Pro-required response keeps the user on the intro/paywall state and opens/shows the Pro upgrade modal.
    • Audio component stops deriving URLs from environment.minioUrl + /propositions/{audioFileId}.
  • Text comparison:
    • Replace client-provided originalText with server-owned original text.
    • Frontend sends { propositionId, userText }.
    • Backend validates proposition ID, checks the same begin entitlement, loads Proposition.Text from DB, then runs comparison.
    • Response may still include original text in the correction result only after access is granted.
  • Free-user standard correction:
    • Free and anonymous users receive the existing deterministic/static comparison for exercises they are allowed to use.
    • Free users can submit only latest-18 free exercises.
    • Free submissions must not call any AI service.
    • Static comparison response shape must remain compatible with the current results UI.

Test Plan

  • Backend tests:
    • Public proposition response excludes original text and audioFileId.
    • Free/anonymous users can begin latest 18 global exercises.
    • Free/anonymous users get pro_required for older exercises.
    • Pro users can begin older exercises.
    • Granted begin response includes presigned audio URL and expiry.
    • Blocked begin response excludes text, audioFileId, and audio URL.
    • Submit endpoint ignores/rejects client original text and loads original text from DB.
    • Submit endpoint rejects Pro-only exercise submissions from non-Pro users.
    • Free/anonymous allowed submissions run static comparison only and do not call AI.
    • Static comparison response remains compatible with the current frontend result components.
  • Frontend tests:
    • Begin button calls protected begin endpoint.
    • Granted begin response transitions from intro to exercise state.
    • Pro-required begin response shows Pro upgrade UI and does not render audio/writing controls.
    • Audio component uses supplied audioUrl.
    • Submit sends proposition ID and user text only.
    • Free-user static comparison results still render in the existing results UI.

Assumptions

  • US-001 entitlement and US-005 free-window metadata exist or are implemented first.
  • Audio presigned URLs use MinIO and expire after 1 hour.
  • Public metadata remains safe to cache on Cloudflare.
  • Direct audio protection is achieved by making the propositions bucket private and only issuing presigned URLs after entitlement succeeds.
  • US-009 is intentionally folded into this story; standard/static correction for Free users is part of the protected submit flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions