api: add a controller for fetching insights
Pedro Lucas Porcellis porcellis@eletrotupi.com · 4 months ago
b3cae39fbd7a790d651c1950d4bc42dc3bc9e9aa Browse filesParents
a772f94Changed files
6
Additions
+114
Deletions
-24
/dev/null → api/src/controllers/insights.ts
1 +
import { Response, NextFunction } from "express";
2 +
import { AuthenticatedRequest } from "@app/middleware/auth";
3 +
import { getInsightsByUserId } from "@app/services/insight.service";
4 +
import { InsightsQuerySchema } from "@app/schemas";
5 +
6 +
export const InsightsController = {
7 +
index: async (
8 +
req: AuthenticatedRequest,
9 +
res: Response,
10 +
next: NextFunction,
11 +
) => {
12 +
try {
13 +
const parsedQuery = InsightsQuerySchema.safeParse(req.query);
14 +
15 +
if (!parsedQuery.success) {
16 +
return res.status(400).json({
17 +
message: "Invalid query parameters",
18 +
errors: parsedQuery.error.flatten(),
19 +
});
20 +
}
21 +
22 +
const insights = await getInsightsByUserId(req.userId!, {
23 +
type: parsedQuery.data.type,
24 +
period: parsedQuery.data.period,
25 +
limit: parsedQuery.data.limit,
26 +
});
27 +
28 +
return res.status(200).json({ insights });
29 +
} catch (err) {
30 +
next(err);
31 +
}
32 +
},
33 +
};
api/src/createApp.ts
1 1
import express from "express";
2 2
import cors from "cors";
3 3
import bodyParser from "body-parser";
4 4
import morgan from "morgan";
5 5
import mainRouter from "@app/routes/main";
26 26
const app = express();
27 27
bootWorkers();
28 28
scheduleInsights();
29 29
30 30
// Configure CORS
31 -
app.use(cors({
32 -
origin: process.env.FRONTEND_URL || '*',
33 -
credentials: true
34 -
}));
31 +
app.use(
32 +
cors({
33 +
origin: process.env.FRONTEND_URL || "*",
34 +
credentials: true,
35 +
}),
36 +
);
35 37
36 38
app.use(bodyParser.json());
37 39
38 -
const isDev = process.env.NODE_ENV !== 'production';
40 +
const isDev = process.env.NODE_ENV !== "production";
39 41
40 42
const logger = pino({
41 -
level: process.env.LOG_LEVEL || 'info',
43 +
level: process.env.LOG_LEVEL || "info",
42 44
...(isDev && {
43 45
transport: {
44 -
target: 'pino-pretty',
46 +
target: "pino-pretty",
45 47
options: {
46 48
colorize: true,
47 49
singleLine: false,
48 -
messageFormat: '{if levelLabel}{levelLabel} - {end}{msg}',
50 +
messageFormat: "{if levelLabel}{levelLabel} - {end}{msg}",
49 51
},
50 52
},
51 53
}),
52 54
});
53 55
57 59
app.use("/users", userRouter);
58 60
app.use("/auth", authRouter);
59 61
app.use("/moods", moodRouter);
60 62
app.use("/sleep_records", sleepRecordRouter);
61 63
app.use("/triggers", triggerRouter);
64 +
app.use("/insights", insightRouter);
62 65
63 66
app.use(errorHandler); // always last
64 67
65 68
// TODO: flesh the actual page here
66 -
app.use(express.static(path.join(__dirname, './static')));
69 +
app.use(express.static(path.join(__dirname, "./static")));
67 70
app.get(/.*/, (req, res) => {
68 -
res.sendFile(path.join(__dirname, './static/index.html')); });
71 +
res.sendFile(path.join(__dirname, "./static/index.html"));
72 +
});
69 73
70 -
process.on('SIGTERM', shutdown);
71 -
process.on('SIGINT', shutdown);
74 +
process.on("SIGTERM", shutdown);
75 +
process.on("SIGINT", shutdown);
72 76
73 77
return app;
74 78
}
75 79
76 80
const shutdown = async () => {
77 81
await Promise.all([closeAllWorkers(), closeAllQueues()]);
78 82
process.exit(0);
79 83
};
/dev/null → api/src/routes/insights.ts
1 +
import { Router } from "express";
2 +
import { requireAuth } from "@app/middleware/auth";
3 +
import { InsightsController } from "@app/controllers/insights";
4 +
5 +
const router = Router();
6 +
7 +
router.get("/", requireAuth, InsightsController.index);
8 +
9 +
export default router;
api/src/schemas/index.ts
1 1
export {
2 2
CreateMoodSchema,
3 -
MoodComponentSchema
4 -
} from '@app/schemas/mood.schema';
3 +
MoodComponentSchema,
4 +
} from "@app/schemas/mood.schema";
5 5
6 -
export type {
7 -
CreateMoodInput
8 -
} from '@app/schemas/mood.schema';
6 +
export type { CreateMoodInput } from "@app/schemas/mood.schema";
9 7
10 8
export {
11 9
CreateUserSchema,
12 10
UpdateUserSchema,
13 11
type CreateUserInput,
14 12
type UpdateUserInput,
15 -
} from '@app/schemas/user.schema';
13 +
} from "@app/schemas/user.schema";
16 14
17 15
export {
18 16
LoginSchema,
19 17
PasswordResetRequestSchema,
20 18
PasswordResetSchema,
21 19
ActivateUserSchema,
22 20
type LoginInput,
23 21
type PasswordResetRequestInput,
24 22
type PasswordResetInput,
25 -
type ActivateUserInput
26 -
} from '@app/schemas/auth.schema';
23 +
type ActivateUserInput,
24 +
} from "@app/schemas/auth.schema";
27 25
28 26
export {
29 27
type CreateSleepRecordInput,
30 -
CreateSleepRecordSchema
31 -
} from '@app/schemas/sleepRecord.schema';
28 +
CreateSleepRecordSchema,
29 +
} from "@app/schemas/sleepRecord.schema";
32 30
33 31
export {
34 32
type CreateTriggerInput,
35 -
CreateTriggerSchema
36 -
} from '@app/schemas/trigger.schema';
33 +
CreateTriggerSchema,
34 +
} from "@app/schemas/trigger.schema";
35 +
36 +
export {
37 +
InsightsQuerySchema,
38 +
type InsightsQueryInput,
39 +
} from "@app/schemas/insight.schema";
/dev/null → api/src/schemas/insight.schema.ts
1 +
import { z } from 'zod';
2 +
import { InsightPeriod, InsightType } from '@prisma/client';
3 +
4 +
export const InsightsQuerySchema = z.object({
5 +
type: z.nativeEnum(InsightType).optional(),
6 +
period: z.nativeEnum(InsightPeriod).optional(),
7 +
limit: z.coerce.number().int().positive().max(100).optional(),
8 +
});
9 +
10 +
export type InsightsQueryInput = z.infer<typeof InsightsQuerySchema>;
/dev/null → api/src/services/insight.service.ts
1 +
import { prisma } from "@app/lib/prisma";
2 +
import { Insight, InsightType, InsightPeriod } from "@prisma/client";
3 +
4 +
export const getInsightsByUserId = async (
5 +
userId: number,
6 +
params?: {
7 +
type?: InsightType;
8 +
period?: InsightPeriod;
9 +
limit?: number;
10 +
},
11 +
): Promise<Insight[]> => {
12 +
return prisma.insight.findMany({
13 +
where: {
14 +
userId,
15 +
...(params?.type && { type: params.type }),
16 +
...(params?.period && { period: params.period }),
17 +
},
18 +
orderBy: { generatedAt: "desc" },
19 +
take: params?.limit ?? 10,
20 +
});
21 +
};
22 +
23 +
export const getLatestInsightByType = async (
24 +
userId: number,
25 +
type: InsightType,
26 +
): Promise<Insight | null> => {
27 +
return prisma.insight.findFirst({
28 +
where: { userId, type },
29 +
orderBy: { generatedAt: "desc" },
30 +
});
31 +
};