import { Injectable, Logger } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { S3Service } from '../s3/s3.service'; import { UpdateDocumentDto } from '../dto/update-document.dto'; import { CreateUserDto } from '../dto/create-user.dto'; import * as bcrypt from 'bcrypt'; import { EmailService } from '../email/email.service'; @Injectable() export class AdminService { private readonly logger = new Logger(AdminService.name); constructor( private readonly prisma: PrismaService, private readonly s3Service: S3Service, private readonly emailService: EmailService, ) {} async getAllDocuments() { return this.prisma.document.findMany({ include: { uploadedBy: { select: { id: true, name: true, email: true, }, }, sharedWith: { select: { id: true, name: true, email: true, }, }, }, orderBy: { createdAt: 'desc', }, }); } async getAllUsers() { return this.prisma.user.findMany(); } async createUser(createUserDto: CreateUserDto) { const hashedPassword = await bcrypt.hash(createUserDto.password, 10); return this.prisma.user.create({ data: { ...createUserDto, password: hashedPassword, }, }); } async updateDocument( id: number, updateDocumentDto: UpdateDocumentDto, file?: Express.Multer.File, ) { let s3Key = undefined; if (file) { s3Key = await this.s3Service.uploadFile(file, 'documents'); } return this.prisma.document.update({ where: { id }, data: { ...updateDocumentDto, ...(s3Key && { s3Key }), }, }); } async shareDocument(documentId: number, userId: number) { this.logger.log('=== Starting document share process ==='); console.log('=== Starting document share process ==='); this.logger.debug('Share request:', { documentId, userId }); console.log('Share request:', { documentId, userId }); try { // Get the document with its current data const document = await this.prisma.document.findUnique({ where: { id: documentId }, include: { uploadedBy: { select: { id: true, name: true, email: true, }, }, }, }); if (!document) { this.logger.error('Document not found:', { documentId }); console.error('Document not found:', { documentId }); throw new Error('Document not found'); } // Get the user we're sharing with const shareUser = await this.prisma.user.findUnique({ where: { id: userId }, select: { id: true, name: true, email: true, }, }); if (!shareUser) { this.logger.error('User not found:', { userId }); console.error('User not found:', { userId }); throw new Error('User not found'); } // Update the document sharing const updatedDocument = await this.prisma.document.update({ where: { id: documentId }, data: { sharedWith: { connect: { id: userId }, }, }, include: { uploadedBy: { select: { id: true, name: true, email: true, }, }, sharedWith: { select: { id: true, name: true, email: true, }, }, }, }); // Send email notification to the user we're sharing with this.logger.log('Sending email notification to shared user:', { userId: shareUser.id, email: shareUser.email, name: shareUser.name, documentTitle: document.title }); console.log('Sending email notification to shared user:', { userId: shareUser.id, email: shareUser.email, name: shareUser.name, documentTitle: document.title }); try { console.log('Attempting to send email notification...'); await this.emailService.sendDocumentNotification( shareUser.email, shareUser.name, document.title, 'shared' ); console.log('Email notification sent successfully'); this.logger.log('Email notification sent successfully'); } catch (emailError) { // Log the full error details console.error('Failed to send email notification:', emailError); this.logger.error('Failed to send email notification:', { error: emailError.message, code: emailError.code, command: emailError.command, response: emailError.response, responseCode: emailError.responseCode, stack: emailError.stack, }); // Don't throw the error, just log it and continue } this.logger.log('=== Document share process completed ==='); console.log('=== Document share process completed ==='); return updatedDocument; } catch (error) { this.logger.error('Error in shareDocument:', { error: error.message, code: error.code, stack: error.stack, }); console.error('Error in shareDocument:', error); throw error; } } async updateDocumentStatus(documentId: number, status: string) { return this.prisma.document.update({ where: { id: documentId }, data: { status }, }); } async uploadDocument( file: Express.Multer.File, title: string, sharedWithId: number, uploadedById: number ) { this.logger.log('=== Starting document upload process ==='); this.logger.debug('Upload parameters:', { title, sharedWithId, uploadedById, fileName: file.originalname, fileSize: file.size, }); try { this.logger.debug('Uploading file to S3...'); const s3Key = await this.s3Service.uploadFile(file, 'documents'); this.logger.debug(`File uploaded to S3 successfully with key: ${s3Key}`); this.logger.debug('Creating document record in database...'); const document = await this.prisma.document.create({ data: { title, s3Key, status: 'pending', sharedWith: { connect: { id: sharedWithId } }, uploadedBy: { connect: { id: uploadedById } } }, include: { uploadedBy: { select: { id: true, name: true, email: true, }, }, sharedWith: { select: { id: true, name: true, email: true, }, }, }, }); this.logger.debug('Document record created:', { id: document.id, title: document.title, uploadedBy: document.uploadedBy, sharedWith: document.sharedWith, }); // Send email notifications this.logger.log('=== Starting email notification process ==='); // Notify the user who the document is shared with const sharedUser = document.sharedWith[0]; if (sharedUser) { this.logger.debug(`Preparing to send notification to shared user:`, { id: sharedUser.id, email: sharedUser.email, name: sharedUser.name, }); try { this.logger.debug('Calling EmailService.sendDocumentNotification for shared user...'); await this.emailService.sendDocumentNotification( sharedUser.email, sharedUser.name, document.title, 'shared' ); this.logger.debug('Shared user notification sent successfully'); } catch (error) { this.logger.error('Failed to send notification to shared user:', { error: error.message, code: error.code, command: error.command, stack: error.stack, }); // Log the email service instance to verify it's properly injected this.logger.debug('EmailService instance:', { exists: !!this.emailService, type: typeof this.emailService, methods: Object.keys(Object.getPrototypeOf(this.emailService)), }); } } else { this.logger.warn('No shared user found in the document record'); } // Notify the uploader const uploader = document.uploadedBy; if (uploader) { this.logger.debug(`Preparing to send notification to uploader:`, { id: uploader.id, email: uploader.email, name: uploader.name, }); try { this.logger.debug('Calling EmailService.sendDocumentNotification for uploader...'); await this.emailService.sendDocumentNotification( uploader.email, uploader.name, document.title, 'uploaded' ); this.logger.debug('Uploader notification sent successfully'); } catch (error) { this.logger.error('Failed to send notification to uploader:', { error: error.message, code: error.code, command: error.command, stack: error.stack, }); // Log the email service instance to verify it's properly injected this.logger.debug('EmailService instance:', { exists: !!this.emailService, type: typeof this.emailService, methods: Object.keys(Object.getPrototypeOf(this.emailService)), }); } } else { this.logger.warn('No uploader found in the document record'); } this.logger.log('=== Document upload process completed ==='); return document; } catch (error) { this.logger.error('Error in uploadDocument:', { error: error.message, code: error.code, stack: error.stack, }); throw error; } } }