import express from "express"; import { Server } from "socket.io"; import bodyParser from "body-parser"; import { fetchPizzy } from "./chefie"; import cors from 'cors'; import { addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, lockPizzaDay, removePizzaOrder, unlockPizzaDay, updateChoice, updateNote } from "./service"; import dotenv from 'dotenv'; import path from 'path'; import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants"; import { getQr } from "./qr"; import { Restaurants } from "./types"; import { generateToken, getLogin, verify } from "./auth"; const ENVIRONMENT = process.env.NODE_ENV || 'production' dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) }); const app = express(); const server = require("http").createServer(app); const io = new Server(server, { cors: { origin: "*", }, }); // Body-parser middleware for parsing JSON app.use(bodyParser.json()); // app.use(express.json()); app.use(cors({ origin: '*' })); const parseToken = (req: any) => { if (req?.headers?.authorization) { return req.headers.authorization.split(' ')[1]; } } // ----------- Metody nevyžadující token -------------- app.get("/api/whoami", (req, res) => { res.send(req.header('remote-user')); }) app.post("/api/login", (req, res) => { // Autentizace pomocí trusted headers const remoteUser = req.header('remote-user'); if (remoteUser && remoteUser.length > 0) { res.status(200).json(generateToken(remoteUser)); return; } // Klasická autentizace loginem if (!req.body?.login || req.body.login.trim().length === 0) { throw Error("Nebyl předán login"); } // TODO zavést podmínky pro délku loginu (min i max) res.status(200).json(generateToken(req.body.login)); }); // TODO dočasné řešení - QR se zobrazuje přes , nemáme sem jak dostat token app.get("/api/qr", (req, res) => { // const login = getLogin(parseToken(req)); if (!req.query?.login) { throw Error("Nebyl předán login"); } const img = getQr(req.query.login as string); res.writeHead(200, { 'Content-Type': 'image/png', 'Content-Length': img.length }); res.end(img); }); // ---------------------------------------------------- /** Middleware ověřující JWT token */ app.use((req, res, next) => { if (req.header('remote-user')) { console.log("Tvuj username: %s.", req.header('remote-user')); } if (!req.headers.authorization) { return res.status(401).json({ error: 'Nebyl předán autentizační token' }); } const token = req.headers.authorization.split(' ')[1]; if (!verify(token)) { return res.status(403).json({ error: 'Neplatný autentizační token' }); } next(); }); /** Vrátí data pro aktuální den. */ app.get("/api/data", (req, res) => { res.status(200).json(getData()); }); /** Vrátí obědové menu pro dostupné podniky. */ app.get("/api/food", async (req, res) => { const mock = !!req.query?.mock; const date = new Date(); const data = { [Restaurants.SLADOVNICKA]: await getMenuSladovnicka(date, mock), [Restaurants.UMOTLIKU]: await getMenuUMotliku(date, mock), [Restaurants.TECHTOWER]: await getMenuTechTower(date, mock), } res.status(200).json(data); }); /** Vrátí seznam dostupných pizz. */ app.get("/api/pizza", (req, res) => { fetchPizzy().then(pizzaList => { // console.log("Výsledek", pizzaList); res.status(200).json(pizzaList); }); }); /** Založí pizza day pro aktuální den, za předpokladu že dosud neexistuje. */ app.post("/api/createPizzaDay", (req, res) => { const login = getLogin(parseToken(req)); const data = createPizzaDay(login); res.status(200).json(data); io.emit("message", data); }); /** Smaže pizza day pro aktuální den, za předpokladu že existuje. */ app.post("/api/deletePizzaDay", (req, res) => { const login = getLogin(parseToken(req)); const data = deletePizzaDay(login); io.emit("message", data); }); app.post("/api/addPizza", (req, res) => { const login = getLogin(parseToken(req)); if (isNaN(req.body?.pizzaIndex)) { throw Error("Nebyl předán index pizzy"); } const pizzaIndex = req.body.pizzaIndex; if (isNaN(req.body?.pizzaSizeIndex)) { throw Error("Nebyl předán index velikosti pizzy"); } const pizzaSizeIndex = req.body.pizzaSizeIndex; fetchPizzy().then(pizzy => { if (!pizzy[pizzaIndex]) { throw Error("Neplatný index pizzy: " + pizzaIndex); } if (!pizzy[pizzaIndex].sizes[pizzaSizeIndex]) { throw Error("Neplatný index velikosti pizzy: " + pizzaSizeIndex); } const data = addPizzaOrder(login, pizzy[pizzaIndex], pizzy[pizzaIndex].sizes[pizzaSizeIndex]); io.emit("message", data); res.status(200).json({}); }) }); app.post("/api/removePizza", (req, res) => { const login = getLogin(parseToken(req)); if (!req.body?.pizzaOrder) { throw Error("Nebyla předána objednávka"); } const data = removePizzaOrder(login, req.body?.pizzaOrder); io.emit("message", data); res.status(200).json({}); }); app.post("/api/lockPizzaDay", (req, res) => { const login = getLogin(parseToken(req)); const data = lockPizzaDay(login); io.emit("message", data); res.status(200).json({}); }); app.post("/api/unlockPizzaDay", (req, res) => { const login = getLogin(parseToken(req)); const data = unlockPizzaDay(login); io.emit("message", data); res.status(200).json({}); }); app.post("/api/finishOrder", (req, res) => { const login = getLogin(parseToken(req)); const data = finishPizzaOrder(login); io.emit("message", data); res.status(200).json({}); }); app.post("/api/finishDelivery", (req, res) => { const login = getLogin(parseToken(req)); const data = finishPizzaDelivery(login, req.body.bankAccount, req.body.bankAccountHolder); io.emit("message", data); res.status(200).json({}); }); app.post("/api/updateChoice", (req, res) => { const login = getLogin(parseToken(req)); const data = updateChoice(login, req.body.choice); io.emit("message", data); res.status(200).json(data); }); app.post("/api/updateNote", (req, res) => { const login = getLogin(parseToken(req)); if (req.body.note && req.body.note.length > 100) { throw Error("Poznámka může mít maximálně 100 znaků"); } const data = updateNote(login, req.body.note); io.emit("message", data); res.status(200).json(data); }); io.on("connection", (socket) => { console.log(`New client connected: ${socket.id}`); socket.on("message", (message) => { io.emit("message", message); }); socket.on("disconnect", () => { console.log(`Client disconnected: ${socket.id}`); }); }); const PORT = process.env.PORT || 3001; const HOST = process.env.HOST || '0.0.0.0'; server.listen(PORT, () => { console.log(`Server listening on ${HOST}, port ${PORT}`); }); console.log(process.env.API_KEY)