Files

100 lines
4.2 KiB
Markdown

# 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)