From 4a49365299a2e81471f73324d673021ef19a466c Mon Sep 17 00:00:00 2001 From: mUusitalo <73437699+mUusitalo@users.noreply.github.com> Date: Sat, 4 May 2024 13:44:16 +0000 Subject: [PATCH 1/8] Define types for analytics --- src/common/types.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/common/types.ts b/src/common/types.ts index ec53317..f15b796 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -74,4 +74,19 @@ export type PhotoCtxType = NarrowedContext< } >; +export type TeamStatistics = { + totalPoints: number; + totalKilometers: number; + totalEntires: number; + numberOfUniqueParticipants: number; + proportionOfContinuingParticipants: number; + period: number; + pointsGainedInPeriod: number; + proportionOfMilestoneAchievers: number; +}; + +export type Statistics = Map; + +export type pointsPerGuild = Map; + export type PrivacyState = "accepted" | "rejected"; From 5347e65a6d8229fd4a8d9d6d75533f9e400009f2 Mon Sep 17 00:00:00 2001 From: mUusitalo <73437699+mUusitalo@users.noreply.github.com> Date: Sat, 4 May 2024 14:41:06 +0000 Subject: [PATCH 2/8] Add point information to entry in DB --- .../migration.sql | 33 +++++++++++++++++++ prisma/schema.prisma | 20 ++++++----- 2 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 prisma/migrations/20240504135407_add_points_to_entry/migration.sql diff --git a/prisma/migrations/20240504135407_add_points_to_entry/migration.sql b/prisma/migrations/20240504135407_add_points_to_entry/migration.sql new file mode 100644 index 0000000..8ae4a9f --- /dev/null +++ b/prisma/migrations/20240504135407_add_points_to_entry/migration.sql @@ -0,0 +1,33 @@ +/* + Warnings: + + - Added the required column `earnedPoints` to the `Entry` table without a default value. This is not possible if the table is not empty. + - Added the required column `sportMultiplier` to the `Entry` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Entry" ADD COLUMN "sportMultiplier" DOUBLE PRECISION; + +UPDATE "Entry" +SET "sportMultiplier" = + CASE + WHEN "sport" = 'swim' THEN 5 + WHEN "sport" IN ('run', 'walk') THEN 1 + WHEN "sport" IN ('ski', 'rollerski', 'rollerblade', 'skateboard') THEN 0.5 + WHEN "sport" = 'cycle' THEN 0.2 + END; + +ALTER TABLE "Entry" +ALTER COLUMN "sportMultiplier" SET NOT NULL; + +ALTER TABLE "Entry" ADD COLUMN "earnedPoints" DOUBLE PRECISION; + +UPDATE "Entry" +SET "earnedPoints" = + CASE + WHEN "doublePoints" THEN "distance" * "sportMultiplier" * 2 + ELSE "distance" * "sportMultiplier" + END; + +ALTER TABLE "Entry" +ALTER COLUMN "earnedPoints" SET NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 554f760..2bba04a 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -23,13 +23,15 @@ model User { } model Entry { - id Int @id @default(autoincrement()) - distance Float - fileId String - sport String - user User @relation(fields: [userId], references: [telegramUserId]) - doublePoints Boolean @default(false) - userId BigInt - createdAt DateTime @default(now()) - valid Boolean? + id Int @id @default(autoincrement()) + distance Float + fileId String + sport String + user User @relation(fields: [userId], references: [telegramUserId]) + doublePoints Boolean @default(false) + userId BigInt + createdAt DateTime @default(now()) + valid Boolean? + sportMultiplier Float + earnedPoints Float } From 474e8de9fd47eae1a15dab14909726b9fadfac99 Mon Sep 17 00:00:00 2001 From: mUusitalo <73437699+mUusitalo@users.noreply.github.com> Date: Sat, 4 May 2024 14:42:49 +0000 Subject: [PATCH 3/8] Calculate analytics in backend WIP --- src/analytics/statistics.ts | 6 ++++++ src/commands/entries.ts | 2 +- src/common/types.ts | 2 ++ src/entries.ts | 24 ++++++++++++++++++++++-- src/launchBotDependingOnNodeEnv.ts | 7 +++++++ 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 src/analytics/statistics.ts diff --git a/src/analytics/statistics.ts b/src/analytics/statistics.ts new file mode 100644 index 0000000..8459421 --- /dev/null +++ b/src/analytics/statistics.ts @@ -0,0 +1,6 @@ +import { prisma } from "../../config"; +import type { Statistics } from "../common/types"; + +export function getStatistics(): Statistics { + return new Map(); +} diff --git a/src/commands/entries.ts b/src/commands/entries.ts index 2d3fa1a..273785f 100644 --- a/src/commands/entries.ts +++ b/src/commands/entries.ts @@ -15,7 +15,7 @@ const entries = async ( if (entries.length > 0) { const validEntries = entries.filter((e) => e.valid !== false); const points = validEntries - .map((e) => e.distance * COEFFICIENTS[e.sport] * (e.doublePoints ? 2 : 1)) + .map((e) => e.earnedPoints) .reduce((p, e) => p + e, 0); const distance = validEntries.reduce((p, e) => p + e.distance, 0); diff --git a/src/common/types.ts b/src/common/types.ts index f15b796..49fe995 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -33,6 +33,8 @@ export type EntryWithoutId = { sport: Sport; userId: number; doublePoints: boolean; + earnedPoints: number; + sportMultiplier: number; }; export type Entry = EntryWithoutId & { diff --git a/src/entries.ts b/src/entries.ts index 769d408..ce688e4 100644 --- a/src/entries.ts +++ b/src/entries.ts @@ -4,6 +4,7 @@ import { prisma } from "../config"; import type { Entry, EntryWithUser } from "./common/types"; import { arrayToCSV } from "./common/utils"; import { isBigInteger, isCompleteEntry } from "./common/validators"; +import { COEFFICIENTS } from "./common/constants"; const entries = new Map>(); @@ -62,7 +63,17 @@ const setEntryValidation = async (entryId: number, valid: boolean) => { }; const setEntryDoublePoints = async (entryId: number, doublePoints: boolean) => { - await prisma.entry.update({ where: { id: entryId }, data: { doublePoints } }); + const oldEntry = await prisma.entry.findUniqueOrThrow({ + where: { id: entryId }, + }); + await prisma.entry.update({ + where: { id: entryId }, + data: { + doublePoints, + earnedPoints: + oldEntry.distance * oldEntry.sportMultiplier * (doublePoints ? 2 : 1), + }, + }); }; const removeLatest = async (userId: number) => { @@ -86,8 +97,17 @@ const entryToDb = async (chatId: number) => { if (!entry || !isCompleteEntry(entry)) throw new Error("Entry is not complete!"); + const sportMultiplier = COEFFICIENTS[entry.sport]; + await prisma.entry.create({ - data: entry, + data: { + ...entry, + sportMultiplier, + earnedPoints: + entry.distance * + sportMultiplier * + (entry.doublePoints ?? false ? 2 : 1), + }, }); entries.delete(chatId); }; diff --git a/src/launchBotDependingOnNodeEnv.ts b/src/launchBotDependingOnNodeEnv.ts index b8a62a6..9d1ba23 100644 --- a/src/launchBotDependingOnNodeEnv.ts +++ b/src/launchBotDependingOnNodeEnv.ts @@ -41,6 +41,13 @@ async function launchWebhookBot(bot: Telegraf>) { res.status(200).send(fs.readFileSync("./entries.csv")); }); + app.get("/statistics", async (req, res) => { + if (req.query.pass !== process.env.ADMIN_PASSWORD) { + console.log("Wrong password"); + return res.status(401).send("Wrong password!"); + } + }); + // Workaround to avoid issue with TSconfig const createWebhookListener = async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion From ca6fa61a66d6bf5c17210ad3447969e303cc0aca Mon Sep 17 00:00:00 2001 From: Niklas Linnanen Date: Sat, 4 May 2024 15:01:06 +0000 Subject: [PATCH 4/8] Move https server to seperate folder --- src/launchBotDependingOnNodeEnv.ts | 37 +++------------------------ src/server/index.ts | 41 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 34 deletions(-) create mode 100644 src/server/index.ts diff --git a/src/launchBotDependingOnNodeEnv.ts b/src/launchBotDependingOnNodeEnv.ts index 9d1ba23..67c93e9 100644 --- a/src/launchBotDependingOnNodeEnv.ts +++ b/src/launchBotDependingOnNodeEnv.ts @@ -4,11 +4,13 @@ import type { Context, Telegraf } from "telegraf"; import type { Update } from "telegraf/typings/core/types/typegram"; import { saveEntriesAsCSV } from "./entries"; +import app, { launchServer } from "./server"; /** * Launch bot in long polling (development) mode */ async function launchLongPollBot(bot: Telegraf>) { + launchServer(); await bot.launch(); } @@ -16,46 +18,13 @@ async function launchLongPollBot(bot: Telegraf>) { * Launch bot in webhook (production) mode */ async function launchWebhookBot(bot: Telegraf>) { - const port = Number.parseInt(process.env.PORT!); - - // Necessary because of Azure App Service health check on startup - const app = express(); - - app.get("/", (_req, res) => { - res.status(200).send("Kovaa tulee"); - }); - - app.get("/health", (_req, res) => { - res.status(200).send("OK"); - }); - - app.get("/entries", async (req, res) => { - if (req.query.pass !== process.env.ADMIN_PASSWORD) { - console.log("Wrong password"); - return res.status(401).send("Wrong password!"); - } - console.log("here"); - await saveEntriesAsCSV(); - res.attachment("./entries.csv"); - res.header("Content-Type", "text/csv"); - res.status(200).send(fs.readFileSync("./entries.csv")); - }); - - app.get("/statistics", async (req, res) => { - if (req.query.pass !== process.env.ADMIN_PASSWORD) { - console.log("Wrong password"); - return res.status(401).send("Wrong password!"); - } - }); - // Workaround to avoid issue with TSconfig const createWebhookListener = async () => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion app.use(await bot.createWebhook({ domain: process.env.DOMAIN! })); }; await createWebhookListener(); - - app.listen(port, () => console.log("Running on port ", port)); + launchServer(); } /** diff --git a/src/server/index.ts b/src/server/index.ts new file mode 100644 index 0000000..c992242 --- /dev/null +++ b/src/server/index.ts @@ -0,0 +1,41 @@ +import express from "express"; +import { saveEntriesAsCSV } from "../entries"; + +// biome-ignore lint/style/noNonNullAssertion: +const port = Number.parseInt(process.env.PORT!); + +const app = express(); + +// Necessary because of Azure App Service health check on startup +app.get("/", (_req, res) => { + res.status(200).send("Kovaa tulee"); +}); + +app.get("/health", (_req, res) => { + res.status(200).send("OK"); +}); + +app.get("/entries", async (req, res) => { + if (req.query.pass !== process.env.ADMIN_PASSWORD) { + console.log("Wrong password"); + return res.status(401).send("Wrong password!"); + } + console.log("here"); + await saveEntriesAsCSV(); + res.attachment("./entries.csv"); + res.header("Content-Type", "text/csv"); + res.status(200).send(fs.readFileSync("./entries.csv")); +}); + +app.get("/statistics", async (req, res) => { + if (req.query.pass !== process.env.ADMIN_PASSWORD) { + console.log("Wrong password"); + return res.status(401).send("Wrong password!"); + } +}); + +export const launchServer = async () => { + app.listen(port, () => console.log("Running on port ", port)); +} + +export default app; \ No newline at end of file From 510984680781bafc02ecdbb00ece6bd7a01500bf Mon Sep 17 00:00:00 2001 From: Niklas Linnanen Date: Sat, 4 May 2024 15:19:31 +0000 Subject: [PATCH 5/8] Add routes for stats --- src/server/index.ts | 8 ++----- src/server/statistics/routes.ts | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 6 deletions(-) create mode 100644 src/server/statistics/routes.ts diff --git a/src/server/index.ts b/src/server/index.ts index c992242..2dd4fe4 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,5 +1,6 @@ import express from "express"; import { saveEntriesAsCSV } from "../entries"; +import statisticsRouter from "./statistics/routes"; // biome-ignore lint/style/noNonNullAssertion: const port = Number.parseInt(process.env.PORT!); @@ -27,12 +28,7 @@ app.get("/entries", async (req, res) => { res.status(200).send(fs.readFileSync("./entries.csv")); }); -app.get("/statistics", async (req, res) => { - if (req.query.pass !== process.env.ADMIN_PASSWORD) { - console.log("Wrong password"); - return res.status(401).send("Wrong password!"); - } -}); +app.use("/statistics", statisticsRouter); export const launchServer = async () => { app.listen(port, () => console.log("Running on port ", port)); diff --git a/src/server/statistics/routes.ts b/src/server/statistics/routes.ts new file mode 100644 index 0000000..dfff6b5 --- /dev/null +++ b/src/server/statistics/routes.ts @@ -0,0 +1,39 @@ +import express from 'express'; + +const router = express.Router({mergeParams: true}); + + + +router.get('/total-points', (req, res) => { + // Logic to calculate and return the totalPoints +}); + +router.get('/:guild/total-kilometers', (req, res) => { + // Logic to calculate and return the totalKilometers +}); + +router.get('/:guild/total-entries', (req, res) => { + // Logic to calculate and return the totalEntries +}); + +router.get('/:guild/number-of-unique-participants', (req, res) => { + // Logic to calculate and return the numberOfUniqueParticipants +}); + +router.get('/:guild/proportion-of-continuing-participants', (req, res) => { + // Logic to calculate and return the proportionOfContinuingParticipants +}); + +router.get('/:guild/period', (req, res) => { + // Logic to calculate and return the period +}); + +router.get('/:guild/points-gained-in-period', (req, res) => { + // Logic to calculate and return the pointsGainedInPeriod +}); + +router.get('/:guild/proportion-of-milestone-achievers', (req, res) => { + // Logic to calculate and return the proportionOfMilestoneAchievers +}); + +export default router; From 67294046e992c2115bdc32749313bbb70ea178ad Mon Sep 17 00:00:00 2001 From: Niklas Linnanen Date: Sat, 4 May 2024 15:22:21 +0000 Subject: [PATCH 6/8] Validate guild query param on stats endpoints --- src/server/statistics/routes.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/server/statistics/routes.ts b/src/server/statistics/routes.ts index dfff6b5..d6b28c7 100644 --- a/src/server/statistics/routes.ts +++ b/src/server/statistics/routes.ts @@ -1,11 +1,25 @@ import express from 'express'; +import { isGuild } from '../../common/validators'; const router = express.Router({mergeParams: true}); +router.use((req, res, next) => { + const guild = req.query.guild; + if (!guild) { + return res.status(400).send('Guild query parameter is required'); + } + + if (!isGuild(guild)) { + return res.status(400).send('Invalid guild query parameter'); + } + + next(); +}); + router.get('/total-points', (req, res) => { - // Logic to calculate and return the totalPoints + // Logic to calculate and }); router.get('/:guild/total-kilometers', (req, res) => { From 1a98909fef4263fb0ec1ad4714f065df6814cd59 Mon Sep 17 00:00:00 2001 From: Niklas Linnanen Date: Sat, 4 May 2024 17:33:30 +0000 Subject: [PATCH 7/8] =?UTF-8?q?Make=20one=20single=20statistic=20route=20a?= =?UTF-8?q?nd=20add=20ry=C3=B6k=C3=A4le=20query=20=F0=9F=9A=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/analytics/statistics.ts | 117 +++++++++++++++++++++++++++- src/common/types.ts | 3 +- src/server/statistics/middleware.ts | 30 +++++++ src/server/statistics/routes.ts | 66 +++++----------- 4 files changed, 166 insertions(+), 50 deletions(-) create mode 100644 src/server/statistics/middleware.ts diff --git a/src/analytics/statistics.ts b/src/analytics/statistics.ts index 8459421..183f383 100644 --- a/src/analytics/statistics.ts +++ b/src/analytics/statistics.ts @@ -1,6 +1,117 @@ import { prisma } from "../../config"; -import type { Statistics } from "../common/types"; +import type { Guild, Statistics, TeamStatistics } from "../common/types"; -export function getStatistics(): Statistics { - return new Map(); +interface PeriodStats { + guild: Guild; + pointsGainedInPeriod: number; + continuingParticipants: number; + proportionOfContinuingParticipants: number; +} + +interface Aggregate { + guild: Guild; + totalPoints: number; + totalKilometers: number; + totalEntries: number; + numberOfUniqueParticipants: number; + milestoneAchievers: number; + proportionOfMilestoneAchievers: number; +} + +// Function to calculate and return the total points +export async function calculateGuildStatistics( + periodStart: Date, + periodEnd: Date, +): Promise { + const aggregates = (await prisma.$queryRaw` + SELECT + guild, + SUM("earnedPoints") as "totalPoints", + SUM(distance) as "totalKilometers", + COUNT(*) as "totalEntries", + COUNT(DISTINCT user) as "numberOfUniqueParticipants", + COUNT(CASE WHEN "earnedPoints" >= 50 THEN 1 END)/COUNT(DISTINCT user) as "proportionOfMilestoneAchievers" + FROM + "Entry" JOIN "User" ON "Entry"."userId" = "User"."telegramUserId" + GROUP BY + guild + `) as Aggregate[]; + + const periodStats = (await prisma.$queryRaw` + WITH period_stats AS ( + SELECT + guild, + SUM("earnedPoints") as "pointsGainedInPeriod", + COUNT(DISTINCT user) as "continuingParticipants" + FROM + "Entry" JOIN "User" ON "Entry"."userId" = "User"."telegramUserId" + WHERE + "Entry"."createdAt" BETWEEN ${periodStart} AND ${periodEnd} + GROUP BY + guild + ), + previous_users AS ( + SELECT + guild, + COUNT(DISTINCT user) as "previousParticipants" + FROM + "Entry" JOIN "User" ON "Entry"."userId" = "User"."telegramUserId" + WHERE + "Entry"."createdAt" < ${periodStart} + GROUP BY + guild + ) + + SELECT + period_stats.guild, + "pointsGainedInPeriod", + CASE WHEN "previousParticipants" = 0 THEN 0 ELSE "continuingParticipants"/"previousParticipants" END as "proportionOfContinuingParticipants" + FROM + period_stats LEFT JOIN previous_users ON period_stats.guild = previous_users.guild + `) as PeriodStats[]; + + const entriesInPeriod = await prisma.entry.findMany({ + where: { + createdAt: { + gte: periodStart, + lte: periodEnd, + }, + }, + }); + + const previousUsers = (await prisma.$queryRaw` + SELECT + guild, + COUNT(DISTINCT user) as "previousParticipants" + FROM + "Entry" JOIN "User" ON "Entry"."userId" = "User"."telegramUserId" + WHERE + "Entry"."createdAt" < ${periodStart} + GROUP BY + guild + `) as { guild: Guild; previousParticipants: number }[]; + + console.log(entriesInPeriod); + const statistics = new Map(); + + for (const aggregate of aggregates) { + const periodStat = periodStats.find( + (stat) => stat.guild === aggregate.guild, + ); + console.log("period", periodStats); + statistics.set(aggregate.guild, { + totalPoints: aggregate.totalPoints, + totalKilometers: aggregate.totalKilometers, + totalEntries: Number(aggregate.totalEntries), + numberOfUniqueParticipants: Number(aggregate.numberOfUniqueParticipants), + proportionOfContinuingParticipants: + Number(periodStat?.proportionOfContinuingParticipants) || 0, + pointsGainedInPeriod: Number(periodStat?.pointsGainedInPeriod) || 0, + proportionOfMilestoneAchievers: Number( + aggregate.proportionOfMilestoneAchievers, + ), + }); + } + + return statistics; } diff --git a/src/common/types.ts b/src/common/types.ts index 49fe995..ca68093 100644 --- a/src/common/types.ts +++ b/src/common/types.ts @@ -79,10 +79,9 @@ export type PhotoCtxType = NarrowedContext< export type TeamStatistics = { totalPoints: number; totalKilometers: number; - totalEntires: number; + totalEntries: number; numberOfUniqueParticipants: number; proportionOfContinuingParticipants: number; - period: number; pointsGainedInPeriod: number; proportionOfMilestoneAchievers: number; }; diff --git a/src/server/statistics/middleware.ts b/src/server/statistics/middleware.ts new file mode 100644 index 0000000..5a94afc --- /dev/null +++ b/src/server/statistics/middleware.ts @@ -0,0 +1,30 @@ +import type { NextFunction, Request, Response } from "express"; + +export const validatePeriod = (req: Request, res: Response, next: NextFunction) => { + const start = req.query.start; + const end = req.query.end; + + if (!start || !end) { + return res.status(400).send('Period start and end query parameters are required'); + } + + if (typeof start !== 'string' || typeof end !== 'string') { + return res.status(400).send('Invalid period start and end query parameters'); + } + + if (Number.isNaN(Date.parse(start)) || Number.isNaN(Date.parse(end))) { + return res.status(400).send('Period start and end query parameters must be valid dates'); + } + + const startDate = new Date(start); + const endDate = new Date(end); + + if (startDate > endDate) { + return res.status(400).send('Period start must be before period end'); + } + + res.locals.periodStart = startDate; + res.locals.periodEnd = endDate; + + next(); +} \ No newline at end of file diff --git a/src/server/statistics/routes.ts b/src/server/statistics/routes.ts index d6b28c7..7fa1c6e 100644 --- a/src/server/statistics/routes.ts +++ b/src/server/statistics/routes.ts @@ -1,53 +1,29 @@ import express from 'express'; -import { isGuild } from '../../common/validators'; +import { validatePeriod } from './middleware'; +import { calculateGuildStatistics } from '../../analytics/statistics'; const router = express.Router({mergeParams: true}); - -router.use((req, res, next) => { - const guild = req.query.guild; - if (!guild) { - return res.status(400).send('Guild query parameter is required'); - } - - if (!isGuild(guild)) { - return res.status(400).send('Invalid guild query parameter'); +export interface StatisticsResponse extends express.Response { + locals: { + guild: string; + periodStart: Date; + periodEnd: Date; + }; +} + +// Middleware to validate the period start and end query parameters +router.use(validatePeriod); + +router.get('/', async (req, res: StatisticsResponse) => { + const { periodStart, periodEnd } = res.locals; + try { + const statistics = await calculateGuildStatistics(periodStart, periodEnd); + res.json(Object.fromEntries(statistics)); + } catch (error) { + console.error(error); + res.status(500).send('An error occurred while calculating statistics'); } - - next(); -}); - - -router.get('/total-points', (req, res) => { - // Logic to calculate and -}); - -router.get('/:guild/total-kilometers', (req, res) => { - // Logic to calculate and return the totalKilometers -}); - -router.get('/:guild/total-entries', (req, res) => { - // Logic to calculate and return the totalEntries -}); - -router.get('/:guild/number-of-unique-participants', (req, res) => { - // Logic to calculate and return the numberOfUniqueParticipants -}); - -router.get('/:guild/proportion-of-continuing-participants', (req, res) => { - // Logic to calculate and return the proportionOfContinuingParticipants -}); - -router.get('/:guild/period', (req, res) => { - // Logic to calculate and return the period -}); - -router.get('/:guild/points-gained-in-period', (req, res) => { - // Logic to calculate and return the pointsGainedInPeriod -}); - -router.get('/:guild/proportion-of-milestone-achievers', (req, res) => { - // Logic to calculate and return the proportionOfMilestoneAchievers }); export default router; From 8ef2f41745471460d6a0379a2b81f754073aba03 Mon Sep 17 00:00:00 2001 From: Niklas Linnanen Date: Sat, 4 May 2024 17:52:15 +0000 Subject: [PATCH 8/8] Add admin auth middleware --- src/server/index.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/server/index.ts b/src/server/index.ts index 2dd4fe4..c5922b8 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -16,12 +16,15 @@ app.get("/health", (_req, res) => { res.status(200).send("OK"); }); -app.get("/entries", async (req, res) => { +app.use(async (req, res, next) => { if (req.query.pass !== process.env.ADMIN_PASSWORD) { console.log("Wrong password"); return res.status(401).send("Wrong password!"); } - console.log("here"); + next(); +}) + +app.get("/entries", async (req, res) => { await saveEntriesAsCSV(); res.attachment("./entries.csv"); res.header("Content-Type", "text/csv");