Diagnostica temporanea: la POST /me/check-ins ritorna 201 ma il record non appare in DB. Aggiunto log prima/dopo upsert + rilettura immediata per individuare se il middleware prisma-field-encryption rollba silenziosamente.
103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
// PRD §5.2 — check-in lampo quotidiano: indicatori soggettivi (energia,
|
|
// sonno, fame, umore) compilabili in 10 secondi, una volta al giorno.
|
|
|
|
import { dailyCheckInInputSchema } from '@ketopath/shared';
|
|
import type { FastifyPluginAsync } from 'fastify';
|
|
|
|
import { requireAuth } from '../../plugins/auth.js';
|
|
import { requirePro } from '../../plugins/require-pro.js';
|
|
|
|
function todayDateOnly(): Date {
|
|
const d = new Date();
|
|
d.setHours(0, 0, 0, 0);
|
|
return d;
|
|
}
|
|
|
|
export const checkInRoutes: FastifyPluginAsync = async (fastify) => {
|
|
fastify.get('/me/check-ins', { preHandler: requireAuth() }, async (request) => {
|
|
const userId = request.user!.id;
|
|
const items = await fastify.prisma.dailyCheckIn.findMany({
|
|
where: { userId },
|
|
orderBy: { date: 'desc' },
|
|
take: 30,
|
|
});
|
|
return {
|
|
items: items.map((c) => ({
|
|
id: c.id,
|
|
date: c.date.toISOString().slice(0, 10),
|
|
energy: c.energy,
|
|
sleep: c.sleep,
|
|
hunger: c.hunger,
|
|
mood: c.mood,
|
|
notes: c.notes,
|
|
})),
|
|
};
|
|
});
|
|
|
|
// GET /me/check-ins/today: ritorna il check-in di oggi (se esiste).
|
|
fastify.get('/me/check-ins/today', { preHandler: requireAuth() }, async (request) => {
|
|
const userId = request.user!.id;
|
|
const date = todayDateOnly();
|
|
const item = await fastify.prisma.dailyCheckIn.findUnique({
|
|
where: { userId_date: { userId, date } },
|
|
});
|
|
if (!item) return { item: null };
|
|
return {
|
|
item: {
|
|
id: item.id,
|
|
date: item.date.toISOString().slice(0, 10),
|
|
energy: item.energy,
|
|
sleep: item.sleep,
|
|
hunger: item.hunger,
|
|
mood: item.mood,
|
|
notes: item.notes,
|
|
},
|
|
};
|
|
});
|
|
|
|
fastify.post(
|
|
'/me/check-ins',
|
|
{ preHandler: [requireAuth(), requirePro()] },
|
|
async (request, reply) => {
|
|
const parsed = dailyCheckInInputSchema.safeParse(request.body);
|
|
if (!parsed.success) {
|
|
return reply.code(400).send({ error: 'invalid_body', issues: parsed.error.issues });
|
|
}
|
|
const data = parsed.data;
|
|
const userId = request.user!.id;
|
|
const date = new Date(data.date);
|
|
date.setHours(0, 0, 0, 0);
|
|
|
|
request.log.info({ userId, date, body: data }, '[check-in] upsert START');
|
|
const item = await fastify.prisma.dailyCheckIn.upsert({
|
|
where: { userId_date: { userId, date } },
|
|
create: {
|
|
userId,
|
|
date,
|
|
energy: data.energy ?? null,
|
|
sleep: data.sleep ?? null,
|
|
hunger: data.hunger ?? null,
|
|
mood: data.mood ?? null,
|
|
notes: data.notes ?? null,
|
|
},
|
|
update: {
|
|
energy: data.energy ?? null,
|
|
sleep: data.sleep ?? null,
|
|
hunger: data.hunger ?? null,
|
|
mood: data.mood ?? null,
|
|
notes: data.notes ?? null,
|
|
},
|
|
});
|
|
request.log.info({ id: item.id }, '[check-in] upsert OK');
|
|
// Rilettura immediata per verificare che il record sia effettivamente
|
|
// persistito (catch silent rollback da middleware encryption).
|
|
const verify = await fastify.prisma.dailyCheckIn.findUnique({
|
|
where: { userId_date: { userId, date } },
|
|
});
|
|
request.log.info({ found: verify != null, verifyId: verify?.id }, '[check-in] verify');
|
|
|
|
return reply.code(201).send({ item: { id: item.id } });
|
|
},
|
|
);
|
|
};
|