MeshiTrack/packages/api/src/modules/stores/stores.routes.ts

159 lines
4.8 KiB
TypeScript
Raw Normal View History

import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import {
CreateStoreSchema,
UpdateStoreSchema,
StoreQuerySchema,
StoreResponseSchema,
StoreListResponseSchema,
} from '@meshitrack/shared';
import { StoresRepository } from './stores.repository.js';
import { StoresService } from './stores.service.js';
type AnyStoreDoc = {
_id: string | { toString: () => string };
householdId: string;
name: string;
address?: string;
location?: { lat: number; lng: number };
url?: string;
notes?: string;
tags: string[];
isActive: boolean;
createdBy: string;
createdAt: string | Date | { toISOString: () => string };
updatedAt: string | Date | { toISOString: () => string };
};
function toIso(v: string | Date | { toISOString: () => string }): string {
if (typeof v === 'string') return v;
return v.toISOString();
}
function toStoreResponse(rawDoc: unknown) {
const doc = rawDoc as AnyStoreDoc;
return {
_id: typeof doc._id === 'string' ? doc._id : doc._id.toString(),
householdId: doc.householdId,
name: doc.name,
...(doc.address != null ? { address: doc.address } : {}),
...(doc.location != null ? { location: doc.location } : {}),
...(doc.url != null ? { url: doc.url } : {}),
...(doc.notes != null ? { notes: doc.notes } : {}),
tags: doc.tags,
isActive: doc.isActive,
createdBy: doc.createdBy,
createdAt: toIso(doc.createdAt),
updatedAt: toIso(doc.updatedAt),
};
}
declare module '@fastify/awilix' {
interface Cradle {
storesRepository: StoresRepository;
storesService: StoresService;
}
}
export default fp(
async (fastify) => {
fastify.diContainer.register({
storesRepository: asClass(StoresRepository, { lifetime: Lifetime.SINGLETON }),
storesService: asClass(StoresService, { lifetime: Lifetime.SINGLETON }),
});
const app = fastify.withTypeProvider<ZodTypeProvider>();
const householdParams = z.object({ householdId: z.string() });
app.route({
method: 'GET',
url: '/api/v1/households/:householdId/stores',
schema: {
params: householdParams,
querystring: StoreQuerySchema,
response: { 200: StoreListResponseSchema },
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('storesService');
const result = await service.list(request.params.householdId, request.query);
return reply.send({
data: result.data.map(toStoreResponse),
pagination: result.pagination,
});
},
});
app.route({
method: 'GET',
url: '/api/v1/households/:householdId/stores/:id',
schema: {
params: householdParams.extend({ id: z.string() }),
response: { 200: StoreResponseSchema },
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('storesService');
const store = await service.getById(request.params.id, request.params.householdId);
return reply.send(toStoreResponse(store));
},
});
app.route({
method: 'POST',
url: '/api/v1/households/:householdId/stores',
schema: {
params: householdParams,
body: CreateStoreSchema,
response: { 201: StoreResponseSchema },
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('storesService');
const store = await service.create(
request.body,
request.params.householdId,
request.user.keycloakId,
);
return reply.status(201).send(toStoreResponse(store));
},
});
app.route({
method: 'PATCH',
url: '/api/v1/households/:householdId/stores/:id',
schema: {
params: householdParams.extend({ id: z.string() }),
body: UpdateStoreSchema,
response: { 200: StoreResponseSchema },
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('storesService');
const store = await service.update(
request.params.id,
request.params.householdId,
request.body,
);
return reply.send(toStoreResponse(store));
},
});
app.route({
method: 'DELETE',
url: '/api/v1/households/:householdId/stores/:id',
schema: {
params: householdParams.extend({ id: z.string() }),
response: { 200: StoreResponseSchema },
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('storesService');
const store = await service.deactivate(request.params.id, request.params.householdId);
return reply.send(toStoreResponse(store));
},
});
},
{
name: 'stores-routes',
dependencies: ['auth-plugin'],
},
);