More data management page tweaks, ensure reusable images don't get deleted automatically
This commit is contained in:
@@ -184,7 +184,7 @@ async function resetDatabase() {
|
||||
SELECT string_agg(tablename, ', ') as tables
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename NOT IN ('users', 'permissions', 'user_permissions', 'calculate_history', 'import_history', 'ai_prompts', 'ai_validation_performance', 'templates');
|
||||
AND tablename NOT IN ('users', 'permissions', 'user_permissions', 'calculate_history', 'import_history', 'ai_prompts', 'ai_validation_performance', 'templates', 'reusable_images');
|
||||
`);
|
||||
|
||||
if (!tablesResult.rows[0].tables) {
|
||||
@@ -204,7 +204,7 @@ async function resetDatabase() {
|
||||
// Drop all tables except users
|
||||
const tables = tablesResult.rows[0].tables.split(', ');
|
||||
for (const table of tables) {
|
||||
if (!['users'].includes(table)) {
|
||||
if (!['users', 'reusable_images'].includes(table)) {
|
||||
await client.query(`DROP TABLE IF EXISTS "${table}" CASCADE`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,19 @@ const METRICS_TABLES = [
|
||||
'vendor_details'
|
||||
];
|
||||
|
||||
// Tables to always protect from being dropped
|
||||
const PROTECTED_TABLES = [
|
||||
'users',
|
||||
'permissions',
|
||||
'user_permissions',
|
||||
'calculate_history',
|
||||
'import_history',
|
||||
'ai_prompts',
|
||||
'ai_validation_performance',
|
||||
'templates',
|
||||
'reusable_images'
|
||||
];
|
||||
|
||||
// Split SQL into individual statements
|
||||
function splitSQLStatements(sql) {
|
||||
sql = sql.replace(/\r\n/g, '\n');
|
||||
@@ -109,7 +122,8 @@ async function resetMetrics() {
|
||||
FROM pg_tables
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename = ANY($1)
|
||||
`, [METRICS_TABLES]);
|
||||
AND tablename NOT IN (SELECT unnest($2::text[]))
|
||||
`, [METRICS_TABLES, PROTECTED_TABLES]);
|
||||
|
||||
outputProgress({
|
||||
operation: 'Initial state',
|
||||
@@ -126,6 +140,15 @@ async function resetMetrics() {
|
||||
});
|
||||
|
||||
for (const table of [...METRICS_TABLES].reverse()) {
|
||||
// Skip protected tables
|
||||
if (PROTECTED_TABLES.includes(table)) {
|
||||
outputProgress({
|
||||
operation: 'Protected table',
|
||||
message: `Skipping protected table: ${table}`
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Use NOWAIT to avoid hanging if there's a lock
|
||||
await client.query(`DROP TABLE IF EXISTS "${table}" CASCADE`);
|
||||
|
||||
@@ -8,7 +8,9 @@ const fs = require('fs');
|
||||
|
||||
// Create uploads directory if it doesn't exist
|
||||
const uploadsDir = path.join('/var/www/html/inventory/uploads/products');
|
||||
const reusableUploadsDir = path.join('/var/www/html/inventory/uploads/reusable');
|
||||
fs.mkdirSync(uploadsDir, { recursive: true });
|
||||
fs.mkdirSync(reusableUploadsDir, { recursive: true });
|
||||
|
||||
// Create a Map to track image upload times and their scheduled deletion
|
||||
const imageUploadMap = new Map();
|
||||
@@ -35,6 +37,12 @@ const connectionCache = {
|
||||
|
||||
// Function to schedule image deletion after 24 hours
|
||||
const scheduleImageDeletion = (filename, filePath) => {
|
||||
// Only schedule deletion for images in the products folder
|
||||
if (!filePath.includes('/uploads/products/')) {
|
||||
console.log(`Skipping deletion for non-product image: ${filename}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete any existing timeout for this file
|
||||
if (imageUploadMap.has(filename)) {
|
||||
clearTimeout(imageUploadMap.get(filename).timeoutId);
|
||||
@@ -407,6 +415,14 @@ router.delete('/delete-image', (req, res) => {
|
||||
return res.status(404).json({ error: 'File not found' });
|
||||
}
|
||||
|
||||
// Only allow deletion of images in the products folder
|
||||
if (!filePath.includes('/uploads/products/')) {
|
||||
return res.status(403).json({
|
||||
error: 'Cannot delete images outside the products folder',
|
||||
message: 'This image is in a protected folder and cannot be deleted through this endpoint'
|
||||
});
|
||||
}
|
||||
|
||||
// Delete the file
|
||||
fs.unlinkSync(filePath);
|
||||
|
||||
@@ -641,11 +657,19 @@ router.get('/check-file/:filename', (req, res) => {
|
||||
return res.status(400).json({ error: 'Invalid filename' });
|
||||
}
|
||||
|
||||
const filePath = path.join(uploadsDir, filename);
|
||||
// First check in products directory
|
||||
let filePath = path.join(uploadsDir, filename);
|
||||
let exists = fs.existsSync(filePath);
|
||||
|
||||
// If not found in products, check in reusable directory
|
||||
if (!exists) {
|
||||
filePath = path.join(reusableUploadsDir, filename);
|
||||
exists = fs.existsSync(filePath);
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if file exists
|
||||
if (!fs.existsSync(filePath)) {
|
||||
if (!exists) {
|
||||
return res.status(404).json({
|
||||
error: 'File not found',
|
||||
path: filePath,
|
||||
@@ -685,13 +709,23 @@ router.get('/check-file/:filename', (req, res) => {
|
||||
// List all files in uploads directory
|
||||
router.get('/list-uploads', (req, res) => {
|
||||
try {
|
||||
if (!fs.existsSync(uploadsDir)) {
|
||||
return res.status(404).json({ error: 'Uploads directory not found', path: uploadsDir });
|
||||
const { directory = 'products' } = req.query;
|
||||
|
||||
// Determine which directory to list
|
||||
let targetDir;
|
||||
if (directory === 'reusable') {
|
||||
targetDir = reusableUploadsDir;
|
||||
} else {
|
||||
targetDir = uploadsDir; // default to products
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(uploadsDir);
|
||||
if (!fs.existsSync(targetDir)) {
|
||||
return res.status(404).json({ error: 'Uploads directory not found', path: targetDir });
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(targetDir);
|
||||
const fileDetails = files.map(file => {
|
||||
const filePath = path.join(uploadsDir, file);
|
||||
const filePath = path.join(targetDir, file);
|
||||
try {
|
||||
const stats = fs.statSync(filePath);
|
||||
return {
|
||||
@@ -709,12 +743,13 @@ router.get('/list-uploads', (req, res) => {
|
||||
});
|
||||
|
||||
return res.json({
|
||||
directory: uploadsDir,
|
||||
directory: targetDir,
|
||||
type: directory,
|
||||
count: files.length,
|
||||
files: fileDetails
|
||||
});
|
||||
} catch (error) {
|
||||
return res.status(500).json({ error: error.message, path: uploadsDir });
|
||||
return res.status(500).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user