fix: generate CSV filename from company name instead of using multer callback
Some checks failed
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 1m32s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Successful in 11m35s
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Deployment Summary (push) Has been skipped
CI/CD Pipeline / Discord Notification (Failure) (push) Has been skipped
CI/CD Pipeline / Discord Notification (Success) (push) Has been skipped
Some checks failed
CI/CD Pipeline / Backend - Build, Test & Push (push) Failing after 1m32s
CI/CD Pipeline / Frontend - Build, Test & Push (push) Successful in 11m35s
CI/CD Pipeline / Integration Tests (push) Has been skipped
CI/CD Pipeline / Deployment Summary (push) Has been skipped
CI/CD Pipeline / Discord Notification (Failure) (push) Has been skipped
CI/CD Pipeline / Discord Notification (Success) (push) Has been skipped
Fixed CSV file upload to properly generate filename based on company name. The previous implementation tried to read `req.body.companyName` in multer's filename callback, but the body is not yet parsed at that point, causing files to be named "unknown.csv". ## Solution 1. Use temporary filename during upload (timestamp + random) 2. After validation and parsing, rename file to proper company name format 3. Delete old file if it exists before renaming 4. Store final filename in database configuration ## Changes - Multer filename callback now generates temporary filename - Added file renaming logic after successful validation - Updated database records to use final filename instead of temp name - Added logging for file operations ## Impact - New CSV uploads will have correct filenames (e.g., "ssc-consolidation.csv") - No more "unknown.csv" files - Existing "unknown.csv" needs to be manually deleted via dashboard 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aeb3d2a75d
commit
f5eabf4861
@ -70,14 +70,12 @@ export class CsvRatesAdminController {
|
|||||||
storage: diskStorage({
|
storage: diskStorage({
|
||||||
destination: './apps/backend/src/infrastructure/storage/csv-storage/rates',
|
destination: './apps/backend/src/infrastructure/storage/csv-storage/rates',
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
// Generate filename: company-name.csv
|
// Use timestamp + random string to avoid conflicts
|
||||||
const companyName = req.body.companyName || 'unknown';
|
// We'll rename it later once we have the company name from req.body
|
||||||
const sanitized = companyName
|
const timestamp = Date.now();
|
||||||
.toLowerCase()
|
const randomStr = Math.random().toString(36).substring(7);
|
||||||
.replace(/\s+/g, '-')
|
const tempFilename = `temp-${timestamp}-${randomStr}.csv`;
|
||||||
.replace(/[^a-z0-9-]/g, '');
|
cb(null, tempFilename);
|
||||||
const filename = `${sanitized}.csv`;
|
|
||||||
cb(null, filename);
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
fileFilter: (req, file, cb) => {
|
fileFilter: (req, file, cb) => {
|
||||||
@ -147,9 +145,16 @@ export class CsvRatesAdminController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Generate final filename based on company name
|
||||||
|
const sanitizedCompanyName = dto.companyName
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
.replace(/[^a-z0-9-]/g, '');
|
||||||
|
const finalFilename = `${sanitizedCompanyName}.csv`;
|
||||||
|
|
||||||
// Auto-convert CSV if needed (FOB FRET → Standard format)
|
// Auto-convert CSV if needed (FOB FRET → Standard format)
|
||||||
const conversionResult = await this.csvConverter.autoConvert(file.path, dto.companyName);
|
const conversionResult = await this.csvConverter.autoConvert(file.path, dto.companyName);
|
||||||
const filePathToValidate = conversionResult.convertedPath;
|
let filePathToValidate = conversionResult.convertedPath;
|
||||||
|
|
||||||
if (conversionResult.wasConverted) {
|
if (conversionResult.wasConverted) {
|
||||||
this.logger.log(
|
this.logger.log(
|
||||||
@ -177,13 +182,28 @@ export class CsvRatesAdminController {
|
|||||||
|
|
||||||
this.logger.log(`Successfully parsed ${ratesCount} rates from ${file.filename}`);
|
this.logger.log(`Successfully parsed ${ratesCount} rates from ${file.filename}`);
|
||||||
|
|
||||||
|
// Rename file to final name (company-name.csv)
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const finalPath = path.join(path.dirname(filePathToValidate), finalFilename);
|
||||||
|
|
||||||
|
// Delete old file if exists
|
||||||
|
if (fs.existsSync(finalPath)) {
|
||||||
|
fs.unlinkSync(finalPath);
|
||||||
|
this.logger.log(`Deleted old file: ${finalFilename}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rename temp file to final name
|
||||||
|
fs.renameSync(filePathToValidate, finalPath);
|
||||||
|
this.logger.log(`Renamed ${file.filename} to ${finalFilename}`);
|
||||||
|
|
||||||
// Check if config exists for this company
|
// Check if config exists for this company
|
||||||
const existingConfig = await this.csvConfigRepository.findByCompanyName(dto.companyName);
|
const existingConfig = await this.csvConfigRepository.findByCompanyName(dto.companyName);
|
||||||
|
|
||||||
if (existingConfig) {
|
if (existingConfig) {
|
||||||
// Update existing configuration
|
// Update existing configuration
|
||||||
await this.csvConfigRepository.update(existingConfig.id, {
|
await this.csvConfigRepository.update(existingConfig.id, {
|
||||||
csvFilePath: file.filename,
|
csvFilePath: finalFilename,
|
||||||
uploadedAt: new Date(),
|
uploadedAt: new Date(),
|
||||||
uploadedBy: user.id,
|
uploadedBy: user.id,
|
||||||
rowCount: ratesCount,
|
rowCount: ratesCount,
|
||||||
@ -204,7 +224,7 @@ export class CsvRatesAdminController {
|
|||||||
// Create new configuration
|
// Create new configuration
|
||||||
await this.csvConfigRepository.create({
|
await this.csvConfigRepository.create({
|
||||||
companyName: dto.companyName,
|
companyName: dto.companyName,
|
||||||
csvFilePath: file.filename,
|
csvFilePath: finalFilename,
|
||||||
type: 'CSV_ONLY',
|
type: 'CSV_ONLY',
|
||||||
hasApi: false,
|
hasApi: false,
|
||||||
apiConnector: null,
|
apiConnector: null,
|
||||||
@ -226,7 +246,7 @@ export class CsvRatesAdminController {
|
|||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
ratesCount,
|
ratesCount,
|
||||||
csvFilePath: file.filename,
|
csvFilePath: finalFilename,
|
||||||
companyName: dto.companyName,
|
companyName: dto.companyName,
|
||||||
uploadedAt: new Date(),
|
uploadedAt: new Date(),
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user