123 lines
4.6 KiB
TypeScript
123 lines
4.6 KiB
TypeScript
import { Router, type Request, type Response } from 'express';
|
|
import { prisma } from '../lib/prisma';
|
|
import { basicAuth } from '../middlewares/basicAuth';
|
|
import { CreateModelSchema, UpdateModelSchema, ListQuerySchema } from '../lib/validators';
|
|
import { decodeBase64ToBuffer } from '../lib/base64';
|
|
|
|
export const router = Router();
|
|
|
|
// Create model
|
|
router.post('/', basicAuth, async (req: Request, res: Response) => {
|
|
const parsed = CreateModelSchema.safeParse(req.body);
|
|
if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
|
|
const { title, desc, model, previewBase64, previewMime } = parsed.data;
|
|
const created = await prisma.circuitModel.create({
|
|
data: {
|
|
title,
|
|
desc,
|
|
model,
|
|
preview: decodeBase64ToBuffer(previewBase64),
|
|
previewMime: previewBase64 ? (previewMime ?? 'image/png') : null,
|
|
authorId: (req as any).user.id,
|
|
},
|
|
});
|
|
res.status(201).json({ id: created.id });
|
|
});
|
|
|
|
// Update model
|
|
router.put('/:id', basicAuth, async (req: Request, res: Response) => {
|
|
const id = req.params.id;
|
|
const owned = await prisma.circuitModel.findUnique({ where: { id }, select: { authorId: true } });
|
|
if (!owned) return res.status(404).json({ error: 'Not found' });
|
|
if (owned.authorId !== (req as any).user.id) return res.status(403).json({ error: 'Forbidden' });
|
|
const parsed = UpdateModelSchema.safeParse(req.body);
|
|
if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
|
|
const { title, desc, model, previewBase64, previewMime } = parsed.data;
|
|
const data: any = {};
|
|
if (title !== undefined) data.title = title;
|
|
if (desc !== undefined) data.desc = desc;
|
|
if (model !== undefined) data.model = model;
|
|
if (previewBase64 !== undefined) {
|
|
data.preview = decodeBase64ToBuffer(previewBase64);
|
|
data.previewMime = previewBase64 ? (previewMime ?? 'image/png') : null;
|
|
}
|
|
const updated = await prisma.circuitModel.update({ where: { id }, data });
|
|
res.json({ id: updated.id });
|
|
});
|
|
|
|
// Delete model
|
|
router.delete('/:id', basicAuth, async (req: Request, res: Response) => {
|
|
const id = req.params.id;
|
|
const owned = await prisma.circuitModel.findUnique({ where: { id }, select: { authorId: true } });
|
|
if (!owned) return res.status(404).json({ error: 'Not found' });
|
|
if (owned.authorId !== (req as any).user.id) return res.status(403).json({ error: 'Forbidden' });
|
|
await prisma.circuitModel.delete({ where: { id } });
|
|
res.status(204).end();
|
|
});
|
|
|
|
// Get by id
|
|
router.get('/:id', async (req: Request, res: Response) => {
|
|
const id = req.params.id;
|
|
const m = await prisma.circuitModel.findUnique({
|
|
where: { id },
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
desc: true,
|
|
model: true,
|
|
authorId: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
if (!m) return res.status(404).json({ error: 'Not found' });
|
|
res.json(m);
|
|
});
|
|
|
|
// List models with pagination and simple search
|
|
router.get('/', async (req: Request, res: Response) => {
|
|
const parsed = ListQuerySchema.safeParse(req.query);
|
|
if (!parsed.success) return res.status(400).json({ error: parsed.error.flatten() });
|
|
const { page, pageSize, authorId, sort, q } = parsed.data;
|
|
const where: any = {};
|
|
if (authorId) where.authorId = authorId;
|
|
if (q) where.OR = [
|
|
{ title: { contains: q, mode: 'insensitive' } },
|
|
{ desc: { contains: q, mode: 'insensitive' } },
|
|
];
|
|
const total = await prisma.circuitModel.count({ where });
|
|
const items = await prisma.circuitModel.findMany({
|
|
where,
|
|
orderBy: { createdAt: sort === 'new' ? 'desc' : 'asc' },
|
|
skip: (page - 1) * pageSize,
|
|
take: pageSize,
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
authorId: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
},
|
|
});
|
|
res.json({ page, pageSize, total, items });
|
|
});
|
|
|
|
// Download model JSON
|
|
router.get('/:id/model.json', async (req: Request, res: Response) => {
|
|
const id = req.params.id;
|
|
const m = await prisma.circuitModel.findUnique({ where: { id }, select: { model: true, title: true } });
|
|
if (!m) return res.status(404).end();
|
|
res.setHeader('Content-Type', 'application/json');
|
|
res.setHeader('Content-Disposition', `attachment; filename="${encodeURIComponent(m.title)}.json"`);
|
|
res.end(JSON.stringify(m.model));
|
|
});
|
|
|
|
// Get preview image
|
|
router.get('/:id/preview', async (req: Request, res: Response) => {
|
|
const id = req.params.id;
|
|
const m = await prisma.circuitModel.findUnique({ where: { id }, select: { preview: true, previewMime: true } });
|
|
if (!m || !m.preview) return res.status(404).end();
|
|
res.setHeader('Content-Type', m.previewMime ?? 'image/png');
|
|
res.end(Buffer.from(m.preview));
|
|
});
|