|
|
@@ -1,155 +1,194 @@
|
|
|
// provider-video-sync.controller.ts
|
|
|
-import { Controller, Post, Get, Query, Logger } from '@nestjs/common';
|
|
|
-import { ApiTags, ApiOperation, ApiResponse, ApiQuery } from '@nestjs/swagger';
|
|
|
-import { ProviderVideoSyncService } from './provider-video-sync.service';
|
|
|
+import { Body, Controller, Get, Post, Query } from '@nestjs/common';
|
|
|
+import {
|
|
|
+ ApiBody,
|
|
|
+ ApiOperation,
|
|
|
+ ApiQuery,
|
|
|
+ ApiResponse,
|
|
|
+ ApiTags,
|
|
|
+} from '@nestjs/swagger';
|
|
|
+import {
|
|
|
+ IsBoolean,
|
|
|
+ IsInt,
|
|
|
+ IsObject,
|
|
|
+ IsOptional,
|
|
|
+ IsString,
|
|
|
+ Max,
|
|
|
+ Min,
|
|
|
+ ValidateNested,
|
|
|
+} from 'class-validator';
|
|
|
+import { Type } from 'class-transformer';
|
|
|
+import {
|
|
|
+ ProviderVideoSyncOptions,
|
|
|
+ ProviderVideoSyncResult,
|
|
|
+ ProviderVideoSyncService,
|
|
|
+} from './provider-video-sync.service';
|
|
|
+
|
|
|
+class ProviderVideoSyncParamDto {
|
|
|
+ @IsOptional()
|
|
|
+ @IsString()
|
|
|
+ status?: string;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ISO string; provider filters "updated after"
|
|
|
+ * Example: "2025-12-18T21:19:09.227Z"
|
|
|
+ */
|
|
|
+ @IsOptional()
|
|
|
+ @IsString()
|
|
|
+ updatedAt?: string;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Allow extra provider param keys without strict validation.
|
|
|
+ * If you want to lock this down later, replace with explicit fields.
|
|
|
+ */
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
+ [k: string]: any;
|
|
|
+}
|
|
|
+
|
|
|
+export class ProviderVideoSyncRunDto {
|
|
|
+ @IsOptional()
|
|
|
+ @IsString()
|
|
|
+ providerCode?: string;
|
|
|
+
|
|
|
+ @IsOptional()
|
|
|
+ @IsBoolean()
|
|
|
+ fullSync?: boolean;
|
|
|
+
|
|
|
+ @IsOptional()
|
|
|
+ @IsBoolean()
|
|
|
+ resetState?: boolean;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Optional override. In normal usage:
|
|
|
+ * - fullSync: resumes from SyncState cursor
|
|
|
+ * - incremental: pageNum is forced to 1
|
|
|
+ */
|
|
|
+ @IsOptional()
|
|
|
+ @IsInt()
|
|
|
+ @Min(1)
|
|
|
+ pageNum?: number;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Default is handled by service; capped at 500 by service.
|
|
|
+ */
|
|
|
+ @IsOptional()
|
|
|
+ @IsInt()
|
|
|
+ @Min(1)
|
|
|
+ @Max(500)
|
|
|
+ pageSize?: number;
|
|
|
+
|
|
|
+ @IsOptional()
|
|
|
+ @IsObject()
|
|
|
+ @ValidateNested()
|
|
|
+ @Type(() => ProviderVideoSyncParamDto)
|
|
|
+ param?: ProviderVideoSyncParamDto;
|
|
|
+}
|
|
|
|
|
|
@ApiTags('Provider Video Sync')
|
|
|
@Controller('provider-video-sync')
|
|
|
export class ProviderVideoSyncController {
|
|
|
- private readonly logger = new Logger(ProviderVideoSyncController.name);
|
|
|
-
|
|
|
- constructor(
|
|
|
- private readonly providerVideoSyncService: ProviderVideoSyncService,
|
|
|
- ) {}
|
|
|
+ constructor(private readonly service: ProviderVideoSyncService) {}
|
|
|
|
|
|
@Post('run')
|
|
|
@ApiOperation({
|
|
|
- summary: 'Trigger provider video sync',
|
|
|
- description:
|
|
|
- 'Triggers a sync of video media data from configured providers. Currently processes a single page of data.',
|
|
|
+ summary: 'Trigger provider video sync (full sync or incremental)',
|
|
|
+ description: [
|
|
|
+ 'POST body supports optional parameters: providerCode, fullSync, resetState, pageNum, pageSize, param.',
|
|
|
+ 'Incremental sync: send param.updatedAt (ISO string).',
|
|
|
+ 'Full sync: set fullSync=true; service ignores param.updatedAt and resumes by pageNum cursor.',
|
|
|
+ ].join('\n'),
|
|
|
})
|
|
|
- @ApiQuery({
|
|
|
- name: 'providerCode',
|
|
|
- required: false,
|
|
|
- type: 'string',
|
|
|
- description: 'Filter by specific provider code (e.g., RVAKC)',
|
|
|
+ @ApiBody({ type: ProviderVideoSyncRunDto })
|
|
|
+ @ApiResponse({ status: 200, type: Object })
|
|
|
+ async run(
|
|
|
+ @Body() dto: ProviderVideoSyncRunDto,
|
|
|
+ ): Promise<ProviderVideoSyncResult> {
|
|
|
+ const options: ProviderVideoSyncOptions = {
|
|
|
+ providerCode: dto.providerCode,
|
|
|
+ fullSync: dto.fullSync,
|
|
|
+ resetState: dto.resetState,
|
|
|
+ pageNum: dto.pageNum,
|
|
|
+ pageSize: dto.pageSize,
|
|
|
+ param: dto.param,
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.service.syncFromProvider(options);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Get('last-summary')
|
|
|
+ @ApiOperation({
|
|
|
+ summary: 'Get last sync summary from memory (this process only)',
|
|
|
})
|
|
|
- @ApiQuery({
|
|
|
- name: 'page',
|
|
|
- required: false,
|
|
|
- type: 'number',
|
|
|
- description: 'Page number (default: 1)',
|
|
|
+ @ApiResponse({ status: 200, type: Object })
|
|
|
+ getLastSummary(): ProviderVideoSyncResult | null {
|
|
|
+ return this.service.getLastSyncSummary();
|
|
|
+ }
|
|
|
+
|
|
|
+ @Post('run-incremental')
|
|
|
+ @ApiOperation({
|
|
|
+ summary: 'Trigger incremental sync quickly (query params)',
|
|
|
+ description:
|
|
|
+ 'Convenience endpoint. Use /run for full control. Incremental uses param.updatedAt.',
|
|
|
})
|
|
|
@ApiQuery({
|
|
|
- name: 'pageSize',
|
|
|
+ name: 'updatedAt',
|
|
|
required: false,
|
|
|
- type: 'number',
|
|
|
- description: 'Number of records per page (default: 100)',
|
|
|
+ description: 'ISO string updatedAt cursor',
|
|
|
})
|
|
|
- @ApiResponse({
|
|
|
- status: 200,
|
|
|
- description: 'Provider video sync triggered successfully',
|
|
|
- schema: {
|
|
|
- type: 'object',
|
|
|
- properties: {
|
|
|
- imported: {
|
|
|
- type: 'number',
|
|
|
- description: 'Total number of videos processed',
|
|
|
- example: 50,
|
|
|
- },
|
|
|
- created: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of new ProviderVideoSync records created',
|
|
|
- example: 10,
|
|
|
- },
|
|
|
- updated: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of existing ProviderVideoSync records updated',
|
|
|
- example: 35,
|
|
|
- },
|
|
|
- skipped: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of records skipped due to errors',
|
|
|
- example: 5,
|
|
|
- },
|
|
|
- errors: {
|
|
|
- type: 'array',
|
|
|
- description: 'Array of errors encountered (if any)',
|
|
|
- items: {
|
|
|
- type: 'object',
|
|
|
- properties: {
|
|
|
- id: { type: 'string', example: '6650a9e5f9c3f12a8b000001' },
|
|
|
- error: {
|
|
|
- type: 'string',
|
|
|
- example: 'Sync failed: provider timeout',
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- })
|
|
|
- async runSync(
|
|
|
- @Query('providerCode') providerCode?: string,
|
|
|
- @Query('page') page?: string,
|
|
|
+ @ApiQuery({ name: 'pageSize', required: false, description: '1..500' })
|
|
|
+ @ApiQuery({ name: 'providerCode', required: false })
|
|
|
+ @ApiQuery({ name: 'resetState', required: false, description: 'true/false' })
|
|
|
+ async runIncremental(
|
|
|
+ @Query('updatedAt') updatedAt?: string,
|
|
|
@Query('pageSize') pageSize?: string,
|
|
|
- ) {
|
|
|
- this.logger.log('[runSync] Trigger received');
|
|
|
- this.logger.debug('[runSync] Query params:', {
|
|
|
- providerCode,
|
|
|
- page,
|
|
|
- pageSize,
|
|
|
- });
|
|
|
-
|
|
|
- const result = await this.providerVideoSyncService.syncFromProvider({
|
|
|
- providerCode,
|
|
|
- page: page ? parseInt(page, 10) : 1,
|
|
|
- pageSize: pageSize ? parseInt(pageSize, 10) : 100,
|
|
|
- });
|
|
|
-
|
|
|
- this.logger.log('[runSync] Sync completed', result);
|
|
|
- return result;
|
|
|
+ @Query('providerCode') providerCode?: string,
|
|
|
+ @Query('resetState') resetState?: string,
|
|
|
+ ): Promise<ProviderVideoSyncResult> {
|
|
|
+ const options: ProviderVideoSyncOptions = {
|
|
|
+ providerCode: providerCode || undefined,
|
|
|
+ fullSync: false,
|
|
|
+ resetState: resetState === 'true',
|
|
|
+ pageSize: pageSize ? Number(pageSize) : undefined,
|
|
|
+ param: {
|
|
|
+ status: 'Completed',
|
|
|
+ updatedAt: updatedAt || undefined,
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.service.syncFromProvider(options);
|
|
|
}
|
|
|
|
|
|
- @Get('status')
|
|
|
+ @Post('run-full')
|
|
|
@ApiOperation({
|
|
|
- summary: 'Get last provider video sync status',
|
|
|
- description: 'Returns a summary of the last provider video sync operation.',
|
|
|
+ summary: 'Trigger full sync quickly (query params)',
|
|
|
+ description:
|
|
|
+ 'Convenience endpoint. Full sync ignores updatedAt filter and resumes by stored pageNum cursor.',
|
|
|
})
|
|
|
- @ApiResponse({
|
|
|
- status: 200,
|
|
|
- description: 'Last sync status retrieved successfully',
|
|
|
- schema: {
|
|
|
- type: 'object',
|
|
|
- nullable: true,
|
|
|
- properties: {
|
|
|
- imported: {
|
|
|
- type: 'number',
|
|
|
- description: 'Total number of videos processed',
|
|
|
- example: 50,
|
|
|
- },
|
|
|
- created: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of new ProviderVideoSync records created',
|
|
|
- example: 10,
|
|
|
- },
|
|
|
- updated: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of existing ProviderVideoSync records updated',
|
|
|
- example: 35,
|
|
|
- },
|
|
|
- skipped: {
|
|
|
- type: 'number',
|
|
|
- description: 'Number of records skipped due to errors',
|
|
|
- example: 5,
|
|
|
- },
|
|
|
- errors: {
|
|
|
- type: 'array',
|
|
|
- description: 'Array of errors encountered (if any)',
|
|
|
- items: {
|
|
|
- type: 'object',
|
|
|
- properties: {
|
|
|
- id: { type: 'string' },
|
|
|
- error: { type: 'string' },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
- },
|
|
|
+ @ApiQuery({ name: 'pageSize', required: false, description: '1..500' })
|
|
|
+ @ApiQuery({ name: 'providerCode', required: false })
|
|
|
+ @ApiQuery({ name: 'resetState', required: false, description: 'true/false' })
|
|
|
+ @ApiQuery({
|
|
|
+ name: 'pageNum',
|
|
|
+ required: false,
|
|
|
+ description: 'override start pageNum (resetState recommended)',
|
|
|
})
|
|
|
- async getStatus() {
|
|
|
- this.logger.log('[getStatus] Status requested');
|
|
|
- const status = this.providerVideoSyncService.getLastSyncSummary();
|
|
|
- return status || { message: 'No sync has been performed yet' };
|
|
|
+ async runFull(
|
|
|
+ @Query('pageSize') pageSize?: string,
|
|
|
+ @Query('providerCode') providerCode?: string,
|
|
|
+ @Query('resetState') resetState?: string,
|
|
|
+ @Query('pageNum') pageNum?: string,
|
|
|
+ ): Promise<ProviderVideoSyncResult> {
|
|
|
+ const options: ProviderVideoSyncOptions = {
|
|
|
+ providerCode: providerCode || undefined,
|
|
|
+ fullSync: true,
|
|
|
+ resetState: resetState === 'true',
|
|
|
+ pageSize: pageSize ? Number(pageSize) : undefined,
|
|
|
+ pageNum: pageNum ? Number(pageNum) : undefined,
|
|
|
+ param: {
|
|
|
+ status: 'Completed',
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ return this.service.syncFromProvider(options);
|
|
|
}
|
|
|
}
|