110 lines
3.5 KiB
TypeScript
110 lines
3.5 KiB
TypeScript
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
|
|
import { BadRequestError, NotFoundError } from '@bytelyst/errors';
|
|
import { extractAuth } from '../../lib/auth.js';
|
|
import { PRODUCT_ID } from '../../lib/product-config.js';
|
|
import * as repo from './repository.js';
|
|
import {
|
|
CreateSavedViewSchema,
|
|
ListSavedViewsQuerySchema,
|
|
UpdateSavedViewSchema,
|
|
type SavedViewDoc,
|
|
} from './types.js';
|
|
|
|
export async function savedViewRoutes(app: FastifyInstance) {
|
|
app.get('/saved-views', async (req: FastifyRequest) => {
|
|
const auth = await extractAuth(req);
|
|
const parsed = ListSavedViewsQuerySchema.safeParse(req.query);
|
|
if (!parsed.success) {
|
|
throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; '));
|
|
}
|
|
|
|
const result = await repo.listSavedViews(auth.sub, PRODUCT_ID, parsed.data);
|
|
return { ...result, limit: parsed.data.limit, offset: parsed.data.offset };
|
|
});
|
|
|
|
app.post('/saved-views', async (req: FastifyRequest, reply: FastifyReply) => {
|
|
const auth = await extractAuth(req);
|
|
const parsed = CreateSavedViewSchema.safeParse(req.body);
|
|
if (!parsed.success) {
|
|
throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; '));
|
|
}
|
|
|
|
const now = new Date().toISOString();
|
|
const doc: SavedViewDoc = {
|
|
id: parsed.data.id,
|
|
productId: PRODUCT_ID,
|
|
userId: auth.sub,
|
|
name: parsed.data.name,
|
|
scope: parsed.data.scope,
|
|
description: parsed.data.description,
|
|
query: parsed.data.query,
|
|
filters: parsed.data.filters,
|
|
sortOrder: parsed.data.sortOrder,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
createdBy: auth.sub,
|
|
updatedBy: auth.sub,
|
|
};
|
|
|
|
const created = await repo.createSavedView(doc);
|
|
reply.code(201);
|
|
return created;
|
|
});
|
|
|
|
app.get('/saved-views/:id', async (req: FastifyRequest) => {
|
|
const auth = await extractAuth(req);
|
|
const { id } = req.params as { id: string };
|
|
|
|
const view = await repo.getSavedView(id, auth.sub);
|
|
if (!view || view.productId !== PRODUCT_ID) {
|
|
throw new NotFoundError('Saved view not found');
|
|
}
|
|
|
|
return view;
|
|
});
|
|
|
|
app.patch('/saved-views/:id', async (req: FastifyRequest) => {
|
|
const auth = await extractAuth(req);
|
|
const { id } = req.params as { id: string };
|
|
|
|
const parsed = UpdateSavedViewSchema.safeParse(req.body);
|
|
if (!parsed.success) {
|
|
throw new BadRequestError(parsed.error.issues.map((issue: { message: string }) => issue.message).join('; '));
|
|
}
|
|
|
|
const existing = await repo.getSavedView(id, auth.sub);
|
|
if (!existing || existing.productId !== PRODUCT_ID) {
|
|
throw new NotFoundError('Saved view not found');
|
|
}
|
|
|
|
const updated = await repo.updateSavedView(id, auth.sub, {
|
|
...parsed.data,
|
|
updatedAt: new Date().toISOString(),
|
|
updatedBy: auth.sub,
|
|
});
|
|
|
|
if (!updated) {
|
|
throw new NotFoundError('Saved view not found');
|
|
}
|
|
|
|
return updated;
|
|
});
|
|
|
|
app.delete('/saved-views/:id', async (req: FastifyRequest, reply: FastifyReply) => {
|
|
const auth = await extractAuth(req);
|
|
const { id } = req.params as { id: string };
|
|
|
|
const existing = await repo.getSavedView(id, auth.sub);
|
|
if (!existing || existing.productId !== PRODUCT_ID) {
|
|
throw new NotFoundError('Saved view not found');
|
|
}
|
|
|
|
const deleted = await repo.deleteSavedView(id, auth.sub);
|
|
if (!deleted) {
|
|
throw new NotFoundError('Saved view not found');
|
|
}
|
|
|
|
reply.code(204).send();
|
|
});
|
|
}
|