fragJulia
Operations

Comprehensive Issue Resolution Plan

88-issue, 7-sprint resolution plan covering security, design, accessibility, bugs, and config — organized by dependency order with file-level cross-references.

Update 2026-04-25: This plan was authored before several structural moves on apps/web (notably the proxy.tsmiddleware.ts rename and Pillar A help-content redirects to docs.fragjulia.de). File paths below may not match current state — this is a spot-check, not a full audit. Treat unannotated references as "state unverified since plan was written".

88 open issues across security, design, accessibility, bugs, and config. This plan organizes them into 7 sprints with clear dependencies and priority order.


Architecture Overview

  • Monorepo: apps/web (Next.js 16 + App Router), apps/mobile (Expo), voice/ (LiveKit agent), packages/shared
  • Stack: React 19, Supabase (auth + DB), Stripe (payments), Resend (email), Upstash Redis (rate limiting), LiveKit (voice), Tailwind v4
  • Design system: V2 components in components/v2/ (Sage-Creme-Terracotta), legacy in components/ (Pine-Gold)
  • Key files: proxy.ts (middleware), lib/ (rate-limit, stripe, hmac, sanitize, supabase, resend)

Update 2026-04-25: apps/web/proxy.ts no longer exists on main; the active middleware file is apps/web/middleware.ts, which imports updateSession from @/lib/supabase/middleware. This was the launch-audit-2026-04-04 finding #1 (CRITICAL), now resolved.


Sprint 0 — Foundation & Config (Days 1–2)

Unblock everything else. Fix build, config, and env var chaos first.

#IssueFile(s)Effort
#60Duplicate next.config files (.ts and .mjs)next.config.ts, next.config.mjsS
#96next.config.ts is empty — add security headers, image domains, redirectsnext.config.tsM
#97Package name "nextjs" → "fragjulia-web"package.jsonS
#59Missing .env.exampleCreate .env.exampleM
#62Vercel deployment returns 403 on root URLvercel.json, next.config.tsM
#112Multiple CSS token systems coexistlib/fj-tokens.ts, globals.cssL

Action plan:

  1. Delete next.config.mjs (empty/stale), consolidate into next.config.ts with security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy), image domains, and redirects
  2. Rename package to fragjulia-web in package.json
  3. Audit all process.env.* usage across lib/ and app/api/ → create comprehensive .env.example
  4. Fix Vercel 403 by verifying vercel.json output directory and root route
  5. Audit CSS token files — consolidate fj-tokens.ts aliases, remove legacy Pine-Gold tokens, map everything to Tailwind v4 theme

Update 2026-04-25: The duplicate-config decision went the other way — apps/web/next.config.ts no longer exists, apps/web/next.config.mjs is the active config (with security headers, image domains, redirects, and Pillar A docs.fragjulia.de redirects). Sprint 0 action plan step 1 is inverted relative to current state.


Sprint 1 — Critical Security (Days 3–5)

These are exploitable vulnerabilities. Fix before any public traffic.

#IssueFile(s)Effort
#26Host Header Injection in Auth Callbackapp/auth/callback/route.tsM
#27Voice Token API has no authenticationapp/api/voice/token/route.tsM
#28CRON_SECRET exposed in URL query parameterCron route handlersM
#29CSP allows unsafe-inline/unsafe-evalnext.config.ts (after Sprint 0 fix)M
#30Rate limiter silently allows all requests when Redis failslib/rate-limit.tsS
#31Kliniken API string interpolation in Supabase filtersapp/api/kliniken/route.tsS
#54XSS risk in FAQ JSON-LD dangerouslySetInnerHTMLlib/json-ld.ts, FAQ pageS

Action plan:

  1. #26: Replace request.headers.get('host') with hardcoded NEXT_PUBLIC_SITE_URL or request.nextUrl.origin for redirect URI construction
  2. #27: Add Supabase auth check (createClientgetUser()) at top of voice token route; return 401 if unauthenticated
  3. #28: Move CRON_SECRET from query param to Authorization: Bearer <secret> header; update Vercel cron config
  4. #29: Add strict CSP in next.config.ts headers — remove unsafe-inline (use nonces), remove unsafe-eval
  5. #30: Change lib/rate-limit.ts Redis failure mode from "allow all" to "deny" (fail-closed); add logging
  6. #31: Use Supabase .eq() / .ilike() parameterized methods instead of string interpolation
  7. #54: Sanitize JSON-LD output with proper JSON escaping instead of dangerouslySetInnerHTML

Sprint 2 — Critical UX & Broken Routes (Days 5–7)

These break core user flows. Users literally can't navigate.

#IssueFile(s)Effort
#65/lexikon route missing — 4+ broken linksMissing app/lexikon/L
#66/breastfriend/chat route missingMissing app/breastfriend/chat/M
#67Chat requires login but should allow anonymousapp/chat/page.tsx, auth middlewareL
#68Header "Wissen" points to missing /lexikoncomponents/v2/header.tsxS
#6912+ pages use old AppHeader/AppFooter instead of V2Multiple page filesL
#70Login: duplicate "Login"/"Anmelden" buttonsapp/login/page.tsxS
#75Broken umlauts in community/live and rezepteMultiple pagesM
#48Missing umlauts (Uberschrift, hinzufugen, Zuruck)Multiple pagesM

Action plan:

  1. #65: Create app/lexikon/page.tsx — glossary/encyclopedia page for medical terms (placeholder with proper V2 layout, or full implementation if content data exists)
  2. #66: Create app/breastfriend/chat/page.tsx — chat endpoint for breastfriend matching flow
  3. #67: Modify chat page to allow anonymous access per DesignGUIDE "anonym" promise. Move auth gate to premium features only, not base chat
  4. #68: Update V2 header nav to point "Wissen" to /lexikon (or temporary redirect to /faq until lexikon is built)
  5. #69: Systematic migration — replace <AppHeader/> / <AppFooter/> with <V2Header/> / <V2Footer/> in these pages: /faq, /login, /registrieren, /datenschutz, /impressum, /nutzungsbedingungen, /profil, /community, /dokumente, /kliniken, /forschung, /magazin
  6. #70: Remove duplicate button — keep single "Anmelden" (German) CTA
  7. #75 + #48: Global find-and-replace for broken umlauts: UberschriftÜberschrift, hinzufugenhinzufügen, ZuruckZurück, LoschenLöschen, OffnenÖffnen, etc. across all .tsx files

Sprint 3 — DesignGUIDE Compliance (Days 8–12)

Align all pages with the V3 DesignGUIDE spec.

3a. Color & Layout (High Priority)

#IssueFile(s)Effort
#73CTA buttons use sage → terracottacomponents/v2/cta.tsx, button classesM
#42Buttons should use terracotta for primary CTAsSame as above
#74Chat page uses pine/gold design systemapp/chat/page.tsxL
#83Login page uses #FFFFFF — no pure white allowedapp/login/page.tsxS
#43Max-width 1200px → 1080pxGlobal container classS
#89chat_nacht/profil_nacht use hardcoded dark colorsapp/chat_nacht/, app/profil_nacht/M

3b. Component Specs

#IssueFile(s)Effort
#35Trust-Strip text deviates from speccomponents/v2/trust-strip.tsxS
#36Alltag icons 56px → 48pxcomponents/v2/alltag.tsxS
#37Features icons 36px → 32pxcomponents/v2/features.tsxS
#38FAQ max-width 800px → 760pxcomponents/v2/faq-home.tsxS
#39Quote attribution extra textcomponents/v2/quote.tsxS
#40Header has extra "Preise" linkcomponents/v2/header.tsxS
#41 / #99Footer 4-column → 3-columncomponents/v2/footer.tsxM
#76Evidence strip duplicate border classescomponents/v2/evidence-strip.tsxS
#77Footer text → "Daten in der EU · DSGVO-konform"components/v2/footer.tsxS
#78Alltag gradient values deviatecomponents/v2/alltag.tsxS
#79Quote missing decorative mark + gradientcomponents/v2/quote.tsxM
#80CTA missing cream bg + terracotta gradientcomponents/v2/cta.tsxM
#95Hero chat preview icon (clock → message)components/v2/hero.tsxS
#98Feature cards hover bar terracotta CSScomponents/v2/features.tsxS
#100Hero min-h-[92vh] verificationcomponents/v2/hero.tsxS
#101Missing section divider linesHomepage layoutM
#102Mobile: Hero chat preview above textcomponents/v2/hero.tsxS
#105Community cards — remove icon containersapp/community/page.tsxS

3c. Page-Level Redesigns

#IssueFile(s)Effort
#71FAQ page old design systemapp/faq/page.tsxL
#72Mobile menu "Menu" → "Menü"components/v2/header.tsxS
#81/wer page editorial layoutapp/wer/page.tsxL
#82Registration undefined CSS var --fj-textapp/registrieren/page.tsxS
#84Community empty state when no categoryapp/community/page.tsxM
#86FAQ accordion smooth transitioncomponents/v2/faq-home.tsx or app/faq/M
#91Willkommen routes both roles to same pageapp/willkommen/page.tsxM
#92Magazin old designapp/magazin/page.tsxL
#93Rezepte emojis → SVG illustrationsapp/rezepte/page.tsxM
#94Community /neu no footer, custom headerapp/community/S
#107Community live pages old designapp/community/M
#108Splash page auto-redirectapp/splash/page.tsxS

Action plan:

  • Work through V2 components first (3b) — these cascade to all pages
  • Then tackle color/layout globals (3a)
  • Then page-level redesigns (3c) last, since they depend on correct components

Sprint 4 — Medium Security + Data Integrity (Days 12–14)

#IssueFile(s)Effort
#32Missing rate limiting on public endpointsapp/api/kliniken/, app/api/studies/, etc.M
#33Stripe price IDs default to empty stringlib/stripe.tsS
#34HMAC secret falls back to JWT secretlib/hmac.tsS
#52Login lacks client-side email validationapp/login/page.tsxS
#53Email regex too permissive in sanitize.tslib/sanitize.tsS
#56Newsletter tokens never expireapp/api/newsletter/unsubscribe/route.tsM
#58Resend API key confusing fallback namelib/resend.tsS
#61Supabase admin non-null assertionlib/supabase/admin.tsS
#63Email send API unsafe type castingapp/api/emails/send/route.tsM
#64Middleware error detection string matchinglib/supabase/middleware.ts or proxy.tsM
#57Dokumente page silently catches errorsapp/dokumente/page.tsxS

Update 2026-04-25: Issue #64's "or proxy.ts" file alternate is stale — apps/web/proxy.ts no longer exists; the live file is apps/web/middleware.ts. The middleware logic itself still lives in lib/supabase/middleware.ts.

Action plan:

  1. Env var validation (#33, #34, #61): Add runtime validation at module load — throw if STRIPE_PRICE_*, HMAC_SECRET, SUPABASE_SERVICE_ROLE_KEY are missing. Pattern: const X = process.env.X ?? (() => { throw new Error('Missing X') })()
  2. Rate limiting (#32): Wrap public API routes (/api/kliniken, /api/studies, /api/newsletter/*) with checkRateLimit() from lib/rate-limit.ts
  3. Email validation (#52, #53): Add proper email regex to login form (client-side), tighten lib/sanitize.ts regex to RFC 5322 subset
  4. Newsletter tokens (#56): Add expires_at column to newsletter tokens table, validate on unsubscribe
  5. Type safety (#63, #64): Replace as casts with Zod validation in email send route; replace string matching in middleware with error code checks
  6. Resend naming (#58): Rename env var RESEND_API_KEY consistently, remove confusing fallback

Sprint 5 — SEO & Accessibility (Days 14–17)

5a. SEO

#IssueFile(s)Effort
#4930+ pages missing metadata exportsAll page.tsx filesL
#51Inconsistent datenschutz linksMultiple pagesS
#104OG image / favicon may not match specapp/layout.tsx, public/M
#110Missing /kontakt and /inbox routesCode referencesS

Action plan:

  • Add export const metadata: Metadata = { title, description, openGraph } to every page.tsx that lacks it
  • Standardize all privacy links to /datenschutz (not /datenschutz-info)
  • Verify OG image and favicon against DesignGUIDE v3

5b. Accessibility (A11Y)

#IssueFile(s)Effort
#44Missing aria-hidden on decorative SVGsAll icon/SVG componentsM
#45Form inputs missing label associationsLogin, registration, chat formsM
#46 / #87Chat missing aria-live regionapp/chat/page.tsxS
#47FAQ buttons missing aria-labelFAQ componentsS
#85Missing prefers-reduced-motionGlobal CSS / animation componentsM
#103Breastfriend wizard progress bar ARIAapp/breastfriend/S
#109Keyboard focus not visibleGlobal CSS focus stylesM
#111Registration no password strength indicatorapp/registrieren/page.tsxM

Action plan:

  • Add aria-hidden="true" to all decorative <svg> icons
  • Add <label htmlFor> associations to all form inputs
  • Add aria-live="polite" to chat message container
  • Add aria-expanded and aria-label to FAQ toggle buttons
  • Add @media (prefers-reduced-motion: reduce) to disable transitions
  • Add visible :focus-visible outline styles globally
  • Add password requirements display on registration form

Sprint 6 — Remaining Polish (Days 17–19)

#IssueFile(s)Effort
#50Kliniken search lacks debounceapp/kliniken/page.tsxS
#55Index-as-key in list rendersMultiple componentsM
#88Cookie banner DSGVO compliancecomponents/cookie-banner.tsxM
#90Forschung toggles non-functionalapp/forschung/page.tsxM
#106Datenschutz interactive privacy dashboardapp/datenschutz/page.tsxL
#23P0 Resolved — verify all fixes still holdMultiple filesM

Action plan:

  • Add 300ms debounce to kliniken search input with useEffect + timeout
  • Replace index keys with stable IDs (data ID fields or generated keys)
  • Audit cookie banner for DSGVO: must have reject-all, granular consent, no pre-checked boxes
  • Wire up forschung toggle save/revoke handlers to Supabase
  • Build interactive datenschutz dashboard per DesignGUIDE
  • Regression check on all P0 fixes from #23

Dependency Graph

Sprint 0 (Config/Build)

Sprint 1 (Security) ←── must complete before any public deployment

Sprint 2 (Broken Routes) ←── unblocks user testing

Sprint 3 (DesignGUIDE) ←── depends on V2 header/footer from Sprint 2 #69

Sprint 4 (Data Integrity) ←── can run parallel with Sprint 3

Sprint 5 (SEO + A11Y) ←── best done after pages are stable

Sprint 6 (Polish) ←── final pass

Issue Cross-Reference by File

Key files touched by multiple issues (merge work to avoid conflicts):

FileIssues
components/v2/header.tsx#40, #68, #72
components/v2/footer.tsx#41, #77, #99
components/v2/hero.tsx#95, #100, #102
components/v2/alltag.tsx#36, #78
components/v2/features.tsx#37, #98
components/v2/quote.tsx#39, #79
components/v2/cta.tsx#42, #73, #80
components/v2/faq-home.tsx#38, #86
next.config.ts#29, #60, #96
lib/rate-limit.ts#30, #32
lib/stripe.ts#33
lib/hmac.ts#34
lib/supabase/admin.ts#61
lib/sanitize.ts#53
app/login/page.tsx#52, #70, #83
app/chat/page.tsx#46, #67, #74, #87
app/community/#84, #94, #105, #107
app/registrieren/page.tsx#82, #111

These issues share the same root cause and should be fixed together:

  1. Env var validation (#33, #34, #61) — add runtime validation for all critical env vars
  2. Email validation (#52, #53) — client + server side
  3. Umlaut encoding (#48, #75) — global find/replace pass
  4. Footer spec (#41, #77, #99) — single footer refactor
  5. Chat accessibility (#46, #87) — single aria-live addition
  6. CTA color (#42, #73) — single terracotta migration

Estimated Effort Summary

SprintIssuesEffortCalendar
0 — Config6~2 daysDays 1–2
1 — Security7~3 daysDays 3–5
2 — Broken Routes8~2 daysDays 5–7
3 — DesignGUIDE35~5 daysDays 8–12
4 — Data Integrity11~2 daysDays 12–14
5 — SEO + A11Y12~3 daysDays 14–17
6 — Polish6~2 daysDays 17–19
Total85 unique (3 duplicates merged)~19 days

Top 5 Immediate Priorities (per user request)

  1. #67 — Chat login wall breaks the core "anonym" promise → Sprint 2
  2. #65 — /lexikon route missing (broken nav for every user) → Sprint 2
  3. #69 — Migrate 12+ pages to V2 header/footer → Sprint 2
  4. #73 — CTA buttons terracotta, not sage → Sprint 3
  5. #75 — Fix broken umlauts → Sprint 2

These 5 should be tackled first within their respective sprints.

Ingested from repo-root PLAN.md on 2026-04-25 (#647).

On this page