import { Application } from "jsr:@oak/oak/application"; import { Router } from "jsr:@oak/oak/router"; import { GameService } from "./server/game-service.ts"; import { Game } from "./server/types.ts"; import staticFiles from "https://deno.land/x/static_files@1.1.6/mod.ts"; import vento from "https://deno.land/x/vento@v0.9.1/mod.ts"; import { LoginService } from "./server/login-service.ts"; const env = vento(); setInterval(() => env.cache.clear(), 1000); const gameService = new GameService(await Deno.openKv()); const loginService = LoginService.officialLoginService(); const router = new Router(); router.get("/", async (ctx) => { const template = await env.load("templates/home.vto"); const result = await template({ tab: "home" }); ctx.response.body = result.content; }); router.get("/calendrier", async (ctx) => { const template = await env.load("templates/calendrier.vto"); const result = await template({ tab: "calendrier", }); ctx.response.body = result.content; }); router.get("/nouvelles", async (ctx) => { const template = await env.load("templates/nouvelles.vto"); const result = await template({ tab: "nouvelles" }); ctx.response.body = result.content; }); router.get("/contact", async (ctx) => { const template = await env.load("templates/contact.vto"); const result = await template({ tab: "contact" }); ctx.response.body = result.content; }); router.get("/reglement", async (ctx) => { const template = await env.load("templates/reglement.vto"); const result = await template({ tab: "reglement" }); ctx.response.body = result.content; }); router.get("/login", async (ctx) => { const username = ctx.request.url.searchParams.get("username") || ""; const password = ctx.request.url.searchParams.get("password") || ""; if (!(await loginService.validateCredentials(username, password))) { ctx.response.status = 403; return; } await loginService.login(ctx); ctx.response.redirect("/membre/games-editor/today"); }); const memberRouter = new Router(); memberRouter.use(async (ctx, next) => { const isLoggedIn = await loginService.isLoggedIn(ctx); if (ctx.request.url.pathname !== "/membre" && !isLoggedIn) { ctx.response.redirect("/membre"); return; } ctx.params.isLoggedIn = isLoggedIn; await next(); }); memberRouter.get("/", async (ctx) => { const template = await env.load("templates/membre.vto"); const result = await template({ tab: "membre", isLoggedIn: ctx.params.isLoggedIn, }); ctx.response.body = result.content; }); function today(): Date { const date = new Date().toLocaleDateString("fr-CA", { timeZone: "America/New_York", }); return new Date(date); } memberRouter.get("/games-editor/today", async (ctx) => { const template = await env.load("templates/partials/games-editor.vto"); const games = await gameService.getAllGames(); const lastGameDay = games .map((g) => g.date) .filter((d) => new Date(d) <= today()) .sort() .reverse()[0]; const result = await template({ tab: "membre", selectedYear: null, isToday: true, games: games.filter((g) => g.date === lastGameDay), years: [...new Set(games.map((x) => x.year))].sort().reverse(), }); ctx.response.body = result.content; }); memberRouter.get("/games-editor/:year", async (ctx) => { const template = await env.load("templates/partials/games-editor.vto"); const games = await gameService.getAllGames(); const selectedYear = parseInt(ctx.params.year); const result = await template({ tab: "membre", selectedYear: selectedYear, games: games.filter((g) => g.year === selectedYear), years: [...new Set(games.map((x) => x.year))].sort().reverse(), }); ctx.response.body = result.content; }); memberRouter.get("/new-game-editor", async (ctx) => { const template = await env.load("templates/partials/new-games-editor.vto"); const result = await template({ tab: "membre", }); ctx.response.body = result.content; }); memberRouter.post("/new-game", async (ctx) => { const payload = await ctx.request.body.form(); const preGame = { date: payload.get("game-date"), time: payload.get("game-time"), away: payload.get("away-team"), home: payload.get("home-team"), }; const [game, error] = await gameService.createNewGame(preGame); if (error !== "") { const template = await env.load("templates/partials/new-game-error.vto"); const result = await template({ tab: "membre", error: error, }); ctx.response.status = 500; ctx.response.body = result.content; return; } const template = await env.load("templates/partials/single-game-editor.vto"); const result = await template({ tab: "membre", game: game, }); ctx.response.headers.set("game-id", game?.id ?? ""); ctx.response.body = result.content; }); memberRouter.delete("/games/:gameId", async (ctx) => { if (!(await loginService.isLoggedIn(ctx))) { ctx.response.redirect("/membre"); return; } const success = await gameService.deleteGameById(ctx.params.gameId); ctx.response.body = `${success}`; }); memberRouter.post("/games/:gameId", async (ctx) => { if (!(await loginService.isLoggedIn(ctx))) { ctx.response.redirect("/membre"); return; } const payload = await ctx.request.body.form(); let preGame = { awayScore: payload.get("away-score") ?? null, homeScore: payload.get("home-score") ?? null, rain: payload.get("game-type") === "rain", practice: payload.get("game-type") === "practice", }; preGame = Object.fromEntries( Object.entries(preGame).filter(([_, v]) => v !== null), ) as Partial; const success = await gameService.updateGame(ctx.params.gameId, preGame); ctx.response.body = success ? `` : ``; }); memberRouter.post("/games/:gameId/switch", async (ctx) => { if (!(await loginService.isLoggedIn(ctx))) { ctx.response.redirect("/membre"); return; } const success = await gameService.switchVisitors(ctx.params.gameId); if (!success) { ctx.response.body = ``; } const template = await env.load("templates/partials/single-game-editor.vto"); const game = await gameService.getGameById(ctx.params.gameId); const result = await template({ tab: "membre", game: game, }); ctx.response.headers.set("game-id", game?.id ?? ""); ctx.response.body = result.content; }); const apiRouter = new Router(); apiRouter.get("/results/:year.json", async (ctx) => { ctx.response.json = true; const games = await gameService.getAllGames(); ctx.response.body = games .filter((g) => g.year == ctx.params.year) .map((g) => { g[g.home] = parseInt(g.homeScore); g[g.away] = parseInt(g.awayScore); return g; }); }); apiRouter.get("/available-years.json", async (ctx) => { ctx.response.json = true; const games = await gameService.getAllGames(); ctx.response.body = [...new Set(games.map((g) => g.year))]; }); router.use("/membre", memberRouter.routes()); router.use("/api", apiRouter.routes()); const app = new Application(); app.use(router.routes()); app.use(staticFiles("static", { brotli: true, fetch: true })); await app.listen({ port: 8000 });