diff --git a/src/data-sources/data-sources.controller.ts b/src/data-sources/data-sources.controller.ts new file mode 100644 index 0000000..da696c0 --- /dev/null +++ b/src/data-sources/data-sources.controller.ts @@ -0,0 +1,71 @@ +import { Body, Controller, Get, Param, ParseUUIDPipe, Patch, Post, Query } from '@nestjs/common'; +import { IsIn, IsInt, IsOptional, IsString } from 'class-validator'; +import { Type } from 'class-transformer'; +import { and, desc, eq } from 'drizzle-orm'; +import { CurrentSession } from '../auth/session.decorator'; +import type { AuthenticatedSession } from '../auth/tenant.guard'; +import { db } from '../db/client'; +import { dataSources } from '../db/schema'; + +class CreateDataSourceDto { + @IsString() name!: string; + @IsOptional() @IsIn(['api','file','manual','database','integration','clickhouse','sheet']) sourceType?: string; + @IsOptional() @IsString() description?: string; + @IsOptional() @IsString() connectionInfo?: string; + @IsOptional() @IsInt() @Type(() => Number) recordCount?: number; +} + +class UpdateDataSourceDto { + @IsOptional() @IsString() name?: string; + @IsOptional() @IsString() description?: string; + @IsOptional() @IsIn(['active','paused','error','archived']) status?: string; + @IsOptional() @IsInt() @Type(() => Number) recordCount?: number; + @IsOptional() @IsString() connectionInfo?: string; +} + +@Controller('data-sources') +export class DataSourcesController { + @Get() + async list( + @CurrentSession() session: AuthenticatedSession, + @Query('status') status?: string, + ) { + const tid = session.tenantId; + let where = eq(dataSources.tenantId, tid) as ReturnType; + if (status) where = and(where, eq(dataSources.status, status)) as typeof where; + return db.query.dataSources.findMany({ where, orderBy: [desc(dataSources.createdAt)], limit: 200 }); + } + + @Post() + async create(@CurrentSession() session: AuthenticatedSession, @Body() dto: CreateDataSourceDto) { + const [row] = await db.insert(dataSources).values({ + tenantId: session.tenantId, + name: dto.name, + sourceType: dto.sourceType ?? 'manual', + description: dto.description, + connectionInfo: dto.connectionInfo, + recordCount: dto.recordCount ?? null, + }).returning(); + return row; + } + + @Patch(':id') + async update( + @CurrentSession() session: AuthenticatedSession, + @Param('id', ParseUUIDPipe) id: string, + @Body() dto: UpdateDataSourceDto, + ) { + const upd: Record = { updatedAt: new Date() }; + if (dto.name !== undefined) upd.name = dto.name; + if (dto.description !== undefined) upd.description = dto.description; + if (dto.status !== undefined) upd.status = dto.status; + if (dto.recordCount !== undefined) upd.recordCount = dto.recordCount; + if (dto.connectionInfo !== undefined) upd.connectionInfo = dto.connectionInfo; + if (dto.status === 'active') upd.lastSyncAt = new Date(); + const [row] = await db.update(dataSources) + .set(upd) + .where(and(eq(dataSources.id, id), eq(dataSources.tenantId, session.tenantId))) + .returning(); + return row; + } +}