Skip to main content

Per-repository guide

All paths are under /shared/from_machine_c/PROJECTS/GREYSCREEN/DEV_PROCESS/ except the gateway.

RepositoryRoleStackDev / release branchPort
gstv-websitePublic streaming SPAReact 18, Vite 3, Tailwindtest / main5173
greyscreen-webviewMobile streaming SPAReact 18, Vite 5, Vidstack + hls.jstest / main5174
greyscreen-expo-webviewPhone shellExpo 54, RN 0.81test / main—
gstv-tvAndroid TV clientExpo 53, react-native-tvostest / main—
gstv-admin-webBack office SPAReact 18, Vite 5, Ant Design 5test / main5173
gstv-customer-apiPublic APIExpress 4, TypeScript, SWCtest / main3001 dev, 3000 test/prod
gstv-admin-apiAdmin API + orchestrationExpress 4, TypeScript, sharptest / main3003
gstv-vpsEphemeral encoderNode 20, ffmpeg, gcsfusetest / main—
gstv-multimediaHLS/RTMP servernode-media-server, Expresstest / main8000 / 8080 / 1935
gstv-infraGCE operator scriptsBash, gcloudno git—
gstv-secretsOrchestrator keyJSONno git—
gstv-migrationMigration + indexes + rulesNode, firebase-adminmaster—
vps-reverse-proxyGateway + test stacknginx, Compose, Bashone branch per VPS80 / 443

gstv-website​

The public product. Catalogue and trailer pages are anonymous; /play/:id, /home, /mylist, /liked, /history and /profile are protected.

  • Start here: src/App.tsx, src/lib/{apiClient,endpoints,authToken}.ts, src/components/catalog/CatalogScreen.tsx.
  • Firebase: src/Firebase/FirebaseConfig.js is branch-selected, not env-selected. main → grey-screen-tv; test → greyscreen-test-5fb86. Never merge the test version into production.
  • Payments: two Stripe implementations exist (hosted flow and embedded flow). Do not add a third; pick the one the screen already uses.
  • Gotcha: Vite build:test vs build:prod, plus the Dockerfile's VITE_MODE argument. A build without the matching .env.${VITE_MODE} refuses to proceed.
  • Legacy: src/actions/ still uses unauthenticated user-ID endpoints. New code should use the typed apiClient.

greyscreen-webview​

Runs inside the phone app. It is not standalone: the native shell supplies the user and token over the bridge, and there is no Firebase auth in the SPA.

  • Start here: src/config/env.ts, src/services/SimpleBridge.ts, src/routes/index.tsx.
  • Base path: Vite base: '/webview/', so the dev URL includes the prefix.
  • Auth: reads localStorage.user and listens for SEND_USER_DATA_TO_WEBVIEW. It does not validate or refresh the token.
  • Payments: Stripe embedded checkout and Flutterwave mobile money. Success polls verifySubcription and tells the shell to patch local state.
  • Gotcha: .env is a stray copy of production values; .env.test is the test origin. Check which one Vite loads before debugging a host mismatch.

greyscreen-expo-webview​

The phone shell: WebView host, Google/Apple sign-in, FCM, Android Cast, TV pairing and remote.

  • Start here: app.config.ts, app/(main)/index.tsx, services/AuthService.ts, services/TVPairingService.ts, services/SimpleBridge.ts.
  • Builds: EAS. APP_VARIANT / EAS_BUILD_PROFILE select the Firebase key folder and app IDs. Android production is built from the android branch; iOS from ios.
  • Gotcha: signup failure falls back to a random local user in both native clients. It is a demo safeguard, not authentication.
  • Security: the repository tracks a production Firebase Admin key and an iOS credential file. Do not reproduce their contents anywhere. See Security notes.

gstv-tv​

Android TV lean-back client.

  • Start here: app/_layout.tsx, components/player/TVVideoPlayer.tsx, services/PairingService.ts, services/RemoteEffectBus.ts.
  • Playback: utils/streamUrl.ts builds <STREAMING_BASE_URL>/{videoId}/{resolution}/playlist.m3u8; trailers are under /stream/trailers.
  • Payments: none. The TV sends the user to the phone and pairs the account.
  • Gotcha: the remote only executes back and navigate.screen; other effects are published on RemoteEffectBus with no subscribers.
  • Release: EAS + Play; google-play-key.json is referenced but missing.

gstv-admin-web​

Back office. JavaScript project, despite some .ts/.tsx files.

  • Start here: src/components/AddVideosModal/index.jsx, src/config/artwork.js, src/actions/upload.js, src/hooks/useNotifications.js.
  • Wizard order: Video Info → Artwork (two required crops) → Videos (trailer + feature) → Upload. Upload order is thumbnail, thumbnailPreview, trailer, video, then saveVideo last, because the API verifies every object before enqueueing.
  • Artwork contract: thumbnail is portrait 3:4, thumbnailPreview is landscape 16:9; dimensions are mirrored in gstv-admin-api/src/utils/imageArtwork.ts. Change both or neither.
  • Gotcha: the fallback API host is the legacy greyscreentv.testapp.pro; ensure VITE_API_BASE_URL is set.

gstv-customer-api​

Public API. Read-heavy.

  • Start here: src/server.ts, src/app.ts, src/routes/*, src/db/catalogSnapshot.ts, src/services/catalog/top10.ts, src/controllers/pay.controller.ts.
  • Auth: /me* requires a verified Firebase ID token. Legacy /users/* and /signup trust a client-supplied ID — do not extend them.
  • Caching: catalogue routes set short revalidation or no-store; only view aggregates are cached. /catalog/browse can be cached longer because the ID set is rebuilt server-side.
  • Gotcha: a Firestore write with an undefined optional field throws. Omit absent keys.

gstv-admin-api​

Internal API and orchestration plane: uploads, artwork conversion, transcode queue, readiness, VM lifecycle, notifications, deletion.

  • Start here: src/server.ts, src/controllers/uploads.controller.ts, src/services/videos/videoReadiness.service.ts, src/services/vm/vmLifecycle.service.ts, src/pubSub/*, src/routes/notifications.route.ts.
  • Single instance: the RTDB consumers are claim queues. Exactly one replica per database. The SSE hub alone could scale; the process cannot.
  • Uploads: images convert to WebP synchronously in completeUpload; videos are queued by saveVideo; the VM is created only while a queue is non-empty.
  • Gotcha: the legacy queue/imageJobs lane still gates VM creation. A stranded child keeps the VM alive and causes create/delete churn. The producer no longer writes it; do not add one.

gstv-vps​

Ephemeral worker. Consumes queue/transcode, runs one job at a time, writes HLS, publishes resolution events, cleans masters, self-deletes when idle.

  • Start here: src/index.js, src/process/serialProcess.js, src/utils/utils.videos.js, src/status/heartbeat.js, src/lifecycle/shutdown.js.
  • Ladder: 144p through 2160p; only rungs at or below the source height. Master playlist is written before the renditions.
  • Gotcha: the production image is pulled from Artifact Registry, which no repo builds automatically. CI's main workflow pushes a different registry image and does not deploy. Deploy the worker image before the API when both change.

gstv-multimedia​

Long-lived VPS streaming node with a two-tier cache.

  • Start here: index.js, cache-manager/*.
  • Routes: /health, /api/cache/*, /videos/*, /trailers/*, catch-all proxy to NMS.
  • Gotcha: RTMP publish/play and cache stats are unauthenticated; CORS is *. No DRM.

gstv-infra and gstv-secrets​

Operator-only, not CI-deployed.

  • startup-script.sh asserts the environment and starts the worker container.
  • create-instance-template.sh recreates the per-env template.
  • setup-iam.sh creates the VM and orchestrator identities.
  • gstv-secrets holds the high-privilege orchestrator key. Never copy it.

gstv-migration​

Reference for Firestore indexes, RTDB rules and the completed Mongo→Firestore import. Its sync.sh-style workflow for gstv-db-core is not safe: the canonical module has diverged from the copies shipped in both APIs. Do not run a sync; diff all three first.

vps-reverse-proxy​

The public entry point. domains.json is the source of truth; one branch per VPS.

  • Test stack: stacks/greyscreentv-test/docker-compose.yml is current. Its README is stale about image tags and the Firestore API.
  • CI: push to a non-main branch → resolve the VPS branch → git reset --hard → generate.sh → reconcile.sh → compose up → install crons.
  • Gotcha: the test stack shares the production Firebase project. The Compose profile keeps the test worker off the shared queue.