Commit Graph
16 Commits
Author SHA1 Message Date
admin 1f177c5e88 perf(landing): inline render-blocking CSS, fix LCP image fetchpriority
Two follow-ups from a fresh PageSpeed Insights run (mobile 87/100/100/100):

1. next.config.ts: enable experimental.inlineCss. Removes a ~342ms
   render-blocking CSS request (globals.css, ~21.6 KiB gzip -- shared by
   every route since Tailwind bundles the whole app's utilities into one
   file). Officially documented, dependency-free, and explicitly
   recommended for small atomic-CSS bundles like ours. Applies globally
   (can't be scoped per route), so hard/full page loads lose cross-page
   CSS caching -- soft client-side navigations are unaffected.

2. page.tsx hero image: `priority` was deprecated in Next.js 16 in favor
   of `fetchPriority`/`loading` as separate props -- it still emitted a
   <link rel=preload> but no longer set fetchpriority="high" on the <img>
   itself. Replaced with loading="eager" fetchPriority="high" per the
   bundled Next.js 16 docs.

SpeedUp-Plan, Gitea #28.
2026-08-21 06:43:22 +02:00
admin 2ec5b107ea chore(deploy): quiet down Sentry build logging after root-causing the sourcemap failure
Confirmed via extensive isolated reproduction (docker run, plain BuildKit
RUN step, and Node child_process.spawn -- all three succeed) that TLS and
the sentry-cli invocation itself are fine; GlitchTip's releases API
rejects the authenticated request with "CSRF check Failed" (403) even
with clean token auth and no cookies. That's a server-side GlitchTip
issue (separate Docker stack), not something fixable from this repo.
Keeping ca-certificates/SSL_CERT_FILE since they're correct regardless.
2026-08-14 07:44:31 +02:00
admin 53789ca56e fix(security): close remaining CSP report-only gaps, make Sentry sourcemap upload verbose for debugging
- connect-src: allow *.fastly.mux.com (Mux HLS chunks/manifests via Fastly
  edge, dynamic regional subdomains) and *.litix.io (Mux Data beacon uses
  inferred.litix.io, not covered by the exact litix.io entry)
- media-src: allow image.mux.com (storyboard.vtt thumbnail track)
- next.config.ts: silent:true -> silent:false + debug:true so the next
  Docker build surfaces what the Sentry/GlitchTip sourcemap upload plugin
  actually does (still minified stacktraces in GlitchTip despite the
  build-arg fix in b469389)
2026-08-14 07:11:30 +02:00
admin b4a49f8ed3 fix: address findings from full security/functionality audit (Gitea #17)
Rate-limiting on messages.send, reports.create, reactions.toggle,
follows.create, reposts.create. Unique constraints on Report to stop
report-flooding. Mux webhook now fails hard in production without a
signing secret instead of skipping verification. Admin-panel outline
buttons fixed on 4 more pages (same white-on-dark bug as the Users page).
Timing-safe comparison for ADMIN_SECRET/CRON_SECRET. Fixed duplicate HSTS
header (Cloudflare's stronger one was being shadowed by the app's own).
tRPC now returns clean JSON 401s instead of HTML redirects when
unauthenticated, and no longer blocks public procedures for logged-out
visitors. 14 previously-silent catch blocks now log to GlitchTip.
invite/page.tsx and EmergencyVetSection.tsx fully localized (were 100%
hardcoded German). Stale TODO comments removed, auth.test.ts scaffold
rewritten to match the actual lazy-upsert pattern instead of a Clerk
webhook flow that was never built.
2026-08-13 12:42:49 +02:00
admin 13513583bc feat(security): ship CSP in Report-Only mode via Clerk's middleware option
Replaces the dead hand-rolled buildCsp() draft in next.config.ts (never
wired in — its script-src had no 'unsafe-inline'/nonce, which blocked
Next.js's own hydration script and blanked the whole app when tested
live on 2026-08-10) with Clerk's official contentSecurityPolicy option
on clerkMiddleware(). This is Clerk's documented workaround: its default
directives already include the permissive script-src Next.js App Router
needs, merged with our app-specific img-src/connect-src/media-src/
font-src/frame-ancestors/object-src/base-uri additions (Supabase, Mux,
litix).

Ships as Content-Security-Policy-Report-Only (reportOnly: true) with
both reporting mechanisms wired to a new /api/csp-report route handler:
Clerk's reportTo (Reporting API) and a classic report-uri (for browsers
without Reporting API support). The route normalizes both report shapes,
rate-limits by IP, and forwards violations to Sentry/GlitchTip as
warnings — never blocks the request.

Deliberately NOT enforced yet — move to reportOnly: false only after a
clean observation period with zero unexpected violations.

Gitea #5
2026-08-12 13:42:08 +02:00
admin f559b3d68e feat(observability): add Sentry error monitoring (@sentry/nextjs)
Server, edge, and client instrumentation wired up via SENTRY_DSN /
NEXT_PUBLIC_SENTRY_DSN. Fully inert without a DSN configured (verified
with a clean local build + full test suite) — safe to ship ahead of
actually having a Sentry project. Source-map upload is opt-in via
SENTRY_AUTH_TOKEN (kept out of the Docker build-arg chain since build
args land in image layer history; only the public DSN is a build arg).
2026-08-10 12:57:42 +02:00
admin e6c1bfd28e docs(security): document why CSP is drafted but not yet enforced
Enforcing it blanked the whole app in production (ClerkProvider wraps
the tree in layout.tsx; a blocked resource during Clerk's client init
throws uncaught). Adding wss://clerk.pawfeed.org to connect-src did not
fix it. Next attempt: ship as Content-Security-Policy-Report-Only first
to see real violation reports before enforcing again. All other
security headers (HSTS, X-Frame-Options, etc.) and rate limiting stay
active and are unaffected.
2026-08-10 08:08:19 +02:00
admin 26706ea06a debug: temporarily disable CSP to isolate login-page regression 2026-08-10 08:04:01 +02:00
admin 5c75d716a8 fix(security): allow wss:// to Clerk in CSP connect-src
Clerk's client SDK opens a WebSocket to the frontend API for session
sync; the CSP only allowed https:// to that origin, silently breaking
login (blank page, "Connection closed" exception) once enforced.
2026-08-10 08:02:11 +02:00
admin f43541a4cc feat(security): add Redis-backed rate limiting and CSP/security headers
Public go-live hardening: Redis-backed rate limiting on the previously
invite-gated public surface (explore browsing, post/comment creation,
invite-cookie routes) plus CSP, HSTS, and standard security headers on
every response, now that the invite gate no longer filters traffic.
2026-08-10 07:57:56 +02:00
admin 4b17537a9a feat(i18n): add EN/DE language support with next-intl
- Install next-intl@4 with cookie-based locale switching (NEXT_LOCALE)
- Add LanguageSwitcher component in Sidebar footer
- Translate Nav, PostTypeSheet, Feed, Explore, WelcomeCard, PetForm,
  AvatarUpload, and Onboarding pages (EN + DE)
- Add SpeciesTranslation and BreedTranslation DB models for locale-aware
  taxonomy; listSpecies and listBreeds accept optional locale param
- Seed German translations for all 3 species and 45 breeds
- Add SQL migration script for Supabase (add_i18n_translations.sql)
- fix(invite): secure cookie flag and set-invite route for HTTPS
2026-06-22 14:01:16 +02:00
admin 5800e06cd9 feat: ad engagement, invite gate, sidebar footer, docker setup
Ad interactions (reactions/comments/reposts on ads):
- Schema: AdReaction, AdComment, AdRepost models with RLS enabled
- tRPC: new ads router (getFeedData, toggleReaction, addComment, listComments, toggleRepost)
- AdCard: fully interactive card with paw, comment sheet, repost — mirrors PostCard style
- getActiveAds includes _count for advertiser engagement metrics

Invite system enforcement:
- proxy.ts: /sign-up blocked without pf_invite cookie
- INVITE_REQUIRED=false env var disables gate for post-beta live launch
- Full invite flow: /join → cookie → /sign-up → consume-invite → 3 codes issued to new user
- Admin: listInviteCodes, createRootInvite, revokeInvite procedures

Sidebar mini-footer:
- Impressum + Datenschutz links always visible at sidebar bottom
- PawFeed Alpha-Test copyright line

Docker self-hosting:
- docker/Dockerfile: 3-stage build using Next.js standalone output
- docker/docker-compose.yml: env_file + build-args for NEXT_PUBLIC_ vars
- .dockerignore at project root
- next.config.ts: output standalone for minimal image
- .env.example: documented INVITE_REQUIRED
2026-06-21 18:12:13 +02:00
admin e3c279d6a4 feat(01-04): switch media storage from Cloudflare R2 to Supabase Storage 2026-06-06 11:32:17 +02:00
admin fb8bb5c8fe feat(01-04): wire CDN avatar into profile, edit page, sidebar, and mobile nav
- pets/[petId]/page.tsx: replace /api/avatar stub with NEXT_PUBLIC_R2_PUBLIC_URL CDN URL
- pets/[petId]/edit/page.tsx: pass initialAvatarKey to PetForm so AvatarUpload shows current photo
- Sidebar.tsx: add avatarKey to Pet type; show AvatarImage from CDN for active pet and dropdown list
- MobileNav.tsx: same CDN avatar pattern for mobile pet switcher
- next.config.ts: add images.remotePatterns for R2 CDN hostname (T-4-04 safeguard)
2026-06-06 11:10:28 +02:00
admin 31f9279ffc fix(01-02): correct next.config.ts syntax error (module.exports inside object literal) 2026-06-06 09:13:48 +02:00
admin dbc074e9ee chore(01-01): scaffold Next.js 16 + install full stack + Vitest test scaffold
- Create-next-app Next.js 16.2.7 with TypeScript 6.0.3, React 19, Tailwind v4, Turbopack
- Install all pinned runtime deps: Clerk 7.4.3, tRPC 11.17.0, Prisma 7.8.0, Zod 4.4.3, AWS SDK 3.1062.0, Sonner 2.0.7, Upstash Redis 1.38.0
- Configure Tailwind v4 (postcss.config.mjs uses @tailwindcss/postcss; globals.css uses @import "tailwindcss")
- Initialize shadcn/ui with default preset; add all Phase 1 components (button, input, form, label, textarea, select, avatar, card, dialog, dropdown-menu, sonner, progress, separator, badge, skeleton)
- Set orange-500 design system: --primary: oklch(0.7024 0.1967 41.45); --background: oklch(0.9882 0 0)
- Load Inter via next/font/google in root layout
- Create vitest.config.ts with jsdom environment and @ path alias
- Create Wave 0 test scaffolds (22 it.todo tests): auth, pet-profile, media-upload + prisma-mock helper
- Create .env.example listing all required service env vars
2026-06-05 19:10:46 +02:00