Setup initial project

This commit is contained in:
Aerilyn Weber 2026-03-27 14:50:34 +09:00
commit db79af06f7
119 changed files with 20761 additions and 0 deletions

View file

@ -0,0 +1,81 @@
import fp from 'fastify-plugin';
import { asClass, Lifetime } from 'awilix';
import type { ZodTypeProvider } from 'fastify-type-provider-zod';
import { z } from 'zod/v4';
import { UsersRepository } from './users.repository.js';
import { UsersService } from './users.service.js';
import { NotFoundError } from '../../common/errors.js';
type AnyUserDoc = {
_id: string | { toString: () => string };
keycloakId: string;
displayName: string;
email: string;
householdIds: string[];
defaultHouseholdId?: string | null;
createdAt: string | { toISOString: () => string };
updatedAt: string | { toISOString: () => string };
};
function toUserResponse(doc: AnyUserDoc) {
const id = typeof doc._id === 'string' ? doc._id : doc._id.toString();
const createdAt = typeof doc.createdAt === 'string' ? doc.createdAt : doc.createdAt.toISOString();
const updatedAt = typeof doc.updatedAt === 'string' ? doc.updatedAt : doc.updatedAt.toISOString();
return {
_id: id,
keycloakId: doc.keycloakId,
displayName: doc.displayName,
email: doc.email,
householdIds: doc.householdIds,
defaultHouseholdId: doc.defaultHouseholdId ?? null,
createdAt,
updatedAt,
};
}
declare module '@fastify/awilix' {
interface Cradle {
usersRepository: UsersRepository;
usersService: UsersService;
}
}
export default fp(
async (fastify) => {
// Register DI
fastify.diContainer.register({
usersRepository: asClass(UsersRepository, { lifetime: Lifetime.SINGLETON }),
usersService: asClass(UsersService, { lifetime: Lifetime.SINGLETON }),
});
const app = fastify.withTypeProvider<ZodTypeProvider>();
// GET /api/v1/users/me — get current user profile (syncs from token on first call)
app.route({
method: 'GET',
url: '/api/v1/users/me',
config: { skipHousehold: true },
schema: {
response: {
200: z.object({
_id: z.string(),
keycloakId: z.string(),
displayName: z.string(),
email: z.string(),
householdIds: z.array(z.string()),
defaultHouseholdId: z.string().nullable(),
createdAt: z.string(),
updatedAt: z.string(),
}),
},
},
handler: async (request, reply) => {
const service = fastify.diContainer.resolve('usersService');
const user = await service.syncFromToken(request.user);
if (!user) throw new NotFoundError('User sync failed');
return reply.send(toUserResponse(user));
},
});
},
{ name: 'users-routes', dependencies: ['auth-plugin'] },
);