Initial commit: YouTube Studio Flow (backend, frontend, infrastructure, docs)

This commit is contained in:
2026-08-11 12:27:44 +02:00
commit d5af006443
304 changed files with 74604 additions and 0 deletions
@@ -0,0 +1,161 @@
# Teams API
Base path: `/api/v1/teams`
All endpoints require `Authorization: Bearer <jwt>`.
Related: [[01 - Auth API]], [[06 - Linting API]], [[04 - Database Schema]]
---
## GET /teams/mine
List all teams the current user belongs to.
**Response:** `Team[]`
---
## GET /teams/:teamId
Get team details including members and connected channels.
**Response:** `TeamDetail`
---
## GET /teams/:teamId/channels
List connected YouTube channels for a team.
**Response:** `Channel[]`
---
## POST /teams/:teamId/members
Invite a user to the team by email address. Requires `ADMIN` role.
**Request body:**
```json
{
"email": "user@example.com",
"role": "EDITOR"
}
```
**Possible `role` values:** `ADMIN`, `EDITOR`, `REVIEWER`, `READONLY`
> `OWNER` is assigned at team creation and cannot be set via this endpoint.
**Response:** `TeamMember`
---
## PATCH /teams/:teamId/members/:userId
Change a team member's role. Requires `ADMIN` role.
**Request body:**
```json
{ "role": "EDITOR" }
```
---
## DELETE /teams/:teamId/members/:userId
Remove a member from the team. Requires `ADMIN` role.
---
## GET /teams/:teamId/settings
Get team-level render and feature settings.
**Response:**
```json
{
"dateFormat": "DD.MM.YYYY",
"timezone": "Europe/Berlin",
"publishingSchedule": [],
"showCanvaLink": false,
"disabledLintRules": [],
"showDeletedVideos": false,
"conflictDetectionEnabled": false,
"conflictDetectionBatchSize": 50,
"conflictDetectionMinAgeDays": 7
}
```
**Field notes:**
| Field | Notes |
|---|---|
| `dateFormat` | Default date format applied by the render engine to `{video.scheduledAt}` and `{video.recordingDate}` tokens when no inline format is specified (e.g. `{video.scheduledAt\|DD.MM.YYYY}` overrides this). |
| `timezone` | Team's local timezone, used for scheduling display. |
| `publishingSchedule` | Array of preferred publishing time slots used by the next-slot calculator. |
| `showCanvaLink` | Whether to show Canva thumbnail link in the video editor UI. |
| `disabledLintRules` | Array of rule codes (e.g. `TITLE_WEAK`) that are suppressed for this team. |
| `showDeletedVideos` | Whether soft-deleted videos appear in the video list. |
| `conflictDetectionEnabled` | Opt-in for the scheduled remote-conflict sweep. Only takes effect when the operator has also set `CONFLICT_DETECTION_ENABLED=true` on the worker. |
| `conflictDetectionBatchSize` | Max videos checked per sweep for this team (1500). |
| `conflictDetectionMinAgeDays` | Skip videos whose `lastSyncedAt` is within the last N days (≥0). |
---
## PATCH /teams/:teamId/settings
Update team settings. Requires `ADMIN` role.
**Request body** (all fields optional):
```json
{
"dateFormat": "DD.MM.YYYY",
"timezone": "Europe/Berlin",
"publishingSchedule": [],
"showCanvaLink": true,
"disabledLintRules": ["TITLE_WEAK"],
"showDeletedVideos": false,
"conflictDetectionEnabled": true,
"conflictDetectionBatchSize": 100,
"conflictDetectionMinAgeDays": 14
}
```
**Validation:**
- `conflictDetectionBatchSize` must be `1 ≤ n ≤ 500`
- `conflictDetectionMinAgeDays` must be `≥ 0`
Invalid values return `400 Bad Request`.
**Side effects:**
- If `disabledLintRules` changes, existing `LintResult` rows for any newly disabled rules are deleted and `Video.lintStatus` is recomputed for all affected videos automatically.
See [[06 - Linting API]] for more on lint result management. See [[05 - Queue System]] for how the conflict-detection settings drive the scheduled sweep.
---
## GET /teams/:teamId/next-publish-slot
Find the next available publishing slot based on the team's configured publishing schedule.
**Query parameters:**
| Param | Type | Required | Description |
|---|---|---|---|
| `channelId` | string | Yes | The channel to check slots for |
**Response:**
```json
{ "slot": "ISO datetime | null" }
```
`null` is returned if no publishing schedule has been configured for the team.