4.2 KiB
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 + pushrender.processor— render onlyvideos.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)