338 lines
9.7 KiB
JavaScript
338 lines
9.7 KiB
JavaScript
const express = require('express');
|
|
const router = express.Router();
|
|
|
|
// Get all import sessions for a user (named + unnamed)
|
|
router.get('/', async (req, res) => {
|
|
try {
|
|
const { user_id } = req.query;
|
|
|
|
if (!user_id) {
|
|
return res.status(400).json({ error: 'user_id query parameter is required' });
|
|
}
|
|
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
const result = await pool.query(`
|
|
SELECT
|
|
id,
|
|
user_id,
|
|
name,
|
|
current_step,
|
|
jsonb_array_length(data) as row_count,
|
|
global_selections,
|
|
created_at,
|
|
updated_at
|
|
FROM import_sessions
|
|
WHERE user_id = $1
|
|
ORDER BY
|
|
CASE WHEN name IS NULL THEN 0 ELSE 1 END,
|
|
updated_at DESC
|
|
`, [user_id]);
|
|
|
|
res.json(result.rows);
|
|
} catch (error) {
|
|
console.error('Error fetching import sessions:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to fetch import sessions',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Get session by ID
|
|
router.get('/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
const result = await pool.query(`
|
|
SELECT * FROM import_sessions
|
|
WHERE id = $1
|
|
`, [id]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Import session not found' });
|
|
}
|
|
|
|
res.json(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Error fetching import session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to fetch import session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Autosave - upsert unnamed session for user
|
|
// IMPORTANT: This must be defined before /:id routes to avoid Express matching "autosave" as an :id
|
|
router.put('/autosave', async (req, res) => {
|
|
try {
|
|
const {
|
|
user_id,
|
|
current_step,
|
|
data,
|
|
product_images,
|
|
global_selections,
|
|
validation_state
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!user_id) {
|
|
return res.status(400).json({ error: 'user_id is required' });
|
|
}
|
|
if (!current_step) {
|
|
return res.status(400).json({ error: 'current_step is required' });
|
|
}
|
|
if (!data || !Array.isArray(data)) {
|
|
return res.status(400).json({ error: 'data must be an array' });
|
|
}
|
|
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
// Upsert: insert or update the unnamed session for this user
|
|
const result = await pool.query(`
|
|
INSERT INTO import_sessions (
|
|
user_id,
|
|
name,
|
|
current_step,
|
|
data,
|
|
product_images,
|
|
global_selections,
|
|
validation_state
|
|
) VALUES ($1, NULL, $2, $3, $4, $5, $6)
|
|
ON CONFLICT (user_id) WHERE name IS NULL
|
|
DO UPDATE SET
|
|
current_step = EXCLUDED.current_step,
|
|
data = EXCLUDED.data,
|
|
product_images = EXCLUDED.product_images,
|
|
global_selections = EXCLUDED.global_selections,
|
|
validation_state = EXCLUDED.validation_state,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
RETURNING id, user_id, name, current_step, created_at, updated_at
|
|
`, [
|
|
user_id,
|
|
current_step,
|
|
JSON.stringify(data),
|
|
product_images ? JSON.stringify(product_images) : null,
|
|
global_selections ? JSON.stringify(global_selections) : null,
|
|
validation_state ? JSON.stringify(validation_state) : null
|
|
]);
|
|
|
|
res.json(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Error autosaving import session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to autosave import session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Delete unnamed session for user (clear autosave)
|
|
// IMPORTANT: This must be defined before /:id routes
|
|
router.delete('/autosave/:user_id', async (req, res) => {
|
|
try {
|
|
const { user_id } = req.params;
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
const result = await pool.query(
|
|
'DELETE FROM import_sessions WHERE user_id = $1 AND name IS NULL RETURNING id, user_id, name, current_step, created_at, updated_at',
|
|
[user_id]
|
|
);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'No autosave session found for user' });
|
|
}
|
|
|
|
res.json({ message: 'Autosave session deleted successfully' });
|
|
} catch (error) {
|
|
console.error('Error deleting autosave session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to delete autosave session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Create new named session
|
|
router.post('/', async (req, res) => {
|
|
try {
|
|
const {
|
|
user_id,
|
|
name,
|
|
current_step,
|
|
data,
|
|
product_images,
|
|
global_selections,
|
|
validation_state
|
|
} = req.body;
|
|
|
|
// Validate required fields
|
|
if (!user_id) {
|
|
return res.status(400).json({ error: 'user_id is required' });
|
|
}
|
|
if (!name || typeof name !== 'string' || name.trim().length === 0) {
|
|
return res.status(400).json({ error: 'name is required for creating a named session' });
|
|
}
|
|
if (!current_step) {
|
|
return res.status(400).json({ error: 'current_step is required' });
|
|
}
|
|
if (!data || !Array.isArray(data)) {
|
|
return res.status(400).json({ error: 'data must be an array' });
|
|
}
|
|
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
const result = await pool.query(`
|
|
INSERT INTO import_sessions (
|
|
user_id,
|
|
name,
|
|
current_step,
|
|
data,
|
|
product_images,
|
|
global_selections,
|
|
validation_state
|
|
) VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING id, user_id, name, current_step, created_at, updated_at
|
|
`, [
|
|
user_id,
|
|
name.trim(),
|
|
current_step,
|
|
JSON.stringify(data),
|
|
product_images ? JSON.stringify(product_images) : null,
|
|
global_selections ? JSON.stringify(global_selections) : null,
|
|
validation_state ? JSON.stringify(validation_state) : null
|
|
]);
|
|
|
|
res.status(201).json(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Error creating import session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to create import session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Update named session by ID
|
|
router.put('/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const {
|
|
name,
|
|
current_step,
|
|
data,
|
|
product_images,
|
|
global_selections,
|
|
validation_state
|
|
} = req.body;
|
|
|
|
if (!current_step) {
|
|
return res.status(400).json({ error: 'current_step is required' });
|
|
}
|
|
if (!data || !Array.isArray(data)) {
|
|
return res.status(400).json({ error: 'data must be an array' });
|
|
}
|
|
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
// Build update query - optionally include name if provided
|
|
const hasName = name !== undefined;
|
|
const result = await pool.query(`
|
|
UPDATE import_sessions
|
|
SET
|
|
${hasName ? 'name = $1,' : ''}
|
|
current_step = $${hasName ? 2 : 1},
|
|
data = $${hasName ? 3 : 2},
|
|
product_images = $${hasName ? 4 : 3},
|
|
global_selections = $${hasName ? 5 : 4},
|
|
validation_state = $${hasName ? 6 : 5},
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = $${hasName ? 7 : 6}
|
|
RETURNING id, user_id, name, current_step, created_at, updated_at
|
|
`, hasName ? [
|
|
typeof name === 'string' ? name.trim() : name,
|
|
current_step,
|
|
JSON.stringify(data),
|
|
product_images ? JSON.stringify(product_images) : null,
|
|
global_selections ? JSON.stringify(global_selections) : null,
|
|
validation_state ? JSON.stringify(validation_state) : null,
|
|
id
|
|
] : [
|
|
current_step,
|
|
JSON.stringify(data),
|
|
product_images ? JSON.stringify(product_images) : null,
|
|
global_selections ? JSON.stringify(global_selections) : null,
|
|
validation_state ? JSON.stringify(validation_state) : null,
|
|
id
|
|
]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Import session not found' });
|
|
}
|
|
|
|
res.json(result.rows[0]);
|
|
} catch (error) {
|
|
console.error('Error updating import session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to update import session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Delete session by ID
|
|
router.delete('/:id', async (req, res) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const pool = req.app.locals.pool;
|
|
if (!pool) {
|
|
throw new Error('Database pool not initialized');
|
|
}
|
|
|
|
const result = await pool.query('DELETE FROM import_sessions WHERE id = $1 RETURNING id, user_id, name, current_step, created_at, updated_at', [id]);
|
|
|
|
if (result.rows.length === 0) {
|
|
return res.status(404).json({ error: 'Import session not found' });
|
|
}
|
|
|
|
res.json({ message: 'Import session deleted successfully' });
|
|
} catch (error) {
|
|
console.error('Error deleting import session:', error);
|
|
res.status(500).json({
|
|
error: 'Failed to delete import session',
|
|
details: error instanceof Error ? error.message : 'Unknown error'
|
|
});
|
|
}
|
|
});
|
|
|
|
// Error handling middleware
|
|
router.use((err, req, res, next) => {
|
|
console.error('Import sessions route error:', err);
|
|
res.status(500).json({
|
|
error: 'Internal server error',
|
|
details: err.message
|
|
});
|
|
});
|
|
|
|
module.exports = router;
|