Spaces:
Runtime error
Runtime error
docs: Introduce comprehensive technical documentation and update planning artifacts to reflect MVP completion and detailed FR implementation status.
d03d74d | # Vault - Inngest Workflows | |
| **Generated:** 2026-02-11 | |
| **Framework:** Inngest 3.49.1 | |
| --- | |
| ## Overview | |
| Inngest provides durable workflow orchestration for long-running background tasks. All workflows are defined in [`src/inngest/`](../src/inngest/). | |
| ### Client Configuration | |
| **File:** [`src/inngest/client.ts`](../src/inngest/client.ts) | |
| ```typescript | |
| import { Inngest } from 'inngest'; | |
| export const inngest = new Inngest({ | |
| id: 'vault', | |
| name: 'Vault AI Discovery', | |
| eventKey: process.env.INNGEST_EVENT_KEY, | |
| }); | |
| ``` | |
| ### Handler Registration | |
| **File:** [`src/app/api/inngest/route.ts`](../src/app/api/inngest/route.ts) | |
| ```typescript | |
| import { serve } from 'inngest/next'; | |
| import { inngest } from '@/inngest/client'; | |
| import { scanVideoArchive } from '@/inngest/functions/scan-video-archive'; | |
| import { detectObjects } from '@/inngest/functions/detect-objects'; | |
| import { matchMarketplace } from '@/inngest/functions/match-marketplace'; | |
| import { monitorLinkHealth, checkSingleLinkHealth } from '@/inngest/functions/monitor-link-health'; | |
| import { warmFeaturedCreatorsCache } from '@/inngest/functions/warm-featured-creators-cache'; | |
| import { healSocialMetadata } from '@/inngest/functions/social-healing'; | |
| export const { GET, POST, PUT } = serve({ | |
| client: inngest, | |
| functions: [ | |
| scanVideoArchive, | |
| detectObjects, | |
| matchMarketplace, | |
| monitorLinkHealth, | |
| checkSingleLinkHealth, | |
| warmFeaturedCreatorsCache, | |
| healSocialMetadata, | |
| ], | |
| signingKey: process.env.INNGEST_SIGNING_KEY, | |
| }); | |
| ``` | |
| --- | |
| ## Workflow: Scan Video Archive | |
| **Function ID:** `scan-video-archive` | |
| **File:** [`src/inngest/functions/scan-video-archive.ts`](../src/inngest/functions/scan-video-archive.ts) | |
| **Event:** `youtube/video-archive.scan` | |
| ### Purpose | |
| Scans a YouTube channel's video archive, fetching metadata and queuing videos for object detection. | |
| ### Event Payload | |
| ```typescript | |
| interface ScanVideoArchiveEvent { | |
| name: 'youtube/video-archive.scan'; | |
| data: { | |
| channelId: string; // Internal DB channel ID | |
| userId: string; // User who initiated scan | |
| scanJobId: string; // Progress tracking ID | |
| }; | |
| } | |
| ``` | |
| ### Workflow Steps | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β scan-video-archive β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β β | |
| β Step 1: validate-channel β | |
| β βββ Verify channel ownership β | |
| β βββ Update syncStatus to 'syncing' β | |
| β βββ Return channel data β | |
| β β | |
| β Step 2: fetch-refresh-token β | |
| β βββ Get OAuth refresh token from accounts table β | |
| β β | |
| β Step 3: refresh-access-token β | |
| β βββ Exchange refresh token for new access token β | |
| β β | |
| β Step 4: get-uploads-playlist β | |
| β βββ Fetch channel's uploads playlist ID β | |
| β β | |
| β Step 5: fetch-video-ids (paginated) β | |
| β βββ Get all video IDs from playlist β | |
| β β | |
| β Step 6: fetch-video-metadata (batched) β | |
| β βββ Get metadata for each video β | |
| β β | |
| β Step 7: store-videos β | |
| β βββ Insert/update videos in database β | |
| β β | |
| β Step 8: trigger-detection (per video) β | |
| β βββ Send 'youtube/video.detect-objects' event β | |
| β β | |
| β Step 9: update-scan-job β | |
| β βββ Mark scan job as completed β | |
| β β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### Error Handling | |
| - **NonRetriableError:** Channel not found, access denied | |
| - **RetryAfterError:** YouTube API rate limits | |
| - **onFailure:** Update scan job status to 'failed', set channel syncStatus to 'errored' | |
| --- | |
| ## Workflow: Detect Objects | |
| **Function ID:** `detect-objects` | |
| **File:** [`src/inngest/functions/detect-objects.ts`](../src/inngest/functions/detect-objects.ts) | |
| **Event:** `youtube/video.detect-objects` | |
| ### Purpose | |
| Extracts frames from a video and runs AI object detection on each frame. | |
| ### Event Payload | |
| ```typescript | |
| interface DetectObjectsEvent { | |
| name: 'youtube/video.detect-objects'; | |
| data: { | |
| videoId: string; // Internal DB video ID | |
| videoUrl: string; // Video URL for processing | |
| }; | |
| } | |
| ``` | |
| ### Workflow Steps | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β detect-objects β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β β | |
| β Step 1: update-scan-status-started β | |
| β βββ Set video scanStatus to 'in_progress' β | |
| β β | |
| β Step 2: analyze-video-content β | |
| β βββ Calculate adaptive frame interval β | |
| β β βββ 0-2 min: every 5 seconds β | |
| β β βββ 2-5 min: every 10 seconds β | |
| β β βββ 5-10 min: every 15 seconds β | |
| β β βββ 10+ min: every 30 seconds β | |
| β βββ Extract frames at intervals (FFmpeg) β | |
| β βββ Process frames in batches of 3 β | |
| β βββ Run AI detection on each frame β | |
| β βββ Generate thumbnails for detections β | |
| β βββ Return all detection results β | |
| β β | |
| β Step 3: store-detections β | |
| β βββ Insert detected objects into database β | |
| β β | |
| β Step 4: trigger-marketplace-matching β | |
| β βββ Send 'discovery/objects.match-marketplace' event β | |
| β β | |
| β Step 5: update-scan-status-completed β | |
| β βββ Set video scanStatus to 'awaiting_approval' β | |
| β β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### AI Vision Providers | |
| Configured via `VISION_PROVIDER` environment variable: | |
| | Provider | Model | Best For | | |
| |----------|-------|----------| | |
| | `gemini` | Gemini Flash | Complex scenes, good accuracy | | |
| | `huggingface` | DETR | Specialized object detection, better free tier | | |
| ### Error Handling | |
| - **VideoUnavailableError:** Video is private or deleted | |
| - **VisionRateLimitError:** AI provider rate limits | |
| - **NonRetriableError:** Missing videoId or videoUrl | |
| --- | |
| ## Workflow: Match Marketplace | |
| **Function ID:** `match-marketplace` | |
| **File:** [`src/inngest/functions/match-marketplace.ts`](../src/inngest/functions/match-marketplace.ts) | |
| **Event:** `discovery/objects.match-marketplace` | |
| ### Purpose | |
| Searches Amazon, eBay, and Etsy for products matching detected objects. | |
| ### Event Payload | |
| ```typescript | |
| interface MatchMarketplaceEvent { | |
| name: 'discovery/objects.match-marketplace'; | |
| data: { | |
| detectedObjectId?: string; // Single ID (legacy) | |
| detectedObjectIds?: string[]; // Batch IDs (preferred) | |
| }; | |
| } | |
| ``` | |
| ### Workflow Steps | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β match-marketplace β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β β | |
| β For each detected object: β | |
| β β | |
| β Step 1: fetch-object-{id} β | |
| β βββ Get detection details from database β | |
| β β | |
| β Step 2: search-amazon-{id} β | |
| β βββ Check cache for existing matches β | |
| β βββ If not cached, search Amazon PA-API β | |
| β βββ Cache results β | |
| β βββ Return matches β | |
| β β | |
| β Step 3: search-ebay-{id} β | |
| β βββ Check cache for existing matches β | |
| β βββ If not cached, search eBay Finding API β | |
| β βββ Cache results β | |
| β βββ Return matches β | |
| β β | |
| β Step 4: search-etsy-{id} β | |
| β βββ Check cache for existing matches β | |
| β βββ If not cached, search Etsy Open API β | |
| β βββ Cache results β | |
| β βββ Return matches β | |
| β β | |
| β Step 5: store-matches-{id} β | |
| β βββ Insert marketplace matches into database β | |
| β β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### Caching Strategy | |
| - **Cache Key:** `{objectId}:{marketplace}` | |
| - **TTL:** 24 hours | |
| - **Storage:** Upstash Redis | |
| ### Rate Limit Handling | |
| - **MarketplaceRateLimitError:** Triggers `RetryAfterError` | |
| - Automatic retry after specified delay | |
| --- | |
| ## Workflow: Monitor Link Health | |
| **Function ID:** `monitor-link-health` | |
| **File:** [`src/inngest/functions/monitor-link-health.ts`](../src/inngest/functions/monitor-link-health.ts) | |
| **Event:** `marketplace/links.health-check` | |
| ### Purpose | |
| Periodically checks affiliate links for availability and updates status. | |
| ### Event Payload | |
| ```typescript | |
| interface MonitorLinkHealthEvent { | |
| name: 'marketplace/links.health-check'; | |
| data: { | |
| matchId?: string; // Single match check | |
| checkAll?: boolean; // Check all active links | |
| }; | |
| } | |
| ``` | |
| ### Workflow Steps | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β monitor-link-health β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β β | |
| β Step 1: fetch-links-to-check β | |
| β βββ Get links with status 'ACTIVE' or 'CHECKING' β | |
| β β | |
| β For each link: β | |
| β β | |
| β Step 2: check-link-{id} β | |
| β βββ HTTP HEAD request to affiliate URL β | |
| β βββ Record HTTP status β | |
| β βββ Return status β | |
| β β | |
| β Step 3: update-link-status-{id} β | |
| β βββ If 200: Set status 'ACTIVE' β | |
| β βββ If 404: Set status 'BROKEN' β | |
| β βββ Increment checkAttempts β | |
| β β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ### Link Status Transitions | |
| ``` | |
| ACTIVE ββcheckββ> CHECKING ββsuccessββ> ACTIVE | |
| β | |
| βββfailureββ> BROKEN | |
| ``` | |
| --- | |
| ## Workflow: Warm Featured Creators Cache | |
| **Function ID:** `warm-featured-creators-cache` | |
| **File:** [`src/inngest/functions/warm-featured-creators-cache.ts`](../src/inngest/functions/warm-featured-creators-cache.ts) | |
| **Event:** `cache/warm-featured-creators` | |
| ### Purpose | |
| Pre-warms cache for featured creators on the homepage. | |
| ### Workflow Steps | |
| 1. Fetch featured creators from database | |
| 2. For each creator, fetch their vault data | |
| 3. Store in Redis cache with TTL | |
| --- | |
| ## Workflow: Heal Social Metadata | |
| **Function ID:** `heal-social-metadata` | |
| **File:** [`src/inngest/functions/social-healing.ts`](../src/inngest/functions/social-healing.ts) | |
| **Event:** `social/metadata.heal` | |
| ### Purpose | |
| Repairs inconsistent platform metadata for multi-platform content. | |
| ### Workflow Steps | |
| 1. Find videos with platform mismatches | |
| 2. Update platform field based on video ID patterns | |
| 3. Update channel platform associations | |
| --- | |
| ## Error Handling Patterns | |
| ### NonRetriableError | |
| For errors that should not be retried: | |
| ```typescript | |
| import { NonRetriableError } from 'inngest'; | |
| throw new NonRetriableError('Channel not found or access denied'); | |
| ``` | |
| ### RetryAfterError | |
| For rate-limited resources: | |
| ```typescript | |
| import { RetryAfterError } from 'inngest'; | |
| throw new RetryAfterError('Amazon rate limit exceeded', '60s'); | |
| ``` | |
| ### Sentry Integration | |
| All functions capture errors with Sentry: | |
| ```typescript | |
| import * as Sentry from '@sentry/nextjs'; | |
| onFailure: async ({ event, error }) => { | |
| Sentry.captureException(error, { | |
| tags: { | |
| source: 'inngest', | |
| function: 'function-name', | |
| type: 'function_failure', | |
| }, | |
| extra: { event: event.data }, | |
| }); | |
| } | |
| ``` | |
| --- | |
| ## Development & Testing | |
| ### Local Development | |
| 1. Start Inngest Dev Server: | |
| ```bash | |
| npx inngest-cli dev | |
| ``` | |
| 2. Application connects to dev server automatically in development mode. | |
| ### Manual Triggers | |
| Use the manual trigger endpoint for testing: | |
| ```bash | |
| curl -X POST http://localhost:3000/api/manual-trigger \ | |
| -H "Content-Type: application/json" \ | |
| -d '{"function": "scan-video-archive", "data": {...}}' | |
| ``` | |
| ### Monitoring | |
| - **Inngest Cloud Dashboard:** View function runs, retries, errors | |
| - **Sentry:** Error tracking with context | |
| - **Logs:** Console output captured in Inngest dashboard | |
| --- | |
| ## Related Documentation | |
| - [API Contracts](./api-contracts.md) - Server actions and endpoints | |
| - [Data Models](./data-models.md) - Database schema | |
| - [Development Guide](./development-guide.md) - Setup instructions | |