From 36913823f20fcaae8fda76e319917f2f2adc21f9 Mon Sep 17 00:00:00 2001 From: Jonas Simoen Date: Sat, 18 May 2024 23:09:24 +0200 Subject: [PATCH] v2024.05.18: commit 2ed88ecb48e616309092969811305feace95da8f Author: Jonas Simoen Date: Sat May 18 23:00:58 2024 +0200 chore: remove logs commit 72be48a11d2b0af8df81c13f88765d9c200539b4 Author: Jonas Simoen Date: Sat May 18 22:57:52 2024 +0200 fix(login): return to app when access denied commit 26b55d76c8333aeffc3425185fbadf599de740e5 Author: Jonas Simoen Date: Sat May 18 22:45:34 2024 +0200 feat(news): add news articles commit 2b6a89153f86ff31ef119240011c83d8f510336f Author: Jonas Simoen Date: Fri May 17 19:09:32 2024 +0200 remove redirect_to_welcome commit a3e0c894a4301df6a99ce72787ef30f2d7032262 Author: Jonas Simoen Date: Fri May 17 13:05:20 2024 +0200 feat(notif) commit af7225689a3823a1d8230564c5a5953a4e95be03 Author: Jonas Simoen Date: Thu May 16 22:04:30 2024 +0200 feat(audit): search audits commit 72e7b422a500edf475005dc32d4b9519c3a063e5 Author: Jonas Simoen Date: Thu May 16 20:40:40 2024 +0200 chore(team): quicker db selects commit 3cdb6139bc73b74d6ee4e61dd2eb461b7617061f Author: Jonas Simoen Date: Thu May 16 20:18:30 2024 +0200 feat(general): user list commit 22a9ca782287e66c5856f8a25ab6db69699d3f79 Author: Jonas Simoen Date: Thu May 16 18:29:35 2024 +0200 fix(boosters) --- api/index.ts | 11 +- package.json | 1 + prisma/schema.prisma | 29 +++- src/controllers/General.ts | 88 +++++++++- src/controllers/News.ts | 57 ++++++ src/controllers/Notification.ts | 38 ++++ src/controllers/Statistic.ts | 2 - src/controllers/Team.ts | 298 +++++++++++++++++--------------- src/controllers/User.ts | 13 +- src/controllers/UserAuth.ts | 5 +- src/controllers/Week.ts | 1 - src/middleware/RequireAdmin.ts | 2 +- src/routers/Admin.ts | 18 +- src/routers/Main.ts | 42 +++-- src/routers/News.ts | 21 +++ src/routers/Notification.ts | 22 +++ src/routers/User.ts | 12 +- src/types/body-schema.ts | 21 +++ src/types/http.d.ts | 2 +- src/utils/PointsCalculator.ts | 3 - 20 files changed, 480 insertions(+), 206 deletions(-) create mode 100644 src/controllers/News.ts create mode 100644 src/controllers/Notification.ts create mode 100644 src/routers/News.ts create mode 100644 src/routers/Notification.ts diff --git a/api/index.ts b/api/index.ts index c23e60c..834af06 100644 --- a/api/index.ts +++ b/api/index.ts @@ -1,14 +1,16 @@ import fastify from "fastify"; import cookies from "@fastify/cookie"; import cors from "@fastify/cors"; +import { applicationDefault, initializeApp } from "firebase-admin/app"; +import admin from "firebase-admin"; import { AdminRouter, PublicRouter } from "../src/routers/Main"; import HttpError from "../src/utils/HttpError"; -import dotenv from "dotenv"; import { deserializeUser } from "../src/utils/DeserializeUser"; import fastifyStatic from "@fastify/static"; import path from "path"; import _default from "fastify-metrics"; +import dotenv from "dotenv"; dotenv.config(); export const server = fastify({ @@ -18,6 +20,13 @@ export const server = fastify({ disableRequestLogging: true, }); + +export const app = initializeApp({ + credential: admin.credential.cert(JSON.parse( + process.env.FIREBASE_ACCOUNT_KEY as string + )) +}); + server.addHook("preHandler", deserializeUser); server.register(require("fastify-list-routes"), { colors: true }); diff --git a/package.json b/package.json index c4e5683..bd855d1 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "fastify-list-routes": "^1.0.0", "fastify-metrics": "^10.6.0", "fastify-stripe": "^2.4.1", + "firebase-admin": "^12.1.0", "fs": "^0.0.1-security", "jsonwebtoken": "^9.0.0", "lodash": "^4.17.21", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 45ed96a..4e3c0ae 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -47,8 +47,10 @@ model User { role Int payed Boolean? @default(false) - teams Team[] - Audit Audit[] + teams Team[] + Audit Audit[] + NotificationToken NotificationToken[] + Article Article[] } model Player { @@ -120,7 +122,7 @@ model Statistic { blocks Int @default(0) interceptions Int @default(0) tackles Int @default(0) - lineClearances Int @default(0) + lineClearances Int @default(0) // Dribbles dribblesAttempted Int @default(0) dribblesSuccess Int @default(0) @@ -314,6 +316,27 @@ model Audit { user User @relation(fields: [userId], references: [id]) } +model NotificationToken { + id Int @id @default(autoincrement()) + token String + timestamp DateTime + user User @relation(fields: [userId], references: [id]) + userId Int +} + +model Article { + id Int @id @default(autoincrement()) + slug String + title String + description String + imageUrl String? + readMore Boolean? + timestampCreated DateTime + timestampUpdated DateTime? + author User @relation(fields: [authorId], references: [id]) + authorId Int +} + // model WeeksOnTeams { // week Week @relation(fields: [weekId], references: [id]) // team Team @relation(fields: [teamId], references: [id]) diff --git a/src/controllers/General.ts b/src/controllers/General.ts index 66f620c..16260af 100644 --- a/src/controllers/General.ts +++ b/src/controllers/General.ts @@ -1,3 +1,4 @@ +import { upcomingWeekId } from "../utils/Common"; import { prisma } from "../db/client"; export const GeneralInfoHandler = async (req: any, rep: any) => { @@ -6,14 +7,6 @@ export const GeneralInfoHandler = async (req: any, rep: any) => { userCount: await prisma.user.count(), teamCount: await prisma.team.count(), clubWinner: await prisma.club.findFirst({where: {winner: true}}), - users: await prisma.user.findMany({ - select: { - firstName: true, - lastName: true, - email: true, - payed: true, - } - }) // teamCount: await prisma.team.count(), }); } @@ -29,4 +22,83 @@ export const PostClubWinnerHandler = async (req: any, rep: any) => { }); await prisma.$queryRaw`CALL "processWinner"()`; rep.send({message: "Winnaar succesvol gewijzigd."}) +} + +export const GetUserOverview = async(req: any, rep: any) => { + const weekId = await upcomingWeekId(); + + const [users, teamsWithActiveBoosters, audits] = await Promise.all([ + prisma.user.findMany({ + select: { + id: true, + email: true, + firstName: true, + lastName: true, + payed: true, + } + }), + prisma.team.findMany({ + select: { + user: { + select: { + email: true, + } + }, + tripleCaptain: true, + viceVictory: true, + freeHit: true, + superSubs: true, + hiddenGem: true, + goalRush: true, + }, + where: { + OR: [ + {tripleCaptain: weekId}, + {viceVictory: weekId}, + {freeHit: weekId}, + {superSubs: weekId}, + {hiddenGem: weekId}, + {goalRush: weekId}, + ], + } + }), + prisma.audit.findMany({ + take: 50, + select: { + id: true, + timestamp: true, + action: true, + user: { + select: { + email: true, + } + }, + }, + orderBy: { + timestamp: "desc" + } + }) + ]); + + rep.send({ + users, + activeBoosters: teamsWithActiveBoosters.map((u: any) => ({ + user: u.user.email, + boosters: Object.keys(u).filter((v: string) => u[v] == weekId) + })), + audits + }) +} + +export const GetAuditHandler = async(req: any, rep: any) => { + const audit = await prisma.audit.findFirst({ + where: { + id: +req.params.id + }, + select: { + action: true, + params: true, + } + }); + rep.send(audit); } \ No newline at end of file diff --git a/src/controllers/News.ts b/src/controllers/News.ts new file mode 100644 index 0000000..2972434 --- /dev/null +++ b/src/controllers/News.ts @@ -0,0 +1,57 @@ +import { prisma } from "../db/client" + +export const GetArticlesHandler = async (req: any, rep: any) => { + const page = +req.query.page || 1; + const [articleCount, articles] = await Promise.all([ + await prisma.article.count(), + await prisma.article.findMany({ + select: { + id: true, + slug: true, + title: true, + description: true, + timestampCreated: true, + timestampUpdated: true, + readMore: true, + imageUrl: true, + author: { + select: { + firstName: true, + } + } + }, + orderBy: { + timestampCreated: 'desc' + }, + take: 5, + skip: (page - 1) * 5 + }), + ]) + rep.send({ + articles, + count: articleCount, + }) +} +export const GetArticleHandler = async (req: any, rep: any) => { + const articles = await prisma.article.findFirst({ + select: { + id: true, + slug: true, + title: true, + description: true, + timestampCreated: true, + timestampUpdated: true, + imageUrl: true, + readMore: true, + author: { + select: { + firstName: true, + } + } + }, + where: { + slug: req.params.slug + } + }); + rep.send(articles) +} \ No newline at end of file diff --git a/src/controllers/Notification.ts b/src/controllers/Notification.ts new file mode 100644 index 0000000..6fef5e9 --- /dev/null +++ b/src/controllers/Notification.ts @@ -0,0 +1,38 @@ +import { getMessaging } from "firebase-admin/messaging"; +import { app } from "../../api/index" +import { prisma } from "../db/client"; + +export const RegisterTokenHandler = async(req: any, rep: any) => { + getMessaging(app).subscribeToTopic(req.body.token, "edd-app"); + const token = await prisma.notificationToken.create({ + data: { + token: req.body.token, + user: { + connect: { + id: +req.user.id + } + }, + timestamp: new Date().toISOString(), + } + }) + rep.status(200); +} + +export const PushNotificationHandler = async(req: any, rep: any) => { + + const id = await getMessaging(app).send({ + topic: 'edd-app', + webpush: { + notification: { + title: req.body.title || "", + body: req.body.body || "", + badge: req.body.photo || "", + icon: req.body.photo || "", + }, + fcmOptions: { + link: req.body.link || "", + } + } + }) + rep.send(id); +} \ No newline at end of file diff --git a/src/controllers/Statistic.ts b/src/controllers/Statistic.ts index 1c7640b..22820a8 100644 --- a/src/controllers/Statistic.ts +++ b/src/controllers/Statistic.ts @@ -19,7 +19,6 @@ export const GetPlayerStatisticsHandler = async (req: any, rep: any) => { })).length; const matchdayFilter = req.query.matchday ? { id: { equals: +req.query.matchday } } : { validated: true } - console.log(matchdayFilter); const players = await prisma.player.findMany({ cacheStrategy: { @@ -361,7 +360,6 @@ export const ImportMatchStatisticHandler = async (req: any, rep: any) => { } const converted = res.data.response.map((resp: any) => { return resp.players.map((player: any) => { - // console.log(player) const stats = player.statistics[0]; return ({ diff --git a/src/controllers/Team.ts b/src/controllers/Team.ts index c6bb320..3869734 100644 --- a/src/controllers/Team.ts +++ b/src/controllers/Team.ts @@ -18,7 +18,6 @@ const GetPlayerByIds = async (allPlayerIds: number[], reqBody: any, weekId: numb value: true, }, }); - console.log(allPlayers); // Business rules checks // 1. Correct positions @@ -69,8 +68,7 @@ export const PostAddTeamHandler = async (req: any, rep: any) => { } const allIds = req.body.starting.concat(req.body.bench); - const weekId = await upcomingWeekId(); - const lastWeekId = await finalWeekId(); + const [weekId, lastWeekId] = await Promise.all([upcomingWeekId(), finalWeekId()]); const allWithValues = await GetPlayerByIds(allIds, req.body, weekId); @@ -139,142 +137,159 @@ export const DeleteDropTeamHandler = async (req: any, rep: any) => { export const GetTeamHandler = async (req: any, rep: any) => { const weekId = await upcomingWeekId(); - const playersWithMultipleSelections = await prisma.player.findMany({ - where: { - selections: { - some: { - teamId: +req.params.id, - weekId, + const [playersWithMultipleSelections, team, transfers] = await Promise.all([ + prisma.player.findMany({ + where: { + selections: { + some: { + teamId: +req.params.id, + weekId, + } } + }, + include: { + selections: { + where: { + teamId: +req.params.id, + weekId, + }, + select: { + captain: true, + starting: true, + value: true, + playerId: true, + weekId: true, + booster: true, + order: true, + }, + } + }, + }), + + prisma.team.findFirst({ + cacheStrategy: { + ttl: 30, + swr: 60, + }, + where: { + id: +req.params.id + }, + include: { + user: true } - }, - include: { - selections: { - where: { - teamId: +req.params.id, - weekId, - }, - select: { - captain: true, - starting: true, - value: true, - playerId: true, - weekId: true, - booster: true, - order: true, - }, - } - }, - }); - const players = playersWithMultipleSelections.sort((a, b) => a.selections[0].order! - b.selections[0].order!) - .map(({ selections, ...rest }) => { - const {order, ...sel} = selections[0]; - return { - ...rest, - selection: sel - } - }) + }), - const team = await prisma.team.findFirst({ - cacheStrategy: { - ttl: 30, - swr: 60, - }, - where: { - id: +req.params.id - }, - include: { - user: true - } + prisma.transfer.findMany({ + where: { + teamId: +req.params.id, + weekId, + } + }), + ]) + + rep.send({ + team: { ...team }, + players: playersWithMultipleSelections + .sort((a, b) => a.selections[0].order! - b.selections[0].order!) + .map(({ selections, ...rest }) => { + const {order, ...sel} = selections[0]; + return { + ...rest, + selection: sel + } + }), + transfers: transfers }); - - const transfers = await prisma.transfer.findMany({ - where: { - teamId: +req.params.id, - weekId, - } - }) - rep.send({ team: { ...team }, players, transfers: transfers }); } export const GetPointsTeamHandler = async (req: any, rep: any) => { - const team = await prisma.team.findUnique({ - cacheStrategy: { - ttl: 30, - swr: 60, - }, - where: { - id: +req.params.id, - } - }); - if(!team) { - rep.status(404); - } - const players = await prisma.player.findMany({ - cacheStrategy: { - ttl: 30, - swr: 60, - }, - include: { - selections: { - select: { - captain: true, - starting: true, - value: true, - playerId: true, - weekId: true, - booster: true, - played: true, - endWinnerSelections: true, - points: true, - order: true, - }, - where: { - teamId: +req.params.id, - weekId: +req.params.weekId, + const [ + team, + players, + deadlineWeek, + transfers, + weeklyData, + globalData + ]: [any, any, any, any, any, any] = await Promise.all([ + prisma.team.findUnique({ + cacheStrategy: { + ttl: 30, + swr: 60, + }, + where: { + id: +req.params.id, + } + }).catch((err: any) => rep.status(404)), + + prisma.player.findMany({ + cacheStrategy: { + ttl: 30, + swr: 60, + }, + include: { + selections: { + select: { + captain: true, + starting: true, + value: true, + playerId: true, + weekId: true, + booster: true, + played: true, + endWinnerSelections: true, + points: true, + order: true, + }, + where: { + teamId: +req.params.id, + weekId: +req.params.weekId, + }, }, + stats: { + where: { + match: { + weekId: +req.params.weekId, + } + } + } }, - stats: { - where: { - match: { + where: { + selections: { + some: { + teamId: +req.params.id, weekId: +req.params.weekId, } } + }, + }), + + prisma.week.findFirst({ + cacheStrategy: { + ttl: 30, + swr: 60, + }, + where: { + id: +req.params.weekId } - }, - where: { - selections: { - some: { - teamId: +req.params.id, - weekId: +req.params.weekId, - } + }), + + prisma.transfer.findMany({ + cacheStrategy: { + ttl: 30, + swr: 60, + }, + where: { + teamId: +req.params.id, + weekId: +req.params.weekId, } - }, - }); - const deadlineWeek = await prisma.week.findFirst({ - cacheStrategy: { - ttl: 30, - swr: 60, - }, - where: { - id: +req.params.weekId - } - }); - const transfers = await prisma.transfer.findMany({ - cacheStrategy: { - ttl: 30, - swr: 60, - }, - where: { - teamId: +req.params.id, - weekId: +req.params.weekId, - } - }); - const weeklyData: [{ teamId: number, points: number, rank: number }] = await prisma.$queryRaw`SELECT "teamId", CAST(SUM(points) AS int) AS points, CAST(RANK() OVER(ORDER BY SUM(points) DESC) AS int) FROM "Selection" s WHERE "weekId" = ${+req.params.weekId} AND starting = 1 GROUP BY "teamId" ORDER BY rank ASC`; - const globalData: [{ teamId: number, points: number, rank: number }] = await prisma.$queryRaw`SELECT "teamId", CAST(SUM(points) AS int) AS points, CAST(RANK() OVER(ORDER BY SUM(points) DESC) AS int) FROM "Selection" s WHERE starting = 1 GROUP BY "teamId" ORDER BY rank ASC`; + }), + + prisma.$queryRaw`SELECT "teamId", CAST(SUM(points) AS int) AS points, CAST(RANK() OVER(ORDER BY SUM(points) DESC) AS int) FROM "Selection" s WHERE "weekId" = ${+req.params.weekId} AND starting = 1 GROUP BY "teamId" ORDER BY rank ASC`, + prisma.$queryRaw`SELECT "teamId", CAST(SUM(points) AS int) AS points, CAST(RANK() OVER(ORDER BY SUM(points) DESC) AS int) FROM "Selection" s WHERE starting = 1 GROUP BY "teamId" ORDER BY rank ASC`, + ]); rep.send({ - players: players.sort((a, b) => a.selections[0].order! - b.selections[0].order!), + players: players.sort((a: any, b: any) => a.selections[0].order! - b.selections[0].order!), team: { ...team, rank: globalData.find((teamData: any) => teamData.teamId === +req.params.id)?.rank || 0, @@ -302,7 +317,7 @@ export const GetPointsTeamHandler = async (req: any, rep: any) => { export const PostBoosterTeamHandler = async (req: any, rep: any) => { const boosterUnCC = req.body.type.charAt(0).toUpperCase() + req.body.type.slice(1); - const validBoosters = ["tripleCaptain","viceVictory","hiddenGem","goalRush","superSubs"]; + const validBoosters = ["tripleCaptain","viceVictory","hiddenGem","goalRush","superSubs","freeHit"]; if(!validBoosters.includes(req.body.type)) throw new HttpError("Invalid booster", 404) @@ -391,24 +406,23 @@ export const PostEditTeamHandler = async (req: any, rep: any) => { if (req.body.bench?.length != 4 || req.body.starting?.length != 11) { throw new HttpError("Invalid team: invalid number of players", 400); } - const weekId = await upcomingWeekId(); - const lastWeekId = await finalWeekId(); - const team = await prisma.team.findFirst({ - where: { - id: +req.params.id - } - }); - let type = 'BEFORE_START' + const [weekId, lastWeekId, team] = await Promise.all([ + upcomingWeekId(), + finalWeekId(), + prisma.team.findFirst({ + where: { + id: +req.params.id + } + }), + ]); + + const hasFreeHit = team?.freeHit && (team?.freeHit == weekId) - if(weekId > +(process.env.OFFICIAL_START_WEEK || 0)) { + if((weekId > +(process.env.OFFICIAL_START_WEEK || 0)) && !hasFreeHit) { throw new HttpError("Editing is not allowed anymore.", 400); } - if(team?.freeHit && (team?.freeHit == weekId)) { - type = 'FREE_HIT' - } - const allIds = req.body.starting.concat(req.body.bench); let allWithValues = await GetPlayerByIds(allIds, req.body, weekId); @@ -421,7 +435,7 @@ export const PostEditTeamHandler = async (req: any, rep: any) => { throw new HttpError(`Invalid team: invalid budget (${budget})`, 400); } - if (type !== 'FREE_HIT') { + if (!hasFreeHit) { // If editing team, all selections should be updated, except if it's for FREE HIT booster. (only 1 gameday!) weekIds = Array.from(Array(lastWeekId - weekId + 1).keys()).map(x => x + weekId); allWithValues = allWithValues!.flatMap((p: any) => weekIds.map(wId => ({ ...p, weekId: wId }))); @@ -457,7 +471,7 @@ export const PostEditTeamHandler = async (req: any, rep: any) => { prisma.audit.create({ data: { userId: req.user.id, - action: `EDIT_TEAM_${type}`, + action: `EDIT_TEAM_${hasFreeHit ? 'FREE_HIT' : 'BEFORE_START'}`, params: JSON.stringify({ userId: req.user.id, selections: allWithValues, @@ -477,8 +491,7 @@ export const PostEditTeamHandler = async (req: any, rep: any) => { } export const PostSelectionsTeamHandler = async (req: any, rep: any) => { - const weekId = await upcomingWeekId(); - const lastWeekId = await finalWeekId(); + const [weekId, lastWeekId] = await Promise.all([upcomingWeekId(), finalWeekId()]); const remainingWeekIds = Array.from(Array(lastWeekId - weekId + 1).keys()).map(x => x + weekId); if (req.body.bench.length != 4 || req.body.starting.length != 11) { @@ -544,8 +557,7 @@ export const PostSelectionsTeamHandler = async (req: any, rep: any) => { export const PostTransfersTeamHandler = async (req: any, rep: any) => { const transfers = req.body.transfers; - const weekId = await upcomingWeekId(); - const lastWeekId = await finalWeekId(); + const [weekId, lastWeekId] = await Promise.all([upcomingWeekId(), finalWeekId()]); const remainingWeekIds = Array.from(Array(lastWeekId - weekId + 1).keys()).map(x => x + weekId); if (transfers) { diff --git a/src/controllers/User.ts b/src/controllers/User.ts index f7daa53..2d6573e 100644 --- a/src/controllers/User.ts +++ b/src/controllers/User.ts @@ -64,17 +64,6 @@ export const PaymentResultHandler = async (req: any, rep: any) => { ); } -export const PutUserHandler = async(req: any, rep: any) => { - const user = await prisma.user.update({ - where: { - id: req.user.id - }, - data: { - - } - }) -} - export const GetTeamsHandler = async (req: any, rep: any) => { const user = await prisma.user.findUnique({ cacheStrategy: { @@ -101,5 +90,5 @@ export const GetTeamsHandler = async (req: any, rep: any) => { } } }) - rep.send({teams, user}); + rep.send({teams, user}); } \ No newline at end of file diff --git a/src/controllers/UserAuth.ts b/src/controllers/UserAuth.ts index eae308a..9d8fb89 100644 --- a/src/controllers/UserAuth.ts +++ b/src/controllers/UserAuth.ts @@ -19,6 +19,9 @@ const refreshTokenCookieOptions: CookieSerializeOptions = { }; export const GoogleAuthHandler = async (req: AccessTokenRequest, rep: any) => { + if(req.query.error) { + rep.redirect(process.env.WEBAPP_URL); + } const code = req.query.code as string try { const { id_token, access_token } = await getGoogleOAuthTokens({ code }); @@ -51,7 +54,7 @@ export const GoogleAuthHandler = async (req: AccessTokenRequest, rep: any) => { const refreshToken = signJwt({ ...user }, { expiresIn: "30d" }); rep.setCookie("token", accessToken, accessTokenCookieOptions); rep.setCookie("refreshToken", refreshToken, refreshTokenCookieOptions); - rep.redirect(`${process.env.WEBAPP_URL}/login/callback?${qs.stringify({ token: accessToken, refreshToken: refreshToken, welcomeRedirect: process.env.REDIR_TO_WELCOME && firstSignIn })}`); + rep.redirect(`${process.env.WEBAPP_URL}/login/callback?${qs.stringify({ token: accessToken, refreshToken: refreshToken })}`); // rep.send(googleUserInfo) diff --git a/src/controllers/Week.ts b/src/controllers/Week.ts index 9a4aedd..18b105f 100644 --- a/src/controllers/Week.ts +++ b/src/controllers/Week.ts @@ -83,7 +83,6 @@ export const PostWeekValidateHandler = async (req: any, rep: any) => { points: true } }); - console.log("teamPoints",teamPoints); const teamsWithSelections = await prisma.team.findMany({ cacheStrategy: { diff --git a/src/middleware/RequireAdmin.ts b/src/middleware/RequireAdmin.ts index c16a467..fc86417 100644 --- a/src/middleware/RequireAdmin.ts +++ b/src/middleware/RequireAdmin.ts @@ -1,6 +1,6 @@ import HttpError from "../utils/HttpError"; -export const requireAdmin = (req: any, rep: any, done: any) => { +export const RequireAdmin = (req: any, rep: any, done: any) => { const user = req.user; if (user.role != 7) { diff --git a/src/routers/Admin.ts b/src/routers/Admin.ts index 4fd8f25..eb7648e 100644 --- a/src/routers/Admin.ts +++ b/src/routers/Admin.ts @@ -1,20 +1,32 @@ import { FastifyPluginAsync } from "fastify"; import { GeneralClubWinnerSchema } from "../types/body-schema"; import { RequireUser } from "../middleware/RequireUser"; -import { GeneralInfoHandler, PostClubWinnerHandler } from "../controllers/General"; +import { GeneralInfoHandler, GetAuditHandler, GetUserOverview, PostClubWinnerHandler } from "../controllers/General"; export const AdminGeneralRouter: FastifyPluginAsync = async server => { server.route({ method: "GET", - url: '/general', + url: '/', preHandler: RequireUser, handler: GeneralInfoHandler, }); server.route({ method: "POST", - url: '/general/winner', + url: '/winner', preHandler: RequireUser, schema: GeneralClubWinnerSchema, handler: PostClubWinnerHandler, }); + server.route({ + method: "GET", + url: '/users', + preHandler: RequireUser, + handler: GetUserOverview + }); + server.route({ + method: "GET", + url: '/audit/:id', + preHandler: RequireUser, + handler: GetAuditHandler + }); } \ No newline at end of file diff --git a/src/routers/Main.ts b/src/routers/Main.ts index 5f8ad18..7e35b59 100644 --- a/src/routers/Main.ts +++ b/src/routers/Main.ts @@ -3,34 +3,38 @@ import { FastifyPluginAsync } from "fastify"; import { TeamRouter } from "./Team"; import { AdminPlayerRouter, PublicPlayerRouter } from './Player'; import { AdminClubRouter, PublicClubRouter } from './Club'; -import { requireAdmin } from '../middleware//RequireAdmin'; +import { RequireAdmin } from '../middleware//RequireAdmin'; import { AdminMatchRouter, PublicMatchRouter } from './Match'; import { AdminMatchEventRouter, PublicMatchEventRouter } from './MatchEvent'; import { AdminMatchStatisticRouter, PublicMatchStatisticRouter, PublicPlayerStatisticRouter } from './Statistic'; import { AdminWeekRouter, PublicWeekRouter } from './Week'; import { PublicPageRouter } from './Page'; import { AdminGeneralRouter } from './Admin'; +import { NotificationRouter } from './Notification'; +import { PublicNewsRouter } from './News'; export const PublicRouter: FastifyPluginAsync = async server => { - server.register(UserRouter, { prefix: '/user' }); - server.register(TeamRouter, { prefix: '/teams' }); - server.register(PublicPlayerRouter, { prefix: '/players' }) - server.register(PublicClubRouter, { prefix: '/clubs' }) - server.register(PublicMatchRouter, { prefix: '/matches' }) - server.register(PublicMatchEventRouter, { prefix: '/matches/:matchId/events' }) - server.register(PublicMatchStatisticRouter, { prefix: '/matches/:matchId/stats' }) - server.register(PublicWeekRouter, { prefix: '/weeks' }) - server.register(PublicPlayerStatisticRouter, { prefix: '/player-stats' }) - server.register(PublicPageRouter, {prefix: '/pages'}) + server.register(NotificationRouter, { prefix: '/notifications' }) + server.register(UserRouter, { prefix: '/user' }); + server.register(TeamRouter, { prefix: '/teams' }); + server.register(PublicPlayerRouter, { prefix: '/players' }) + server.register(PublicClubRouter, { prefix: '/clubs' }) + server.register(PublicMatchRouter, { prefix: '/matches' }) + server.register(PublicMatchEventRouter, { prefix: '/matches/:matchId/events' }) + server.register(PublicMatchStatisticRouter, { prefix: '/matches/:matchId/stats' }) + server.register(PublicWeekRouter, { prefix: '/weeks' }) + server.register(PublicPlayerStatisticRouter, { prefix: '/player-stats' }) + server.register(PublicPageRouter, {prefix: '/pages'}) + server.register(PublicNewsRouter, {prefix: '/news'}) } export const AdminRouter: FastifyPluginAsync = async server => { - server.addHook("preHandler", requireAdmin) - server.register(AdminGeneralRouter) - server.register(AdminPlayerRouter, { prefix: '/players' }) - server.register(AdminClubRouter, { prefix: '/clubs' }) - server.register(AdminMatchRouter, { prefix: '/matches' }) - server.register(AdminMatchEventRouter, { prefix: '/matches/:matchId/events' }) - server.register(AdminMatchStatisticRouter, { prefix: '/matches/:matchId/stats' }) - server.register(AdminWeekRouter, { prefix: '/weeks' }) + server.addHook("preHandler", RequireAdmin) + server.register(AdminGeneralRouter, { prefix: '/general' }) + server.register(AdminPlayerRouter, { prefix: '/players' }) + server.register(AdminClubRouter, { prefix: '/clubs' }) + server.register(AdminMatchRouter, { prefix: '/matches' }) + server.register(AdminMatchEventRouter, { prefix: '/matches/:matchId/events' }) + server.register(AdminMatchStatisticRouter, { prefix: '/matches/:matchId/stats' }) + server.register(AdminWeekRouter, { prefix: '/weeks' }) } \ No newline at end of file diff --git a/src/routers/News.ts b/src/routers/News.ts new file mode 100644 index 0000000..872d622 --- /dev/null +++ b/src/routers/News.ts @@ -0,0 +1,21 @@ +import { FastifyPluginAsync } from "fastify"; +import { GetArticleHandler, GetArticlesHandler } from "../controllers/News"; + +export const PublicNewsRouter: FastifyPluginAsync = async server => { + server.route({ + method: 'GET', + url: '', + handler: GetArticlesHandler, + schema: { + querystring: { + page: { type: 'number' } + } + } + }); + + server.route({ + method: 'GET', + url: '/:slug', + handler: GetArticleHandler + }); +} \ No newline at end of file diff --git a/src/routers/Notification.ts b/src/routers/Notification.ts new file mode 100644 index 0000000..bf8aa43 --- /dev/null +++ b/src/routers/Notification.ts @@ -0,0 +1,22 @@ +import { FastifyPluginAsync } from "fastify"; +import { RegisterTokenHandler, PushNotificationHandler } from "../controllers/Notification"; +import { RequireAdmin } from "../middleware/RequireAdmin"; +import { RequireUser } from "../middleware/RequireUser"; +import { PostTokenSchema } from "../types/body-schema"; + +export const NotificationRouter: FastifyPluginAsync = async server => { + server.route({ + method: "POST", + url: '/registration', + preHandler: RequireUser, + handler: RegisterTokenHandler, + schema: PostTokenSchema, + }); + server.route({ + method: "POST", + url: '/new', + preHandler: [RequireUser, RequireAdmin], + handler: PushNotificationHandler, + schema: PostTokenSchema, + }); +} \ No newline at end of file diff --git a/src/routers/User.ts b/src/routers/User.ts index a8940f5..c168b8f 100644 --- a/src/routers/User.ts +++ b/src/routers/User.ts @@ -1,6 +1,6 @@ import { FastifyPluginAsync } from "fastify"; import { GoogleAuthHandler } from "../controllers/UserAuth"; -import { GetProfileHandler, GetTeamsHandler, LogoutHandler, PaymentIntentHandler, PaymentResultHandler, PutUserHandler } from "../controllers/User"; +import { GetProfileHandler, GetTeamsHandler, LogoutHandler, PaymentIntentHandler, PaymentResultHandler } from "../controllers/User"; import { RequireUser } from "../middleware/RequireUser"; export const UserRouter: FastifyPluginAsync = async server => { @@ -13,6 +13,7 @@ export const UserRouter: FastifyPluginAsync = async server => { server.route({ method: "POST", url: '/logout', + preHandler: RequireUser, handler: LogoutHandler }); @@ -26,12 +27,14 @@ export const UserRouter: FastifyPluginAsync = async server => { server.route({ method: "POST", url: '/pay', + preHandler: RequireUser, handler: PaymentIntentHandler, }); server.route({ method: "GET", url: '/payment-result', + preHandler: RequireUser, handler: PaymentResultHandler, schema: { querystring: { @@ -42,13 +45,6 @@ export const UserRouter: FastifyPluginAsync = async server => { } }); - server.route({ - method: "PUT", - url: '/', - preHandler: RequireUser, - handler: PutUserHandler - }); - server.route({ method: "GET", url: '/teams', diff --git a/src/types/body-schema.ts b/src/types/body-schema.ts index cdefed8..f65e47c 100644 --- a/src/types/body-schema.ts +++ b/src/types/body-schema.ts @@ -9,6 +9,27 @@ export const GeneralClubWinnerSchema = { }, }; +export const PostTokenSchema = { + body: { + type: "object", + properties: { + token: { type: "string" }, + }, + }, +} + +export const PostNotificationSchema = { + body: { + type: "object", + properties: { + title: { type: "string" }, + body: { type: "string" }, + link: { type: "string" }, + photo: { type: "string" }, + }, + }, +} + export const AddTeamSchema = { body: { type: "object", diff --git a/src/types/http.d.ts b/src/types/http.d.ts index a26f3a7..71e5005 100644 --- a/src/types/http.d.ts +++ b/src/types/http.d.ts @@ -6,7 +6,7 @@ declare type RequestWithUser = FastifyRequest & { } type AccessTokenRequest = FastifyRequest<{ - Querystring: { code: string }; + Querystring: { code?: string, error?: string }; }>; declare type Reply = FastifyReply & {} diff --git a/src/utils/PointsCalculator.ts b/src/utils/PointsCalculator.ts index a770d0e..9201c74 100644 --- a/src/utils/PointsCalculator.ts +++ b/src/utils/PointsCalculator.ts @@ -222,8 +222,5 @@ export const calculatePoints = (playerStat: Statistic, positionId: number | null // Clean sheet tempPoints += (playerStat.minutesPlayed >= 60 && (playerStat.goalsAgainst || 0 ) === 0) ? PPS.CLEAN_SHEET[positionId] : 0; - if(playerStat.playerId == 47) { - console.log(tempPoints); - } return tempPoints; } \ No newline at end of file