import { Inject, Injectable } from '@nestjs/common'; import { Pothos, PothosRef, PothosSchema, SchemaBuilderToken, } from '@smatch-corp/nestjs-pothos'; import { Builder } from '../Graphql/graphql.builder'; import { PrismaService } from '../Prisma/prisma.service'; import { MinioService } from 'src/Minio/minio.service'; import { UploadedFileType } from '@prisma/client'; @Injectable() export class UploadedFileSchema extends PothosSchema { constructor( @Inject(SchemaBuilderToken) private readonly builder: Builder, private readonly prisma: PrismaService, private readonly minioService: MinioService, ) { super(); } @PothosRef() uploadedFile() { return this.builder.prismaObject('UploadedFile', { description: 'A file uploaded by a user.', fields: (t) => ({ id: t.exposeID('id', { description: 'The ID of the uploaded file.', }), userId: t.exposeID('userId', { description: 'The ID of the user who uploaded the file.', }), actualFileName: t.exposeString('actualFileName', { description: 'The original name of the file.', }), fileName: t.exposeString('fileName', { description: 'The name of the file in minio.', }), fileType: t.expose('fileType', { type: UploadedFileType, nullable: true, description: 'The type of the file.', }), fileUrl: t.exposeString('fileUrl', { description: 'The URL of the file.', }), uploadedAt: t.expose('uploadedAt', { type: 'DateTime', nullable: true, description: 'The date and time the file was uploaded.', }), user: t.relation('user', { description: 'The user who uploaded the file.', }), center: t.relation('center', { description: 'The center that the file belongs to.', }), service: t.relation('service', { description: 'The service that the file belongs to.', }), workshop: t.relation('workshop', { description: 'The workshop that the file belongs to.', }), }), }); } @Pothos() init(): void { this.builder.queryFields((t) => ({ uploadedFile: t.prismaField({ description: 'Retrieve a single uploaded file by its unique identifier.', type: this.uploadedFile(), args: this.builder.generator.findUniqueArgs('UploadedFile'), resolve: async (query, root, args) => { const file = await this.prisma.uploadedFile.findUnique({ ...query, where: args.where, }); if (!file) { throw new Error('File not found'); } const fileUrl = await this.minioService.getFileUrl(file.id, 'files'); if (!fileUrl) { throw new Error('Cannot retrieve file url'); } file.fileUrl = fileUrl; return file; }, }), uploadedFiles: t.prismaField({ description: 'Retrieve a list of uploaded files with optional filtering, ordering, and pagination.', type: [this.uploadedFile()], args: this.builder.generator.findManyArgs('UploadedFile'), resolve: async (query, root, args) => { const files = await this.prisma.uploadedFile.findMany({ ...query, skip: args.skip ?? undefined, take: args.take ?? undefined, orderBy: args.orderBy ?? undefined, where: args.filter ?? undefined, }); const fileUrls = await Promise.all( files.map((file) => this.minioService.getFileUrl(file.id, 'files')), ); return files.map((file, index) => ({ ...file, fileUrl: fileUrls[index] ?? '', })); }, }), })); // Mutations section this.builder.mutationFields((t) => ({ singleUpload: t.prismaField({ description: 'Upload a single file for a user.', type: this.uploadedFile(), args: { userId: t.arg({ type: 'String', required: true, }), file: t.arg({ type: 'Upload', required: true, }), fileType: t.arg({ type: UploadedFileType, required: true, }), }, resolve: async (query, root, args) => { const user = await this.prisma.user.findUnique({ where: { id: args.userId, }, }); if (!user) { throw new Error('User not found'); } const { filename, mimetype, actualFileName } = await this.minioService.uploadFile(args.file, 'files'); if (!mimetype) { throw new Error('File type not supported'); } const fileUrl = await this.minioService.getFileUrl(filename, 'files'); if (!fileUrl) { throw new Error('Cannot retrieve file url, please try again later'); } const uploadedFile = await this.prisma.uploadedFile.create({ data: { userId: user.id, fileName: filename, actualFileName: actualFileName, type: mimetype, fileType: args.fileType, fileUrl: fileUrl ?? '', uploadedAt: new Date(), }, }); return uploadedFile; }, }), multipleUpload: t.prismaField({ description: 'Upload multiple files for a user.', type: [this.uploadedFile()], args: { userId: t.arg({ type: 'String', required: true, }), files: t.arg({ type: ['Upload'], required: true, }), fileType: t.arg({ type: UploadedFileType, required: true, }), }, resolve: async (query, root, args) => { const user = await this.prisma.user.findUnique({ where: { id: args.userId, }, }); if (!user) { throw new Error('User not found'); } const uploadedFiles = await Promise.all( args.files.map((file) => this.minioService.uploadFile(file, 'files'), ), ); // get file urls const fileUrls = await Promise.all( uploadedFiles.map((file) => this.minioService.getFileUrl(file.filename, 'files'), ), ); // map uploadedFiles to db const dbFiles = uploadedFiles.map((file, index) => ({ userId: user.id, fileName: file.filename, type: file.mimetype, fileType: args.fileType, actualFileName: file.actualFileName, fileUrl: fileUrls[index] ?? '', uploadedAt: new Date(), })); // create files in db const createdFiles = await this.prisma.uploadedFile.createManyAndReturn({ data: dbFiles, }); return createdFiles; }, }), deleteUploadedFile: t.prismaField({ description: 'Delete a single uploaded file by its unique identifier.', type: this.uploadedFile(), args: { id: t.arg({ type: 'String', required: true, }), }, resolve: async (query, root, args) => { const file = await this.prisma.uploadedFile.findUnique({ where: { id: args.id, }, }); if (!file) { throw new Error('File not found'); } await this.minioService.deleteFile(file.fileName, 'files'); await this.prisma.uploadedFile.delete({ where: { id: file.id, }, }); return file; }, }), deleteUploadedFiles: t.prismaField({ description: 'Delete multiple uploaded files by their unique identifiers.', type: [this.uploadedFile()], args: { ids: t.arg({ type: ['String'], required: true, }), }, resolve: async (query, root, args) => { const files = await this.prisma.uploadedFile.findMany({ where: { id: { in: args.ids, }, }, }); await this.prisma.uploadedFile.deleteMany({ where: { id: { in: args.ids, }, }, }); await Promise.all( files.map((file) => this.minioService.deleteFile(file.fileName, 'files'), ), ); return files; }, }), })); } }