# Backend Architecture ## Entry Points | File | Role | |---|---| | `src/main.ts` | Starts the NestJS HTTP API on port 3001 | | `src/worker.ts` | Starts the BullMQ worker (no HTTP server) | ## Module Map ``` src/modules/ auth/ Google OAuth, JWT strategy, guards videos/ Video CRUD, bulk ops, sync enqueue, findAll with filters video-configs/ Per-video description config (block order, overrides, variables) blocks/ DescriptionBlock CRUD + versioning templates/ Template CRUD, preview, apply-to-video collaborators/ Collaborator CRUD teams/ Team management, members, channels, settings, publishing schedule team-variables/ Global team-level key/value variables saved-views/ Saved filter presets for the video list campaigns/ Campaign date windows (CAMPAIGN blocks auto-include when active) playlists/ YouTube playlist sync + video↔playlist management linting/ Rule-based lint checks on video metadata bulk-jobs/ Bulk metadata operations (preview, execute, rollback) youtube-sync/ Channel import (YouTubeApiClient, ChannelImportService) calendar/ Scheduled video calendar view imports/ CSV + JSON workspace import exports/ CSV + JSON workspace export quota/ YouTube API quota tracking and history src/shared/ prisma/ PrismaService singleton render-engine/ VideoRenderService + RenderEngineService + hashMetadata audit/ AuditService — logs all user mutations quota/ QuotaService — tracks and enforces YouTube API quota system-variables/ Built-in {video.*} and {collab.*} token registry src/queues/processors/ youtube-sync.processor.ts Renders description + pushes all fields to YouTube render.processor.ts Background render without pushing lint.processor.ts Runs all lint rules against a video bulk-metadata.processor.ts Processes bulk job items import.processor.ts CSV import processing ``` ## Auth Pattern Every controller method receives the authenticated user via `@Req() req`. Extract: - `req.user.id` — actorId (for audit logs, ownership checks) - `req.user.teamId` — always scope DB queries to this team All routes are protected by `JwtAuthGuard`. Role-restricted routes additionally use `RolesGuard` with `@Roles(TeamRole.EDITOR)` or `@Roles(TeamRole.ADMIN)`. ## Team Scoping Rule **Always** filter database queries by team. The ownership chain is: ``` Team → Channel → Video ``` To scope a video query: `where: { channel: { teamId: req.user.teamId } }` ## Audit Logging `AuditService.log(actorId, entityType, entityId, action, before, after)` must be called for every user-facing mutation (create/update/delete). Background/system operations do not log. Tracked entity types: `Video`, `VideoConfig`, `DescriptionBlock`, `Template`, `Collaborator`, `TeamVariable`, `SavedView`, `TeamMember`. Modules that need audit logging must import `AuditModule` in their `@Module` imports array. ## Shared Services ### VideoRenderService Single source of truth for description rendering. Fetches all required data (blocks, team variables, campaign blocks, collaborators, team date format, playlists) and delegates to `RenderEngineService`. Used by all three render paths: - `youtube-sync.processor` — render + push - `render.processor` — render only - `videos.service.ts renderDescription()` — on-demand **Never duplicate the data-fetching logic.** Always go through `VideoRenderService`. ### RenderEngineService Pure computation. Takes `RenderInput`, returns `{ rendered: string, hash: string }`. Used directly only by `video-configs.service.ts renderPreview()` and `templates.service.ts renderPreview()` (which already have all data). ### QuotaService `canSpend(units)` and `spend(units, operation, meta)` must be called before any YouTube API write. Quota resets at midnight Pacific Time. `videos.update` costs 50 units; daily limit is 10,000. ### AuditService See Audit Logging above. ## Related - [[01 - System Overview]] - [[04 - Database Schema]] - [[05 - Queue System]] - [[07 - Render Engine]] - [[03 - Metadata Linting]] (features)