159 lines
4.8 KiB
TypeScript
159 lines
4.8 KiB
TypeScript
|
|
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'],
|
||
|
|
},
|
||
|
|
);
|