177 lines
5.8 KiB
JavaScript
177 lines
5.8 KiB
JavaScript
/**
|
|
* Script to restore document references in database from MinIO files
|
|
*
|
|
* Scans MinIO for existing files and creates/updates database references
|
|
*/
|
|
|
|
const { S3Client, ListObjectsV2Command, HeadObjectCommand } = require('@aws-sdk/client-s3');
|
|
const { Client } = require('pg');
|
|
const { v4: uuidv4 } = require('uuid');
|
|
require('dotenv').config();
|
|
|
|
const MINIO_ENDPOINT = process.env.AWS_S3_ENDPOINT || 'http://localhost:9000';
|
|
const BUCKET_NAME = 'xpeditis-documents';
|
|
|
|
// Initialize MinIO client
|
|
const s3Client = new S3Client({
|
|
region: 'us-east-1',
|
|
endpoint: MINIO_ENDPOINT,
|
|
credentials: {
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID || 'minioadmin',
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || 'minioadmin',
|
|
},
|
|
forcePathStyle: true,
|
|
});
|
|
|
|
async function restoreDocumentReferences() {
|
|
const pgClient = new Client({
|
|
host: process.env.DATABASE_HOST || 'localhost',
|
|
port: process.env.DATABASE_PORT || 5432,
|
|
user: process.env.DATABASE_USER || 'xpeditis',
|
|
password: process.env.DATABASE_PASSWORD || 'xpeditis_dev_password',
|
|
database: process.env.DATABASE_NAME || 'xpeditis_dev',
|
|
});
|
|
|
|
try {
|
|
await pgClient.connect();
|
|
console.log('✅ Connected to database\n');
|
|
|
|
// Get all MinIO files
|
|
console.log('📋 Listing files in MinIO...');
|
|
let allFiles = [];
|
|
let continuationToken = null;
|
|
|
|
do {
|
|
const command = new ListObjectsV2Command({
|
|
Bucket: BUCKET_NAME,
|
|
ContinuationToken: continuationToken,
|
|
});
|
|
|
|
const response = await s3Client.send(command);
|
|
|
|
if (response.Contents) {
|
|
allFiles = allFiles.concat(response.Contents);
|
|
}
|
|
|
|
continuationToken = response.NextContinuationToken;
|
|
} while (continuationToken);
|
|
|
|
console.log(` Found ${allFiles.length} files in MinIO\n`);
|
|
|
|
// Group files by booking ID
|
|
const filesByBooking = {};
|
|
allFiles.forEach(file => {
|
|
const parts = file.Key.split('/');
|
|
if (parts.length >= 3 && parts[0] === 'csv-bookings') {
|
|
const bookingId = parts[1];
|
|
const documentId = parts[2].split('-')[0]; // Extract UUID from filename
|
|
const fileName = parts[2].substring(37); // Remove UUID prefix (36 chars + dash)
|
|
|
|
if (!filesByBooking[bookingId]) {
|
|
filesByBooking[bookingId] = [];
|
|
}
|
|
|
|
filesByBooking[bookingId].push({
|
|
key: file.Key,
|
|
documentId: documentId,
|
|
fileName: fileName,
|
|
size: file.Size,
|
|
lastModified: file.LastModified,
|
|
});
|
|
}
|
|
});
|
|
|
|
console.log(`📦 Found files for ${Object.keys(filesByBooking).length} bookings\n`);
|
|
|
|
let updatedCount = 0;
|
|
let createdDocsCount = 0;
|
|
|
|
for (const [bookingId, files] of Object.entries(filesByBooking)) {
|
|
// Check if booking exists
|
|
const bookingResult = await pgClient.query(
|
|
'SELECT id, documents FROM csv_bookings WHERE id = $1',
|
|
[bookingId]
|
|
);
|
|
|
|
if (bookingResult.rows.length === 0) {
|
|
console.log(`⚠️ Booking not found: ${bookingId.substring(0, 8)}... (skipping)`);
|
|
continue;
|
|
}
|
|
|
|
const booking = bookingResult.rows[0];
|
|
const existingDocs = booking.documents || [];
|
|
|
|
console.log(`\n📦 Booking: ${bookingId.substring(0, 8)}...`);
|
|
console.log(` Existing documents in DB: ${existingDocs.length}`);
|
|
console.log(` Files in MinIO: ${files.length}`);
|
|
|
|
// Create document references for files
|
|
const newDocuments = files.map(file => {
|
|
// Determine MIME type from file extension
|
|
const ext = file.fileName.split('.').pop().toLowerCase();
|
|
const mimeTypeMap = {
|
|
pdf: 'application/pdf',
|
|
png: 'image/png',
|
|
jpg: 'image/jpeg',
|
|
jpeg: 'image/jpeg',
|
|
txt: 'text/plain',
|
|
};
|
|
const mimeType = mimeTypeMap[ext] || 'application/octet-stream';
|
|
|
|
// Determine document type
|
|
let docType = 'OTHER';
|
|
if (file.fileName.toLowerCase().includes('bill-of-lading') || file.fileName.toLowerCase().includes('bol')) {
|
|
docType = 'BILL_OF_LADING';
|
|
} else if (file.fileName.toLowerCase().includes('packing-list')) {
|
|
docType = 'PACKING_LIST';
|
|
} else if (file.fileName.toLowerCase().includes('commercial-invoice') || file.fileName.toLowerCase().includes('invoice')) {
|
|
docType = 'COMMERCIAL_INVOICE';
|
|
}
|
|
|
|
const doc = {
|
|
id: file.documentId,
|
|
type: docType,
|
|
fileName: file.fileName,
|
|
filePath: `${MINIO_ENDPOINT}/${BUCKET_NAME}/${file.key}`,
|
|
mimeType: mimeType,
|
|
size: file.size,
|
|
uploadedAt: file.lastModified.toISOString(),
|
|
};
|
|
|
|
console.log(` ✅ ${file.fileName} (${(file.size / 1024).toFixed(2)} KB)`);
|
|
return doc;
|
|
});
|
|
|
|
// Update the booking with new document references
|
|
await pgClient.query(
|
|
'UPDATE csv_bookings SET documents = $1 WHERE id = $2',
|
|
[JSON.stringify(newDocuments), bookingId]
|
|
);
|
|
|
|
updatedCount++;
|
|
createdDocsCount += newDocuments.length;
|
|
}
|
|
|
|
console.log(`\n📊 Summary:`);
|
|
console.log(` Bookings updated: ${updatedCount}`);
|
|
console.log(` Document references created: ${createdDocsCount}`);
|
|
console.log(`\n✅ Document references restored`);
|
|
} catch (error) {
|
|
console.error('❌ Error:', error);
|
|
throw error;
|
|
} finally {
|
|
await pgClient.end();
|
|
console.log('\n👋 Disconnected from database');
|
|
}
|
|
}
|
|
|
|
restoreDocumentReferences()
|
|
.then(() => {
|
|
console.log('\n✅ Script completed successfully');
|
|
process.exit(0);
|
|
})
|
|
.catch((error) => {
|
|
console.error('\n❌ Script failed:', error);
|
|
process.exit(1);
|
|
});
|