268 lines
8.6 KiB
TypeScript
268 lines
8.6 KiB
TypeScript
import express from "express";
|
|
import { Server } from "socket.io";
|
|
import bodyParser from "body-parser";
|
|
import cors from 'cors';
|
|
import { addChoice, addPizzaOrder, createPizzaDay, deletePizzaDay, finishPizzaDelivery, finishPizzaOrder, getData, getPizzaList, lockPizzaDay, removeChoice, removeChoices, removePizzaOrder, savePizzaList, unlockPizzaDay, updateDepartureTime, updateNote } from "./service";
|
|
import dotenv from 'dotenv';
|
|
import path from 'path';
|
|
import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku } from "./restaurants";
|
|
import { getQr } from "./qr";
|
|
import { generateToken, getLogin, getTrusted, verify } from "./auth";
|
|
import { Locations, Restaurants } from "../../types";
|
|
import { downloadPizzy } from "./chefie";
|
|
|
|
const ENVIRONMENT = process.env.NODE_ENV || 'production';
|
|
dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) });
|
|
|
|
// Validace nastavení JWT tokenu - nemá bez něj smysl vůbec povolit server spustit
|
|
if (!process.env.JWT_SECRET) {
|
|
throw Error("Není vyplněna proměnná prostředí JWT_SECRET");
|
|
}
|
|
|
|
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: '*'
|
|
}));
|
|
|
|
// app.use((req, res, next) => {
|
|
// console.log("--- Request ---")
|
|
// console.log(req.url);
|
|
// console.log(req.baseUrl);
|
|
// console.log(req.originalUrl);
|
|
// console.log(req.path);
|
|
// next();
|
|
// });
|
|
|
|
app.use(express.static('public'))
|
|
|
|
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, true));
|
|
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, false));
|
|
});
|
|
|
|
// TODO dočasné řešení - QR se zobrazuje přes <img>, 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", async (req, res) => {
|
|
res.status(200).json(await getData());
|
|
});
|
|
|
|
/** Vrátí obědové menu pro dostupné podniky. */
|
|
app.get("/api/food", async (req, res) => {
|
|
const mock = !!process.env.MOCK_DATA;
|
|
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);
|
|
});
|
|
|
|
/** Založí pizza day pro aktuální den, za předpokladu že dosud neexistuje. */
|
|
app.post("/api/createPizzaDay", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await 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", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await deletePizzaDay(login);
|
|
io.emit("message", data);
|
|
});
|
|
|
|
app.post("/api/addPizza", async (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;
|
|
let pizzy = await getPizzaList();
|
|
if (!pizzy) {
|
|
pizzy = await downloadPizzy();
|
|
savePizzaList(pizzy);
|
|
}
|
|
if (!pizzy[pizzaIndex]) {
|
|
throw Error("Neplatný index pizzy: " + pizzaIndex);
|
|
}
|
|
if (!pizzy[pizzaIndex].sizes[pizzaSizeIndex]) {
|
|
throw Error("Neplatný index velikosti pizzy: " + pizzaSizeIndex);
|
|
}
|
|
const data = await addPizzaOrder(login, pizzy[pizzaIndex], pizzy[pizzaIndex].sizes[pizzaSizeIndex]);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/removePizza", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
if (!req.body?.pizzaOrder) {
|
|
throw Error("Nebyla předána objednávka");
|
|
}
|
|
const data = await removePizzaOrder(login, req.body?.pizzaOrder);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/lockPizzaDay", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await lockPizzaDay(login);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/unlockPizzaDay", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await unlockPizzaDay(login);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/finishOrder", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await finishPizzaOrder(login);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/finishDelivery", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await finishPizzaDelivery(login, req.body.bankAccount, req.body.bankAccountHolder);
|
|
io.emit("message", data);
|
|
res.status(200).json({});
|
|
});
|
|
|
|
app.post("/api/addChoice", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const trusted = getTrusted(parseToken(req));
|
|
if (req.body.locationIndex > -1) {
|
|
const data = await addChoice(login, trusted, req.body.locationIndex, req.body.foodIndex);
|
|
io.emit("message", data);
|
|
res.status(200).json(data);
|
|
}
|
|
res.status(400); // TODO přidat popis chyby
|
|
});
|
|
|
|
app.post("/api/removeChoices", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await removeChoices(login, req.body.locationIndex);
|
|
io.emit("message", data);
|
|
res.status(200).json(data);
|
|
});
|
|
|
|
app.post("/api/removeChoice", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await removeChoice(login, req.body.locationIndex, req.body.foodIndex);
|
|
io.emit("message", data);
|
|
res.status(200).json(data);
|
|
});
|
|
|
|
app.post("/api/updateNote", async (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 = await updateNote(login, req.body.note);
|
|
io.emit("message", data);
|
|
res.status(200).json(data);
|
|
});
|
|
|
|
app.post("/api/changeDepartureTime", async (req, res) => {
|
|
const login = getLogin(parseToken(req));
|
|
const data = await updateDepartureTime(login, req.body?.time);
|
|
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}`);
|
|
});
|
|
|
|
// Umožníme vypnutí serveru přes SIGINT, jinak Docker čeká než ho sestřelí
|
|
process.on('SIGINT', function () {
|
|
console.log("\nSIGINT (Ctrl-C), vypínám server");
|
|
process.exit(0);
|
|
}); |