/* * Copyright (C) 2025 Jakub Žitník * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ import express, { Request, Response } from "express"; import path from "path"; const app = express(); import fs from "fs/promises"; import { getCurrentInterval } from "./scheduleRules.js"; import bodyParser from "body-parser"; import cors from "cors"; import next from "next"; import { fileURLToPath } from "url"; const DB_FOLDER = path.join(process.cwd(), "volume", "db"); const WEB_FOLDER = path.join(process.cwd(), "web", "public"); const SERVE_WEB = process.env.SERVE_WEB != "false"; const VERSIONS = ["v1", "v2", "v3"]; const PORT = process.env.PORT || 3000; // @ts-ignore globalThis.File = class File {}; app.use(bodyParser.json()); app.use(cors({ origin: "*", methods: ["GET", "POST", "OPTIONS"], allowedHeaders: ["Content-Type"], })); app.get('/', async (req: Request, res: Response) => { const userAgent = req.headers['user-agent'] || ''; const isBrowser = /Mozilla|Chrome|Firefox|Safari|Edg/.test(userAgent); if (isBrowser && SERVE_WEB) { res.sendFile(path.join(WEB_FOLDER, "index.html")); } else { const dataStr = await fs.readFile(path.join(DB_FOLDER, "v1.json"), "utf8"); const data = JSON.parse(dataStr); data["status"]["currentUpdateSchedule"] = getCurrentInterval(); res.json(data); } }); VERSIONS.forEach((version) => { app.get(`/versioned/${version}`, async (_: Request, res: Response) => { try { const filePath = path.join(DB_FOLDER, `${version}.json`); const dataStr = await fs.readFile(filePath, "utf8"); const data = JSON.parse(dataStr); data.status.currentUpdateSchedule = getCurrentInterval(); res.json(data); } catch (err) { console.error(err); res.status(500).json({ error: "Failed to load version data" }); } }); }); app.get("/status", async (_: Request, res: Response) => { const dataStr = await fs.readFile(path.resolve("./volume/customState.json"), {encoding: "utf8"}); const data = JSON.parse(dataStr); if (data.working) { res.json({ working: true }) } else { res.json({ working: data.working, message: data.message }) } }) app.get("/posts/viewer/redirect", (_: Request, res: Response) => { res.redirect(302, "/viewer"); }) app.post("/report", async (req: Request, res: Response): Promise => { const { class: className, location, content } = req.body; if (!className || !location || !content) { return res.status(400).json({ error: "Missing required fields." }); } if (!["TIMETABLE", "ABSENCES", "ABSENCE", "TAKES_PLACE", "OTHER"].includes(location)) { return res.status(400).json({ error: "Invalid location value." }); } const url = process.env.REPORT_WEBHOOK_URL; if (!url) { console.error("REPORT_WEBHOOK_URL is not set."); return res.status(500).json({ error: "Server configuration error." }); } let resp; try { resp = await fetch(url, { method: "POST", headers: { "Content-Type": "text/plain", }, body: `${content}\n\nClass: ${className}\nLocation: ${location}`, }); } catch (err: any) { console.error('Fetch failed:', err.message); console.error(err); throw err; } if (!resp.ok) { throw new Error(`Request failed`); } res.status(200).json({ message: "Report received successfully." }); }); if (SERVE_WEB) { const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) const dev = process.env.NODE_ENV !== 'production' // @ts-ignore const nextApp = next({ dev, dir: path.join(__dirname, '../viewer') }) const handle = nextApp.getRequestHandler() await nextApp.prepare() app.all(/^\/viewer(?:$|\/.*)/, (req, res) => { return handle(req, res) }) app.use(express.static(path.join(process.cwd(), 'web/public'), { index: 'index.html', extensions: ['html'], })) } app.listen(PORT, () => { console.log(`Server is running at http://localhost:${PORT}`); });