Initial commit: redefined-designs storefront
This commit is contained in:
Executable
+124
@@ -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;
|
||||
Reference in New Issue
Block a user