This commit is contained in:
@@ -64,11 +64,16 @@ describe('FileStorageService', () => {
|
||||
attachmentRepo = module.get(getRepositoryToken(Attachment));
|
||||
|
||||
jest.clearAllMocks();
|
||||
(fs.ensureDirSync as jest.Mock).mockReturnValue(true);
|
||||
(fs.writeFile as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.pathExists as jest.Mock).mockResolvedValue(true);
|
||||
(fs.move as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.remove as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.ensureDirSync as unknown as jest.Mock).mockReturnValue(true);
|
||||
(fs.writeFile as unknown as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.pathExists as unknown as jest.Mock).mockResolvedValue(true);
|
||||
(fs.move as unknown as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.remove as unknown as jest.Mock).mockResolvedValue(undefined);
|
||||
(fs.readFile as unknown as jest.Mock).mockResolvedValue(
|
||||
Buffer.from('test')
|
||||
);
|
||||
(fs.stat as unknown as jest.Mock).mockResolvedValue({ size: 1024 });
|
||||
(fs.ensureDir as unknown as jest.Mock).mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
@@ -86,7 +91,7 @@ describe('FileStorageService', () => {
|
||||
});
|
||||
|
||||
it('should throw BadRequestException if write fails', async () => {
|
||||
(fs.writeFile as jest.Mock).mockRejectedValueOnce(
|
||||
(fs.writeFile as unknown as jest.Mock).mockRejectedValueOnce(
|
||||
new Error('Write error')
|
||||
);
|
||||
await expect(service.upload(mockFile, 1)).rejects.toThrow(
|
||||
|
||||
@@ -201,6 +201,77 @@ export class FileStorageService {
|
||||
return crypto.createHash('sha256').update(buffer).digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ NEW: Import Staging File (For Legacy Migration)
|
||||
* ย้ายไฟล์จาก staging_ai ไปยัง permanent storage โดยตรง
|
||||
*/
|
||||
async importStagingFile(
|
||||
sourceFilePath: string,
|
||||
userId: number,
|
||||
options?: { issueDate?: Date; documentType?: string }
|
||||
): Promise<Attachment> {
|
||||
if (!(await fs.pathExists(sourceFilePath))) {
|
||||
this.logger.error(`Staging file not found: ${sourceFilePath}`);
|
||||
throw new NotFoundException(`Source file not found: ${sourceFilePath}`);
|
||||
}
|
||||
|
||||
// 1. Get file stats & checksum
|
||||
const stats = await fs.stat(sourceFilePath);
|
||||
const fileExt = path.extname(sourceFilePath);
|
||||
const originalFilename = path.basename(sourceFilePath);
|
||||
const storedFilename = `${uuidv4()}${fileExt}`;
|
||||
|
||||
// Determine mime type basic
|
||||
let mimeType = 'application/octet-stream';
|
||||
if (fileExt.toLowerCase() === '.pdf') mimeType = 'application/pdf';
|
||||
else if (fileExt.toLowerCase() === '.xlsx')
|
||||
mimeType =
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
|
||||
|
||||
const fileBuffer = await fs.readFile(sourceFilePath);
|
||||
const checksum = this.calculateChecksum(fileBuffer);
|
||||
|
||||
// 2. Generate Permanent Path
|
||||
const refDate = options?.issueDate || new Date();
|
||||
const effectiveDate = isNaN(refDate.getTime()) ? new Date() : refDate;
|
||||
const year = effectiveDate.getFullYear().toString();
|
||||
const month = (effectiveDate.getMonth() + 1).toString().padStart(2, '0');
|
||||
const docTypeFolder = options?.documentType || 'General';
|
||||
|
||||
const permanentDir = path.join(
|
||||
this.permanentDir,
|
||||
docTypeFolder,
|
||||
year,
|
||||
month
|
||||
);
|
||||
await fs.ensureDir(permanentDir);
|
||||
|
||||
const newPath = path.join(permanentDir, storedFilename);
|
||||
|
||||
// 3. Move File
|
||||
try {
|
||||
await fs.move(sourceFilePath, newPath, { overwrite: true });
|
||||
} catch (error) {
|
||||
this.logger.error(`Failed to move staging file to ${newPath}`, error);
|
||||
throw new BadRequestException('Failed to process staging file');
|
||||
}
|
||||
|
||||
// 4. Create Database Record
|
||||
const attachment = this.attachmentRepository.create({
|
||||
originalFilename,
|
||||
storedFilename,
|
||||
filePath: newPath,
|
||||
mimeType,
|
||||
fileSize: stats.size,
|
||||
isTemporary: false,
|
||||
referenceDate: effectiveDate,
|
||||
checksum,
|
||||
uploadedByUserId: userId,
|
||||
});
|
||||
|
||||
return this.attachmentRepository.save(attachment);
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ NEW: Delete File
|
||||
* ลบไฟล์ออกจาก Disk และ Database
|
||||
|
||||
Reference in New Issue
Block a user