PlayoutAdmin — Environment Variables & Changelog
2026-07-12 — VOD media serving moved off CoreAPI (cproxy passthrough)
Why: CoreAPI's /assets/* route (ServeAsset) streamed every media byte
through the Fiber app process — the same process handling DB writes, tusd
webhooks, and thumbnail generation. This put a control-plane service in the
data plane for all VOD traffic. Phase 1 removes CoreAPI from that path
entirely: a cproxy node now caches reads directly from SeaweedFS.
New chain:
Browser → nginx (/assets/) → cproxy_vod:8080 → SeaweedFS:8333
Browser → nginx (/assets/) → CoreAPI:3000 → SeaweedFS)
Backend change — bucket now embedded in asset URLs
CoreAPI/domain/media/models.go — PublicURL()/ThumbURL() now include the
Bucket field, since cproxy/SeaweedFS's S3 gateway requires /<bucket>/<key>
in the path (no implicit default bucket, unlike CoreAPI's old ServeAsset
which silently assumed one).
before: /assets/jastv/banner/hero.jpg
after: /assets/playtelly/jastv/banner/hero.jpg
No DB migration needed — Bucket was already populated on every Media row;
this is a computed-field change only, takes effect immediately on restart.
nginx — /assets/ proxy target changed
nginx.conf.template — /assets/ now proxies to ${ASSET_SERVING_URL}
(cproxy) instead of ${COREAPI_URL}. Trailing slash on proxy_pass strips
the /assets/ prefix before forwarding, since cproxy/SeaweedFS has no such
namespace — it expects the bucket-prefixed path directly.
GET /assets/playtelly/jastv/vod/a.mp4
→ http://cproxy_vod:8080/playtelly/jastv/vod/a.mp4
→ http://seaweedfs:8333/playtelly/jastv/vod/a.mp4
cproxy — new node added
New service cproxy_vod in seaweedfs.yml, config in cproxy-vod.yml.
Single static passthrough origin, url-pattern: ^/.*$ (bucket-agnostic —
forwards whatever path it receives straight to SeaweedFS's S3 gateway).
Host-exposed on 9222 (proxy) / 9221 (api) for local curl-testing;
internal container-network address is cproxy_vod:8080.
Frontend — new env var, split preview vs. shareable-link URLs
MediaList.jsx — previously used window.location.origin to build asset
URLs. Now uses two separate env-driven constants:
const ASSET_BASE = import.meta.env.VITE_ASSET_BASE_URL || ""
const CDN_HOST = import.meta.env.VITE_CDN_HOST || ASSET_BASE
ASSET_BASE— used for anything rendered inline inside the admin app itself (thumbnails, video/audio/image preview player). Same-origin, goes through this app's own nginx.CDN_HOST— used only for copyable/shareable/downloadable links (the "URL" / "Thumbnail URL" rows, document download button). Falls back toASSET_BASEuntil a real public CDN exists.
Both resolve to the same value today (VITE_CDN_HOST unset locally) — this
is a zero-behavior-change addition, wiring the split ahead of the real CDN
going live.
Environment Variables
Build-time (Vite — baked into JS bundle, requires image rebuild to change)
| Variable | Local value | Role | Status |
|---|---|---|---|
VITE_ASSET_BASE_URL |
http://localhost:15173 |
Same-origin base URL for in-app media preview/thumbnails, proxied through this app's own nginx. | Active |
VITE_CDN_HOST |
"" (unset) |
Public CDN hostname for shareable/downloadable asset links. Falls back to VITE_ASSET_BASE_URL when empty. |
Active, pending real value — set to https://cdn.castis.io (or equivalent) once CDN/GSLB tier exists |
VITE_CORE_API_URL |
http://localhost:3000 |
Base URL for axios clients in utils/api.js. |
Wired but consumer file (utils/api.js) is unimported/dead — no functional effect |
VITE_AUTH_API_URL |
http://localhost:3002 |
Same as above. | Same as above |
VITE_TELLYID_URL |
passed as ${VITE_AUTH_URL} |
Fallback/default for TellyID auth service URL. | Active — utils/auth.js |
VITE_AUTH_URL |
from root .env |
Auth redirect flow. | Active — components/sideBar/Sidebar.jsx |
NPM_AUTH |
from root .env |
.npmrc auth for private @playtelly registry. Build-stage only. |
Active (build infra) |
Runtime (container env — read by nginx via envsubst, no rebuild needed)
| Variable | Local value | Role | Status |
|---|---|---|---|
COREAPI_URL |
http://coreapi:3000 |
Upstream for /api/ proxy block. |
Active |
ASSET_SERVING_URL |
http://cproxy_vod:8080 |
Upstream for /assets/ proxy block. |
Active (new 2026-07-12, was ${COREAPI_URL} before) |
TUSD_URL |
http://tusd:8080 |
Upstream for /files/ proxy block (tus resumable upload). |
Active |
FORWARDED_HOST |
— | Was intended for forwarded-host header context. | Dead — confirmed zero references anywhere in nginx template; removed |
Historical / dead, not part of current architecture
ASSET_BASE_URL(CoreAPI-side env) — deliberately removed per 2026-06-19 refactor (see changelog below). CoreAPI no longer bakes any frontend host into API responses.utils/api.js— contains references toVITE_CORE_API_URL/VITE_AUTH_API_URLbut the file itself is dead code (unimported, contains an undefined-variable bug). Not part of the media-serving path.
Prior changelog
2026-06-19 — Asset URL refactor, relative paths, unified /assets/ route
Why: Backend was baking ASSET_BASE_URL (frontend host) into API
responses, coupling CoreAPI to wherever the frontend was deployed.
What changed:
models.go — PublicURL()/ThumbURL() return relative paths:
before: http://localhost:15173/media-assets/jastv/banner/hero.jpg
after: /assets/jastv/banner/hero.jpg
config.go — AssetBaseURL field removed, ASSET_BASE_URL env no longer
read by CoreAPI.
Route unified to /assets/* (previously mismatched with /media-assets/*
returned by models.go).
nginx location /media-assets/ replaced with location /assets/.
Superseded by 2026-07-12 change above — bucket segment re-added to the
path (/assets/<bucket>/<key>) to support cproxy/SeaweedFS's S3-gateway
path requirements.