import { randomBytes } from "node:crypto"; import { mkdirSync } from "node:fs"; import { join } from "node:path"; import { CVParserModule as CVParserCoreModule } from "@cv/ai-parser"; import { AIModule } from "@cv/ai-provider"; import { FileExtractionModule } from "@cv/file-upload"; import { DatabaseModule } from "@cv/system"; import { Module } from "@nestjs/common"; import { MulterModule } from "@nestjs/platform-express"; import { diskStorage } from "multer"; import { DataImportModule } from "@/modules/data-import/data-import.module"; import { FileImportSource } from "@/modules/data-import/sources/file-import-source"; import { EducationModule } from "@/modules/education/education.module"; import { EmploymentModule } from "@/modules/job-experience/employment/employment.module"; import { ProfileModule } from "@/modules/profile/profile.module"; import { UserAiSettingsModule } from "@/modules/user-settings/user-ai-settings.module"; import { AIProviderResolverService } from "./ai-provider-resolver.service"; import { CVParserResolver } from "./cv-parser.resolver"; import { CVParserService } from "./cv-parser.service"; import { EntityResolverService } from "./entity-resolver.service"; import { FileUploadController } from "./file-upload.controller"; import { UploadFileResolver } from "./graphql/upload-file.resolver"; import { ImportOnboardingStep } from "./onboarding/import.step"; @Module({ imports: [ CVParserCoreModule.forConfig(), AIModule.forConfig(), FileExtractionModule.forRoot(), DatabaseModule, UserAiSettingsModule, ProfileModule, EmploymentModule, EducationModule, DataImportModule, MulterModule.register({ storage: diskStorage({ destination: (_req, _file, cb) => { const tmpDir = join(process.cwd(), "tmp", "cv-uploads"); mkdirSync(tmpDir, { recursive: true }); cb(null, tmpDir); }, filename: (_req, file, cb) => { const uniqueSuffix = `${Date.now()}-${randomBytes(8).toString("hex")}`; const ext = file.originalname.split(".").pop() ?? ""; cb(null, `${uniqueSuffix}.${ext}`); }, }), limits: { fileSize: 10 * 1024 * 1024, // 10MB }, fileFilter: (_req, file, cb) => { const allowedMimes = [ "application/pdf", "application/vnd.openxmlformats-officedocument.wordprocessingml.document", "text/plain", "text/markdown", ]; if (allowedMimes.includes(file.mimetype)) { cb(null, true); } else { cb( new Error( `File type ${file.mimetype} not allowed. Supported: PDF, DOCX, TXT, MD`, ), false, ); } }, }), ], providers: [ EntityResolverService, AIProviderResolverService, CVParserService, CVParserResolver, UploadFileResolver, ImportOnboardingStep, FileImportSource, ], controllers: [FileUploadController], exports: [CVParserService], }) export class CVParserModule {}