Initial commit: redefined-designs storefront

This commit is contained in:
2026-08-13 21:07:54 +00:00
commit 9be4986dd3
38 changed files with 2121 additions and 0 deletions
+124
View File
@@ -0,0 +1,124 @@
import { Router, Request, Response } from 'express';
import multer from 'multer';
import path from 'path';
import { pool } from '../db';
const router = Router();
const storage = multer.diskStorage({
destination: '/app/uploads',
filename: (_req, file, cb) => {
const ext = path.extname(file.originalname);
cb(null, `${Date.now()}-${Math.round(Math.random() * 1e6)}${ext}`);
}
});
const upload = multer({ storage });
const SELECT_WITH_IMAGES = `
SELECT i.*,
COALESCE(
json_agg(json_build_object('id', img.id, 'image_path', img.image_path, 'sort_order', img.sort_order)
ORDER BY img.sort_order) FILTER (WHERE img.id IS NOT NULL),
'[]'
) AS images
FROM items i
LEFT JOIN item_images img ON img.item_id = i.id
`;
router.get('/items', async (_req: Request, res: Response) => {
const { rows } = await pool.query(`${SELECT_WITH_IMAGES} GROUP BY i.id ORDER BY i.created_at DESC`);
res.json(rows);
});
router.post('/items', upload.array('images', 6), async (req: Request, res: Response) => {
const { name, description, price } = req.body;
const files = (req.files as Express.Multer.File[]) || [];
const client = await pool.connect();
try {
await client.query('BEGIN');
const { rows } = await client.query(
`INSERT INTO items (name, description, price_cents) VALUES ($1, $2, $3) RETURNING *`,
[name, description, Math.round(parseFloat(price) * 100)]
);
const item = rows[0];
for (let i = 0; i < files.length; i++) {
await client.query(
`INSERT INTO item_images (item_id, image_path, sort_order) VALUES ($1, $2, $3)`,
[item.id, `/uploads/${files[i].filename}`, i]
);
}
await client.query('COMMIT');
const { rows: full } = await pool.query(`${SELECT_WITH_IMAGES} WHERE i.id = $1 GROUP BY i.id`, [item.id]);
res.json(full[0]);
} catch (err) {
await client.query('ROLLBACK');
console.error(err);
res.status(500).json({ error: 'internal error' });
} finally {
client.release();
}
});
router.put('/items/:id', upload.array('images', 6), async (req: Request, res: Response) => {
const { name, description, price } = req.body;
const files = (req.files as Express.Multer.File[]) || [];
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query(
`UPDATE items SET name=$1, description=$2, price_cents=$3 WHERE id=$4`,
[name, description, Math.round(parseFloat(price) * 100), req.params.id]
);
if (files.length) {
const { rows: existing } = await client.query(
`SELECT COALESCE(MAX(sort_order), -1) AS max_sort FROM item_images WHERE item_id = $1`,
[req.params.id]
);
let nextSort = existing[0].max_sort + 1;
for (const file of files) {
await client.query(
`INSERT INTO item_images (item_id, image_path, sort_order) VALUES ($1, $2, $3)`,
[req.params.id, `/uploads/${file.filename}`, nextSort++]
);
}
}
await client.query('COMMIT');
const { rows: full } = await pool.query(`${SELECT_WITH_IMAGES} WHERE i.id = $1 GROUP BY i.id`, [req.params.id]);
res.json(full[0]);
} catch (err) {
await client.query('ROLLBACK');
console.error(err);
res.status(500).json({ error: 'internal error' });
} finally {
client.release();
}
});
router.delete('/items/:id', async (req: Request, res: Response) => {
await pool.query(`DELETE FROM items WHERE id = $1`, [req.params.id]);
res.status(204).end();
});
router.delete('/items/:id/images/:imageId', async (req: Request, res: Response) => {
await pool.query(`DELETE FROM item_images WHERE id = $1 AND item_id = $2`, [req.params.imageId, req.params.id]);
res.status(204).end();
});
router.post('/items/:id/mark-sold', async (req: Request, res: Response) => {
const { rows } = await pool.query(
`UPDATE items SET status='sold', sold_at=now() WHERE id=$1 RETURNING *`,
[req.params.id]
);
res.json(rows[0]);
});
router.post('/items/:id/mark-available', async (req: Request, res: Response) => {
const { rows } = await pool.query(
`UPDATE items SET status='available', sold_at=NULL, reserved_until=NULL, paypal_order_id=NULL
WHERE id=$1 RETURNING *`,
[req.params.id]
);
res.json(rows[0]);
});
export default router;