238 lines
7.2 KiB
TypeScript
238 lines
7.2 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("/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>`;
|
|
});
|
|
|
|
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 = `<i class="fa fa-exclamation-triangle" aria-hidden="true"></i>`;
|
|
}
|
|
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 });
|