because I got bored of customising my CV for every job
at main 85 lines 3.1 kB view raw
1import { randomBytes } from "node:crypto"; 2import { mkdirSync } from "node:fs"; 3import { join } from "node:path"; 4import { CVParserModule as CVParserCoreModule } from "@cv/ai-parser"; 5import { AIModule } from "@cv/ai-provider"; 6import { FileExtractionModule } from "@cv/file-upload"; 7import { DatabaseModule } from "@cv/system"; 8import { Module } from "@nestjs/common"; 9import { MulterModule } from "@nestjs/platform-express"; 10import { diskStorage } from "multer"; 11import { DataImportModule } from "@/modules/data-import/data-import.module"; 12import { FileImportSource } from "@/modules/data-import/sources/file-import-source"; 13import { EducationModule } from "@/modules/education/education.module"; 14import { EmploymentModule } from "@/modules/job-experience/employment/employment.module"; 15import { ProfileModule } from "@/modules/profile/profile.module"; 16import { UserAiSettingsModule } from "@/modules/user-settings/user-ai-settings.module"; 17import { AIProviderResolverService } from "./ai-provider-resolver.service"; 18import { CVParserResolver } from "./cv-parser.resolver"; 19import { CVParserService } from "./cv-parser.service"; 20import { EntityResolverService } from "./entity-resolver.service"; 21import { FileUploadController } from "./file-upload.controller"; 22import { UploadFileResolver } from "./graphql/upload-file.resolver"; 23import { ImportOnboardingStep } from "./onboarding/import.step"; 24 25@Module({ 26 imports: [ 27 CVParserCoreModule.forConfig(), 28 AIModule.forConfig(), 29 FileExtractionModule.forRoot(), 30 DatabaseModule, 31 UserAiSettingsModule, 32 ProfileModule, 33 EmploymentModule, 34 EducationModule, 35 DataImportModule, 36 MulterModule.register({ 37 storage: diskStorage({ 38 destination: (_req, _file, cb) => { 39 const tmpDir = join(process.cwd(), "tmp", "cv-uploads"); 40 mkdirSync(tmpDir, { recursive: true }); 41 cb(null, tmpDir); 42 }, 43 filename: (_req, file, cb) => { 44 const uniqueSuffix = `${Date.now()}-${randomBytes(8).toString("hex")}`; 45 const ext = file.originalname.split(".").pop() ?? ""; 46 cb(null, `${uniqueSuffix}.${ext}`); 47 }, 48 }), 49 limits: { 50 fileSize: 10 * 1024 * 1024, // 10MB 51 }, 52 fileFilter: (_req, file, cb) => { 53 const allowedMimes = [ 54 "application/pdf", 55 "application/vnd.openxmlformats-officedocument.wordprocessingml.document", 56 "text/plain", 57 "text/markdown", 58 ]; 59 60 if (allowedMimes.includes(file.mimetype)) { 61 cb(null, true); 62 } else { 63 cb( 64 new Error( 65 `File type ${file.mimetype} not allowed. Supported: PDF, DOCX, TXT, MD`, 66 ), 67 false, 68 ); 69 } 70 }, 71 }), 72 ], 73 providers: [ 74 EntityResolverService, 75 AIProviderResolverService, 76 CVParserService, 77 CVParserResolver, 78 UploadFileResolver, 79 ImportOnboardingStep, 80 FileImportSource, 81 ], 82 controllers: [FileUploadController], 83 exports: [CVParserService], 84}) 85export class CVParserModule {}