Commit Graph
15 Commits
Author SHA1 Message Date
Leon SerfatyandClaude Opus 5 8067980774 fix(deploy): inject Dokploy env into containers via env_file
The compose services declared only STORAGE_DIR and PORT under `environment`,
so DATABASE_URL, BETTER_AUTH_SECRET and the Turnstile keys never reached the
containers: `docker compose` reads the generated .env for ${VAR} interpolation
(build args) but does not pass it into services on its own. `prisma migrate
deploy` therefore started with no DATABASE_URL and both containers exited 1 in
a restart loop.

Add `env_file: [.env]` to web and worker so the Environment tab actually
becomes runtime configuration, as the file's own comment already claimed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:57:10 -04:00
Leon SerfatyandClaude Opus 5 0c6b6c5cad fix(deploy): attach compose services to dokploy-network
Dokploy's managed Postgres runs as its own service on the shared
`dokploy-network`. The compose stack only had its own default network, so
web and worker could not resolve the database host — `prisma migrate deploy`
failed on boot and both containers crash-looped.

Join both services to `dokploy-network` while keeping `default` for
web<->worker traffic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:52:14 -04:00
Leon SerfatyandClaude Opus 5 3e9ba07175 feat: Cloudflare Turnstile on auth, CSP fixes, admin/SEO/analytics additions
Turnstile bot protection (sign-in, sign-up, password-reset):
- Register Better Auth's captcha plugin with the cloudflare-turnstile
  provider; endpoints listed explicitly rather than relying on defaults.
  /reset-password is intentionally excluded — it is reached only via a
  single-use emailed token.
- Add an explicit-render Turnstile widget component. Tokens are single-use,
  so each form resets the challenge after a failed submit; submit stays
  disabled until a token is held.
- Read the site key server-side and pass it down as a prop, so rotating it
  does not require a rebuild.
- Fail fast in production when TURNSTILE_SECRET_KEY is missing, and when a
  secret is set without a site key (that combination would demand a token
  no form can produce, locking every user out).
- Pass a throwaway secret during `next build` in the Dockerfile, mirroring
  the existing BETTER_AUTH_SECRET treatment, so image builds don't need it.

CSP fixes in middleware (these blocked Turnstile entirely):
- Add frame-src for challenges.cloudflare.com. Without it the widget's
  iframe fell back to default-src 'self' and was blocked outright.
- Allow 'unsafe-eval' and websockets in DEVELOPMENT only. `next dev`
  compiles with eval(), so the strict policy threw EvalError and killed
  hydration — no client JS ran at all, which also meant form submit
  handlers never fired. Production policy is unchanged and still strict.

Also included (concurrent work in the tree):
- Admin organizations pages and lib/admin/orgs.
- Episode moderation migration, SEO metadata (sitemap, robots, JSON-LD,
  OG/Twitter images, manifest), Umami analytics, not-found page.

Local dev database: docker-compose.dev.yml provisions Postgres 18 on port
5443 (5432-5442 are in use by other local projects).

Note: `npx tsc --noEmit` currently fails in app/(app)/team/page.tsx — an
`invitations` prop the component does not accept. This predates the commit
and will fail `next build` until fixed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 11:10:55 -04:00
Leon SerfatyandClaude Opus 4.8 35379212fb fix(worker): neutralize server-only at runtime so the tsx worker can boot
The worker runs worker/index.ts under tsx (plain Node) and transitively
imports lib/flags (and other server-only-guarded modules) via the generation
pipeline. `server-only` resolves to its throwing index.js outside Next's RSC
bundler, crash-looping the worker container. Copy the package's empty.js over
index.js in the final image so the runtime import is a no-op; the build-time
client/server guard already ran during `next build`. Web is unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:31:38 -04:00
Leon SerfatyandClaude Opus 4.8 328cd62766 fix(docker): use a >=32-char build-time BETTER_AUTH_SECRET placeholder
The security-hardening pass tightened lib/auth/auth.ts to reject secrets
shorter than 32 chars (or known placeholders) in production. next build
evaluates that guard while collecting page data for /api/auth/[...all], so
the old 21-char "build-time-placeholder" now fails the build. Lengthen the
throwaway build-only value to satisfy the guard; the real secret is still
injected at run time and never baked into the image.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:19:56 -04:00
Leon SerfatyandClaude Opus 4.8 19a403b431 fix(storage): serve assets via app routes when MEDIA_PUBLIC_BASE_URL is unset
publicUrl() defaulted MEDIA_BASE to "/media" even with no env set, so cover
art on public share pages pointed at a /media path that only the old nginx
(Plesk) box served. In the containerized Coolify/Docker deploy nothing serves
/media, breaking those images. Return null when MEDIA_PUBLIC_BASE_URL is unset
so callers fall back to the app's own /api/public/.../cover route (the deploy
README already documents assets as app-served). Backward compatible: behavior
is unchanged when the env var is set.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:10:52 -04:00
Leon Serfaty 51c541ad22 Security & robustness hardening pass
Cross-cutting input-validation, isolation, and DoS-resistance fixes across
the app, API, billing, queue, and infra layers.

- Runtime validation (zod) for client-supplied admin actions (role/plan/
  limits), series generation index, and all pg-boss queue payloads
- Auth: require email verification before sign-in; reject weak/placeholder/
  short BETTER_AUTH_SECRET in production
- Billing: sanitize Stripe/PayPal errors (log server-side, generic to client);
  race-safe subscription upsert; only count "processed" webhook events as
  handled; verify org membership in getEffectivePlan to block plan escalation
- Series generation: reserve usage up front and refund on failure; bill the
  owning org, not the caller's active org
- Injection defenses: HTML-escape user fields in emails, strip CR/LF from
  subject/recipient, validate ElevenLabs voiceId before URL interpolation
- Media routes: stream off disk instead of buffering whole files; rate-limit
  anonymous public audio/cover endpoints by client IP
2026-06-20 20:59:03 -04:00
Leon SerfatyandClaude Opus 4.8 cd1d6a1a28 Add brand logo + favicon across app surfaces
Ship the Podcast Distribution AI wordmark as real assets and replace the
Mic-tile + text placeholder everywhere it appeared.

- public/logo-dark.png (dark wordmark, for light backgrounds) and
  public/logo-light.png (light wordmark, for dark backgrounds)
- New <Logo> component swaps the two via Tailwind dark: variants, so the
  dark logo shows on all non-themed surfaces (marketing, auth, admin,
  public share) and the light logo only in dark-mode app surfaces
- Wire <Logo> into the marketing header/footer, auth layout, app header
  (default branch only - white-label org logos untouched), admin header
  (+ Admin badge), and the public share page
- Favicon via App Router file convention: app/icon.png + app/apple-icon.png

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:58:37 -04:00
Leon Serfaty 013a3525e5 docs: point deploy git URL at the actual repo (admin/podcastdistributiona) 2026-06-20 20:17:09 -04:00
Leon Serfaty 4fb2645db1 Rebrand PodcastYes -> Podcast Distribution AI (podcastdistributionai.com) 2026-06-20 20:12:43 -04:00
Leon Serfaty 3ad90f076f fix: tolerate empty NEXT_PUBLIC_APP_URL during production build 2026-06-08 04:46:58 -04:00
Leon Serfaty 305278a846 Add comprehensive /features page + allow Unsplash imagery 2026-06-08 04:37:10 -04:00
Leon Serfaty 8138827657 Retarget deployment from Plesk to Dokploy (Docker Compose) 2026-06-07 18:30:53 -04:00
Leon Serfaty f033f00379 Comprehensive admin + user dashboards (production-ready) 2026-06-07 17:54:30 -04:00
Leon Serfaty 155507f21a Initial commit: PodcastYes — AI podcast platform 2026-06-07 03:58:32 -04:00