Files
website/server.ts
T
2024-10-17 18:39:40 -04:00

237 lines
7.1 KiB
TypeScript

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("/didier", async (ctx) => {
const template = await env.load("templates/didier.vto");
const result = await template({ tab: "home" });
ctx.response.body = result.content;
});
router.get("/didier/un", async (ctx) => {
const template = await env.load("templates/didier-un.vto");
const result = await template({ tab: "home" });
ctx.response.body = result.content;
});
router.get("/didier/deux", async (ctx) => {
const template = await env.load("templates/didier-deux.vto");
const result = await template({ tab: "home" });
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<Game>;
const success = await gameService.updateGame(ctx.params.gameId, preGame);
ctx.response.body = success
? `<i class="fa fa-floppy-o" aria-hidden="true"></i>`
: `<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>`;
});
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 });