import { Controller, Post, Get, Query, UseGuards, UseInterceptors, UploadedFile, Res, BadRequestException, } from "@nestjs/common"; import { FileInterceptor } from "@nestjs/platform-express"; import { Response } from "express"; import { ImportExportService } from "./services/import-export.service"; import { ExportQueryDto, ExportFormat, ImportResponseDto } from "./dto"; import { AuthGuard } from "../auth/guards/auth.guard"; import { WorkspaceGuard, PermissionGuard } from "../common/guards"; import { Workspace, Permission, RequirePermission } from "../common/decorators"; import { CurrentUser } from "../auth/decorators/current-user.decorator"; /** * Controller for knowledge import/export endpoints * All endpoints require authentication and workspace context */ @Controller("knowledge") @UseGuards(AuthGuard, WorkspaceGuard, PermissionGuard) export class ImportExportController { constructor(private readonly importExportService: ImportExportService) {} /** * POST /api/knowledge/import * Import knowledge entries from uploaded file (.md or .zip) * Requires: MEMBER role or higher */ @Post("import") @RequirePermission(Permission.WORKSPACE_MEMBER) @UseInterceptors(FileInterceptor("file")) async importEntries( @Workspace() workspaceId: string, @CurrentUser() user: any, @UploadedFile() file: Express.Multer.File ): Promise { if (!file) { throw new BadRequestException("No file uploaded"); } const result = await this.importExportService.importEntries( workspaceId, user.id, file ); return { success: result.failed === 0, totalFiles: result.totalFiles, imported: result.imported, failed: result.failed, results: result.results, }; } /** * GET /api/knowledge/export * Export knowledge entries as a zip file * Query params: * - format: 'markdown' (default) or 'json' * - entryIds: optional array of entry IDs to export (exports all if not provided) * Requires: Any workspace member */ @Get("export") @RequirePermission(Permission.WORKSPACE_ANY) async exportEntries( @Workspace() workspaceId: string, @Query() query: ExportQueryDto, @Res() res: Response ): Promise { const format = query.format || ExportFormat.MARKDOWN; const entryIds = query.entryIds; const { stream, filename } = await this.importExportService.exportEntries( workspaceId, format, entryIds ); // Set response headers res.setHeader("Content-Type", "application/zip"); res.setHeader("Content-Disposition", `attachment; filename="${filename}"`); // Pipe the archive stream to response stream.pipe(res); } }