diff --git a/.env.docker.example b/.env.docker.example index cab4e31..349e68b 100644 --- a/.env.docker.example +++ b/.env.docker.example @@ -70,5 +70,7 @@ STRIPE_WEBHOOK_SECRET="" BUNNY_STREAM_API_KEY="" BUNNY_STREAM_LIBRARY_ID="" BUNNY_API_KEY="" +# Playback host for Bunny versions. Set BUNNY_CDN_URL: the app reads it at request +# time, while NEXT_PUBLIC_BUNNY_CDN_URL only reaches the browser in a source build. BUNNY_CDN_URL="" NEXT_PUBLIC_BUNNY_CDN_URL="" diff --git a/README.md b/README.md index eb6c6b7..f6a4bd6 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,7 @@ OPENFRAME_REQUIRE_INVITE_CODE=false Behavior when disabled: - `OPENFRAME_ENABLE_STRIPE=false` disables Stripe checkout and customer portal flows and removes billing-based workspace restrictions. -- `OPENFRAME_ENABLE_BUNNY_UPLOADS=false` hides Bunny direct-upload entry points. URL-based providers such as YouTube remain available. +- `OPENFRAME_ENABLE_BUNNY_UPLOADS=false` hides Bunny direct-upload entry points. URL-based providers such as YouTube remain available. When enabling it, set `BUNNY_CDN_URL` (not only `NEXT_PUBLIC_BUNNY_CDN_URL`): it is read at request time, so a published image picks up the playback host without a rebuild. - `OPENFRAME_ENABLE_S3_VIDEO_UPLOADS=true` (with `R2_*` configured) enables presigned uploads to your own S3-compatible storage. Set `OPENFRAME_ENABLE_BUNNY_UPLOADS=false` — only one direct-upload backend can be active. The bucket must allow CORS `PUT` from your app origin (for example `http://localhost:3000` in dev and your production URL). For Docker + MinIO, keep `R2_ENDPOINT=http://minio:9000` (app-internal) and set `R2_PRESIGN_ENDPOINT` to the browser-reachable MinIO origin (for example `http://localhost:9000` locally, or `https://minio.example.com` when MinIO is behind a reverse proxy). Use the origin only — no path suffix. The app's Content-Security-Policy is generated from runtime env at request time, so published Docker images pick up custom `R2_PRESIGN_ENDPOINT` values without rebuilding or editing `next.config.ts`. - `OPENFRAME_REQUIRE_INVITE_CODE=false` allows open registration while keeping invitation-link registration intact. - `OPENFRAME_ENABLE_ANALYTICS=true` records first-touch attribution and funnel events into your own database, readable on `/admin/growth`, or as JSON on `/api/admin/growth` by a script sending `Authorization: Bearer $OPENFRAME_ADMIN_API_TOKEN` (at least 32 characters, unset by default, in which case an admin session is the only way in). Off by default, and nothing leaves the instance either way. diff --git a/app/layout.tsx b/app/layout.tsx index c16359f..4f311c6 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -2,6 +2,10 @@ import type { Metadata } from 'next'; import { Geist_Mono, JetBrains_Mono } from 'next/font/google'; import { Toaster } from 'sonner'; import { ThemeProvider } from '@/components/theme-provider'; +import { + buildRuntimePublicConfig, + RUNTIME_PUBLIC_CONFIG_ELEMENT_ID, +} from '@/lib/runtime-public-config'; import { seoConfig } from '@/lib/seo'; import './globals.css'; @@ -120,6 +124,16 @@ export default function RootLayout({ suppressHydrationWarning > + {/* Not executed, only parsed by readRuntimePublicConfig(). It carries the + public settings the browser cannot get from NEXT_PUBLIC_* variables, + which are frozen into the bundle when the image is built. */} +