Refaktor: Nálezy SonarQube
Some checks are pending
ci/woodpecker/push/workflow Pipeline is running

This commit is contained in:
Martin Berka 2025-03-05 21:48:02 +01:00
parent 55fd368663
commit e55ee7c11e
31 changed files with 103 additions and 141 deletions

View File

@ -24,7 +24,7 @@ import { useEasterEgg } from './context/eggs';
import { getImage } from './api/EasterEggApi';
import { Link } from 'react-router';
import { STATS_URL } from './AppRoutes';
import { ClientData, Food, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, LunchChoices, UserLunchChoice, PizzaVariant } from '../../types';
import { ClientData, Food, PizzaOrder, DepartureTime, PizzaDayState, Restaurant, RestaurantDayMenu, RestaurantDayMenuMap, LunchChoice, UserLunchChoice, PizzaVariant } from '../../types';
const EVENT_CONNECT = "connect"
@ -41,7 +41,7 @@ const EASTER_EGG_DEFAULT_DURATION = 0.75;
function App() {
const auth = useAuth();
const settings = useSettings();
const [easterEgg, easterEggLoading] = useEasterEgg(auth);
const [easterEgg, _] = useEasterEgg(auth);
const [isConnected, setIsConnected] = useState<boolean>(false);
const [data, setData] = useState<ClientData>();
const [food, setFood] = useState<RestaurantDayMenuMap>();
@ -65,7 +65,7 @@ function App() {
// Načtení dat po přihlášení
useEffect(() => {
if (!auth || !auth.login) {
if (!auth?.login) {
return
}
getData().then(({ data }) => {
@ -82,7 +82,7 @@ function App() {
// Přenačtení pro zvolený den
useEffect(() => {
if (!auth || !auth.login) {
if (!auth?.login) {
return
}
getData(dayIndex).then((data: ClientData) => {
@ -96,11 +96,9 @@ function App() {
// Registrace socket eventů
useEffect(() => {
socket.on(EVENT_CONNECT, () => {
// console.log("Connected!");
setIsConnected(true);
});
socket.on(EVENT_DISCONNECT, () => {
// console.log("Disconnected!");
setIsConnected(false);
});
socket.on(EVENT_MESSAGE, (newData: ClientData) => {
@ -119,7 +117,7 @@ function App() {
}, [socket]);
useEffect(() => {
if (!auth || !auth.login) {
if (!auth?.login) {
return
}
// TODO tohle občas náhodně nezafunguje, nutno přepsat, viz https://medium.com/@teh_builder/ref-objects-inside-useeffect-hooks-eb7c15198780
@ -277,7 +275,7 @@ function App() {
const handlePizzaChange = async (value: SelectedOptionValue | SelectedOptionValue[]) => {
if (auth?.login && data?.pizzaList) {
if (!(typeof value === 'string')) {
if (typeof value !== 'string') {
throw Error('Nepodporovaný typ hodnoty');
}
const s = value.split('|');
@ -365,13 +363,13 @@ function App() {
content = <h3>Chyba načtení dat</h3>
}
return <Col md={12} lg={3} className='mt-3'>
<h3 style={{ cursor: 'pointer' }} onClick={() => doAddClickFoodChoice(location, undefined)}>{location}</h3>
<h3 style={{ cursor: 'pointer' }} onClick={() => doAddClickFoodChoice(location)}>{location}</h3>
{menu?.lastUpdate && <small>Poslední aktualizace: {getHumanDateTime(new Date(menu.lastUpdate))}</small>}
{content}
</Col>
}
if (!auth || !auth.login) {
if (!auth?.login) {
return <Login />;
}
@ -411,8 +409,8 @@ function App() {
<div className='wrapper'>
{data.isWeekend ? <h4>Užívejte víkend :)</h4> : <>
<Alert variant={'primary'}>
<img src='hat.png' style={{ position: "absolute", width: "70px", rotate: "-45deg", left: -40, top: -58 }} />
<img src='snowman.png' style={{ position: "absolute", height: "110px", right: 10, top: 5 }} />
<img alt="" src='hat.png' style={{ position: "absolute", width: "70px", rotate: "-45deg", left: -40, top: -58 }} />
<img alt="" src='snowman.png' style={{ position: "absolute", height: "110px", right: 10, top: 5 }} />
Poslední změny:
<ul>
<li>Možnost výběru restaurace a jídel kliknutím v tabulce</li>
@ -429,7 +427,6 @@ function App() {
<Row className='food-tables'>
{/* TODO zjednodušit, stačí iterovat klíče typu Restaurant */}
{food['SLADOVNICKA'] && renderFoodTable('SLADOVNICKA', food['SLADOVNICKA'])}
{/* {food['UMOTLIKU'] && renderFoodTable('UMOTLIKU', food['UMOTLIKU'])} */}
{food['TECHTOWER'] && renderFoodTable('TECHTOWER', food['TECHTOWER'])}
{food['ZASTAVKAUMICHALA'] && renderFoodTable('ZASTAVKAUMICHALA', food['ZASTAVKAUMICHALA'])}
{food['SENKSERIKOVA'] && renderFoodTable('SENKSERIKOVA', food['SENKSERIKOVA'])}
@ -452,7 +449,7 @@ function App() {
<p style={{ marginTop: "10px" }}>Na co dobrého? <small>(nepovinné)</small></p>
<Form.Select ref={foodChoiceRef} onChange={doAddFoodChoice}>
<option></option>
{foodChoiceList.map((food, index) => <option key={index} value={index}>{food.name}</option>)}
{foodChoiceList.map((food, index) => <option key={food.name} value={index}>{food.name}</option>)}
</Form.Select>
</>}
{foodChoiceList && !closed && <>
@ -491,7 +488,7 @@ function App() {
const userPayload = entry[1];
const userChoices = userPayload?.selectedFoods;
const trusted = userPayload?.trusted || false;
return <tr key={index}>
return <tr key={entry[0]}>
<td>
{trusted && <span className='trusted-icon'>
<FontAwesomeIcon title='Uživatel ověřený doménovým přihlášením' icon={faCircleCheck} style={{ cursor: "help" }} />
@ -587,9 +584,6 @@ function App() {
<Button className='danger mb-3' title="Umožní znovu editovat objednávky." onClick={async () => {
await unlockPizzaDay();
}}>Odemknout</Button>
{/* <Button className='danger mb-3' style={{ marginLeft: '20px' }} onClick={async () => {
await addToCart();
}}>Přidat vše do košíku</Button> */}
<Button className='danger mb-3' style={{ marginLeft: '20px' }} title={noOrders ? "Nelze objednat - neexistuje žádná objednávka" : "Použij po objednání. Objednávky zůstanou zamčeny."} disabled={noOrders} onClick={async () => {
await finishOrder();
}}>Objednáno</Button>

View File

@ -25,9 +25,8 @@ export default function Login() {
}, [auth]);
const doLogin = useCallback(async () => {
const length = loginRef?.current?.value && loginRef?.current?.value.length && loginRef.current.value.replace(/\s/g, '').length
const length = loginRef?.current?.value.length && loginRef.current.value.replace(/\s/g, '').length
if (length) {
// TODO odchytávat cokoliv mimo 200
const token = await login(loginRef.current?.value);
if (token) {
auth?.setToken(token);
@ -35,7 +34,7 @@ export default function Login() {
}
}, [auth]);
if (!auth || !auth.login) {
if (!auth?.login) {
return <div className='login'>
<h1>Luncher</h1>
<h4 style={{ marginBottom: "50px" }}>Aplikace pro profesionální management obědů</h4>

View File

@ -93,7 +93,7 @@ export function formatDate(date: Date, format?: string) {
let month = String(date.getMonth() + 1).padStart(2, "0");
let year = String(date.getFullYear());
const f = (format === undefined) ? 'YYYY-MM-DD' : format;
const f = format ?? 'YYYY-MM-DD';
return f.replace('DD', day).replace('MM', month).replace('YYYY', year);
}

View File

@ -1,5 +1,4 @@
import { AddChoiceRequest, ChangeDepartureTimeRequest, RemoveChoiceRequest, RemoveChoicesRequest, UpdateNoteRequest } from "../../../types";
import { LunchChoice } from "../../../types";
import { AddChoiceRequest, ChangeDepartureTimeRequest, LunchChoice, RemoveChoiceRequest, RemoveChoicesRequest, UpdateNoteRequest } from "../../../types";
import { api } from "./Api";
const FOOD_API_PREFIX = '/api/food';

View File

@ -1,5 +1,4 @@
import { AddPizzaRequest, FinishDeliveryRequest, RemovePizzaRequest, UpdatePizzaDayNoteRequest, UpdatePizzaFeeRequest } from "../../../types";
import { PizzaVariant } from "../../../types";
import { AddPizzaRequest, FinishDeliveryRequest, PizzaVariant, RemovePizzaRequest, UpdatePizzaDayNoteRequest, UpdatePizzaFeeRequest } from "../../../types";
import { api } from "./Api";
const PIZZADAY_API_PREFIX = '/api/pizzaDay';

View File

@ -1,5 +1,4 @@
import { UpdateFeatureVoteRequest } from "../../../types";
import { FeatureRequest } from "../../../types";
import { FeatureRequest, UpdateFeatureVoteRequest } from "../../../types";
import { api } from "./Api";
const VOTING_API_PREFIX = '/api/voting';

View File

@ -78,7 +78,7 @@ export default function Header() {
cislo = cislo.padStart(16, '0');
}
let sum = 0;
for (var i = 0; i < cislo.length; i++) {
for (let i = 0; i < cislo.length; i++) {
const char = cislo.charAt(i);
const order = (cislo.length - 1) - i;
const weight = (2 ** order) % 11;

View File

@ -2,15 +2,15 @@ import { IconDefinition } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
type Props = {
title?: String,
title?: string,
icon: IconDefinition,
description: String,
animation?: String,
description: string,
animation?: string,
}
function Loader(props: Props) {
function Loader(props: Readonly<Props>) {
return <div className='loader'>
<h1>{props.title || 'Prosím čekejte...'}</h1>
<h1>{props.title ?? 'Prosím čekejte...'}</h1>
<FontAwesomeIcon icon={props.icon} className={`loader-icon mb-3 ` + (props.animation ?? '')} />
<p>{props.description}</p>
</div>

View File

@ -10,7 +10,7 @@ type Props = {
creator: string,
}
export default function PizzaOrderList({ state, orders, onDelete, creator }: Props) {
export default function PizzaOrderList({ state, orders, onDelete, creator }: Readonly<Props>) {
const saveFees = async (customer: string, text?: string, price?: number) => {
await updatePizzaFee(customer, text, price);
}
@ -21,8 +21,7 @@ export default function PizzaOrderList({ state, orders, onDelete, creator }: Pro
const total = orders.reduce((total, order) => total + order.totalPrice, 0);
return <>
<Table className="mt-3" striped bordered hover>
return <Table className="mt-3" striped bordered hover>
<thead>
<tr>
<th>Jméno</th>
@ -42,5 +41,4 @@ export default function PizzaOrderList({ state, orders, onDelete, creator }: Pro
</tr>
</tbody>
</Table>
</>
}

View File

@ -13,19 +13,19 @@ type Props = {
onFeeModalSave: (customer: string, name?: string, price?: number) => void,
}
export default function PizzaOrderRow({ creator, order, state, onDelete, onFeeModalSave }: Props) {
export default function PizzaOrderRow({ creator, order, state, onDelete, onFeeModalSave }: Readonly<Props>) {
const auth = useAuth();
const [isFeeModalOpen, setFeeModalOpen] = useState<boolean>(false);
const [isFeeModalOpen, setIsFeeModalOpen] = useState<boolean>(false);
const saveFees = (customer: string, text?: string, price?: number) => {
onFeeModalSave(customer, text, price);
setFeeModalOpen(false);
setIsFeeModalOpen(false);
}
return <>
<td>{order.customer}</td>
<td>{order.pizzaList!.map<React.ReactNode>((pizzaOrder, index) =>
<span key={index}>
<td>{order.pizzaList!.map<React.ReactNode>(pizzaOrder =>
<span key={pizzaOrder.name}>
{`${pizzaOrder.name}, ${pizzaOrder.size} (${pizzaOrder.price} Kč)`}
{auth?.login === order.customer && state === PizzaDayState.CREATED &&
<FontAwesomeIcon onClick={() => {
@ -35,11 +35,11 @@ export default function PizzaOrderRow({ creator, order, state, onDelete, onFeeMo
</span>)
.reduce((prev, curr, index) => [prev, <br key={`br-${index}`} />, curr])}
</td>
<td style={{ maxWidth: "200px" }}>{order.note || '-'}</td>
<td style={{ maxWidth: "200px" }}>{order.note ?? '-'}</td>
<td style={{ maxWidth: "200px" }}>{order.fee?.price ? `${order.fee.price}${order.fee.text ? ` (${order.fee.text})` : ''}` : '-'}</td>
<td>
{order.totalPrice} {auth?.login === creator && state === PizzaDayState.CREATED && <FontAwesomeIcon onClick={() => { setFeeModalOpen(true) }} title='Nastavit příplatek' className='action-icon' icon={faMoneyBill1} />}
{order.totalPrice} {auth?.login === creator && state === PizzaDayState.CREATED && <FontAwesomeIcon onClick={() => { setIsFeeModalOpen(true) }} title='Nastavit příplatek' className='action-icon' icon={faMoneyBill1} />}
</td>
<PizzaAdditionalFeeModal customerName={order.customer} isOpen={isFeeModalOpen} onClose={() => setFeeModalOpen(false)} onSave={saveFees} initialValues={{ text: order.fee?.text, price: order.fee?.price?.toString() }} />
<PizzaAdditionalFeeModal customerName={order.customer} isOpen={isFeeModalOpen} onClose={() => setIsFeeModalOpen(false)} onSave={saveFees} initialValues={{ text: order.fee?.text, price: order.fee?.price?.toString() }} />
</>
}

View File

@ -9,7 +9,7 @@ type Props = {
}
/** Modální dialog pro hlasování o nových funkcích. */
export default function FeaturesVotingModal({ isOpen, onClose, onChange, initialValues }: Props) {
export default function FeaturesVotingModal({ isOpen, onClose, onChange, initialValues }: Readonly<Props>) {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.currentTarget.value as FeatureRequest, e.currentTarget.checked);
@ -31,7 +31,7 @@ export default function FeaturesVotingModal({ isOpen, onClose, onChange, initial
label={FeatureRequest[key]}
onChange={handleChange}
value={key}
defaultChecked={initialValues && initialValues.includes(key as FeatureRequest)}
defaultChecked={initialValues?.includes(key as FeatureRequest)}
/>
})}
<p className="mt-3" style={{ fontSize: '12px' }}>Něco jiného? Dejte vědět.</p>

View File

@ -8,7 +8,7 @@ type Props = {
}
/** Modální dialog pro úpravu obecné poznámky. */
export default function NoteModal({ isOpen, onClose, onSave }: Props) {
export default function NoteModal({ isOpen, onClose, onSave }: Readonly<Props>) {
const note = useRef<HTMLInputElement>(null);
const save = () => {

View File

@ -10,17 +10,17 @@ type Props = {
}
/** Modální dialog pro nastavení příplatků za pizzu. */
export default function PizzaAdditionalFeeModal({ customerName, isOpen, onClose, onSave, initialValues }: Props) {
export default function PizzaAdditionalFeeModal({ customerName, isOpen, onClose, onSave, initialValues }: Readonly<Props>) {
const textRef = useRef<HTMLInputElement>(null);
const priceRef = useRef<HTMLInputElement>(null);
const doSubmit = () => {
onSave(customerName, textRef.current?.value, parseInt(priceRef.current?.value || "0"));
onSave(customerName, textRef.current?.value, parseInt(priceRef.current?.value ?? "0"));
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
onSave(customerName, textRef.current?.value, parseInt(priceRef.current?.value || "0"));
onSave(customerName, textRef.current?.value, parseInt(priceRef.current?.value ?? "0"));
}
}

View File

@ -23,7 +23,7 @@ type Result = {
}
/** Modální dialog pro výpočet výhodnosti pizzy. */
export default function PizzaCalculatorModal({ isOpen, onClose }: Props) {
export default function PizzaCalculatorModal({ isOpen, onClose }: Readonly<Props>) {
const diameter1Ref = useRef<HTMLInputElement>(null);
const price1Ref = useRef<HTMLInputElement>(null);
const diameter2Ref = useRef<HTMLInputElement>(null);

View File

@ -9,7 +9,7 @@ type Props = {
}
/** Modální dialog pro uživatelská nastavení. */
export default function SettingsModal({ isOpen, onClose, onSave }: Props) {
export default function SettingsModal({ isOpen, onClose, onSave }: Readonly<Props>) {
const settings = useSettings();
const bankAccountRef = useRef<HTMLInputElement>(null);
const nameRef = useRef<HTMLInputElement>(null);

View File

@ -1,5 +1,4 @@
import React, { ReactNode, useContext, useState } from "react"
import { useEffect } from "react"
import React, { ReactNode, useContext, useEffect, useState } from "react"
import { useJwt } from "react-jwt";
import { deleteToken, getToken, storeToken } from "../Utils";
@ -16,7 +15,7 @@ type ContextProps = {
const authContext = React.createContext<AuthContextProps | null>(null);
export function ProvideAuth(props: ContextProps) {
export function ProvideAuth(props: Readonly<ContextProps>) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{props.children}</authContext.Provider>
}
@ -29,7 +28,7 @@ function useProvideAuth(): AuthContextProps {
const [loginName, setLoginName] = useState<string | undefined>();
const [trusted, setTrusted] = useState<boolean | undefined>();
const [token, setToken] = useState<string | undefined>(getToken());
const { decodedToken } = useJwt(token || '');
const { decodedToken } = useJwt(token ?? '');
useEffect(() => {
if (token && token.length > 0) {

View File

@ -1,5 +1,4 @@
import React, { ReactNode, useContext, useState } from "react"
import { useEffect } from "react"
import React, { ReactNode, useContext, useEffect, useState } from "react"
const BANK_ACCOUNT_NUMBER_KEY = 'bank_account_number';
const BANK_ACCOUNT_HOLDER_KEY = 'bank_account_holder_name';
@ -20,7 +19,7 @@ type ContextProps = {
const settingsContext = React.createContext<SettingsContextProps | null>(null);
export function ProvideSettings(props: ContextProps) {
export function ProvideSettings(props: Readonly<ContextProps>) {
const settings = useProvideSettings();
return <settingsContext.Provider value={settings}>{props.children}</settingsContext.Provider>
}
@ -45,7 +44,7 @@ function useProvideSettings(): SettingsContextProps {
}
const hideSoups = localStorage.getItem(HIDE_SOUPS_KEY);
if (hideSoups !== null) {
setHideSoups(hideSoups === 'true' ? true : false);
setHideSoups(hideSoups === 'true');
}
}, [])

View File

@ -18,8 +18,3 @@ export const SocketContext = React.createContext();
export const EVENT_CONNECT = 'connect';
export const EVENT_DISCONNECT = 'disconnect';
export const EVENT_MESSAGE = 'message';
// export const EVENT_CONFIG = 'config';
// export const EVENT_TOASTER = 'toaster';
// export const EVENT_VOTING = 'voting';
// export const EVENT_VOTE_CONFIG = 'voteSettings';
// export const EVENT_ADMIN = 'admin';

View File

@ -49,16 +49,16 @@ export async function downloadPizzy(mock: boolean): Promise<Pizza[]> {
const $ = load(html);
const links = $('.vypisproduktu > div > h4 > a');
const urls = [];
for (let i = 0; i < links.length; i++) {
if (links[i].name === 'a' && links[i].attribs?.href) {
const pizzaUrl = links[i].attribs?.href;
for (const element of links) {
if (element.name === 'a' && element.attribs?.href) {
const pizzaUrl = element.attribs?.href;
urls.push(buildPizzaUrl(pizzaUrl));
}
}
// Scrapneme jednotlivé pizzy
const result: Pizza[] = [];
for (let i = 0; i < urls.length; i++) {
const pizzaUrl = urls[i];
for (const element of urls) {
const pizzaUrl = element;
const pizzaHtml = await axios.get(pizzaUrl).then(res => res.data);
// Název
const name = $('.produkt > h2', pizzaHtml).first().text()

View File

@ -14,7 +14,7 @@ import votingRoutes from "./routes/votingRoutes";
import easterEggRoutes from "./routes/easterEggRoutes";
import statsRoutes from "./routes/statsRoutes";
const ENVIRONMENT = process.env.NODE_ENV || 'production';
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
@ -35,7 +35,7 @@ app.use(cors({
// Zapínatelný login přes hlavičky - pokud je zapnutý nepovolí "basicauth"
const HTTP_REMOTE_USER_ENABLED = process.env.HTTP_REMOTE_USER_ENABLED === 'true' || false;
const HTTP_REMOTE_USER_HEADER_NAME = process.env.HTTP_REMOTE_USER_HEADER_NAME || 'remote-user';
const HTTP_REMOTE_USER_HEADER_NAME = process.env.HTTP_REMOTE_USER_HEADER_NAME ?? 'remote-user';
if (HTTP_REMOTE_USER_ENABLED) {
if (!process.env.HTTP_REMOTE_TRUSTED_IPS) {
throw new Error('Je zapnutý login z hlaviček, ale není nastaven rozsah adres ze kterých hlavička může přijít.');
@ -79,7 +79,6 @@ app.post("/api/login", (req, res) => {
// 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");
}
@ -148,8 +147,8 @@ app.use((err: any, req: any, res: any, next: any) => {
next();
});
const PORT = process.env.PORT || 3001;
const HOST = process.env.HOST || '0.0.0.0';
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}`);

View File

@ -1,14 +1,11 @@
/** Notifikace */
import axios from 'axios';
import dotenv from 'dotenv';
import path from 'path';
import { getClientData, getToday } from "./service";
import { getUsersByLocation, getHumanTime } from "./utils";
import getStorage from "./storage";
import { NotifikaceData, NotifikaceInput } from '../../types';
const storage = getStorage();
const ENVIRONMENT = process.env.NODE_ENV || 'production'
const ENVIRONMENT = process.env.NODE_ENV ?? 'production';
dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) });
// const gotifyDataRaw = process.env.GOTIFY_SERVERS_AND_KEYS || "{}";

View File

@ -128,11 +128,11 @@ export async function removePizzaOrder(login: string, pizzaOrder: PizzaVariant)
if (!clientData.pizzaDay) {
throw Error("Pizza day pro dnešní den neexistuje");
}
const orderIndex = clientData.pizzaDay!.orders!.findIndex(o => o.customer === login);
const orderIndex = clientData.pizzaDay.orders!.findIndex(o => o.customer === login);
if (orderIndex < 0) {
throw Error("Nebyly nalezeny žádné objednávky pro uživatele " + login);
}
const order = clientData.pizzaDay!.orders![orderIndex];
const order = clientData.pizzaDay.orders![orderIndex];
const index = order.pizzaList!.findIndex(o => o.name === pizzaOrder.name && o.size === pizzaOrder.size);
if (index < 0) {
throw Error("Objednávka s danými parametry nebyla nalezena");
@ -308,7 +308,7 @@ export async function updatePizzaFee(login: string, targetLogin: string, text?:
targetOrder.fee = { text, price };
}
// Přepočet ceny
targetOrder.totalPrice = targetOrder.pizzaList.reduce((price, pizzaOrder) => price + pizzaOrder.price, 0) + (targetOrder.fee?.price || 0);
targetOrder.totalPrice = targetOrder.pizzaList.reduce((price, pizzaOrder) => price + pizzaOrder.price, 0) + (targetOrder.fee?.price ?? 0);
await storage.setData(today, clientData);
return clientData;
}

View File

@ -92,7 +92,6 @@ export const getMenuSladovnicka = async (firstDayOfWeek: Date, mock: boolean = f
const rowText = $(dayRow).first().text().trim();
if (rowText === searchedDayText) {
index = i;
return;
}
})
if (index === undefined) {
@ -360,7 +359,6 @@ export const getMenuZastavkaUmichala = async (firstDayOfWeek: Date, mock: boolea
price: "",
isSoup: false,
}];
continue;
} else {
const url = (currentDate.getDate() === nowDate) ?
ZASTAVKAUMICHALA_URL : ZASTAVKAUMICHALA_URL + '/?do=dailyMenu-changeDate&dailyMenu-dateString=' + formatDate(currentDate, 'DD.MM.YYYY');

View File

@ -1,5 +1,5 @@
import express, { NextFunction } from "express";
import { getLogin, getTrusted } from "../auth";
import { getLogin } from "../auth";
import { parseToken } from "../utils";
import path from "path";
import fs from "fs";
@ -34,17 +34,12 @@ function generateUrl() {
*/
function getEasterEggImage(req: any, res: any, next: NextFunction) {
const login = getLogin(parseToken(req));
const trusted = getTrusted(parseToken(req));
try {
// TODO vrátit!
// if (trusted) {
if (true) {
if (login in easterEggs) {
const imagePath = easterEggs[login][Math.floor(Math.random() * easterEggs[login].length)].path;
res.sendFile(path.join(__dirname, IMAGES_PATH, imagePath));
return;
}
}
res.sendStatus(404);
} catch (e: any) { next(e) }
}
@ -124,7 +119,7 @@ let easterEggs: EasterEggsJson;
if (fs.existsSync(EASTER_EGGS_JSON_PATH)) {
const content = fs.readFileSync(EASTER_EGGS_JSON_PATH, 'utf-8');
easterEggs = JSON.parse(content);
for (const [key, eggs] of Object.entries(easterEggs)) {
for (const [_, eggs] of Object.entries(easterEggs)) {
for (const easterEgg of eggs) {
const url = generateUrl();
easterEgg.url = url;
@ -138,17 +133,12 @@ if (fs.existsSync(EASTER_EGGS_JSON_PATH)) {
// Získání náhodného easter eggu pro přihlášeného uživatele
router.get("/", async (req, res, next) => {
const login = getLogin(parseToken(req));
const trusted = getTrusted(parseToken(req));
try {
// TODO vrátit!
// if (trusted) {
if (true) {
if (easterEggs && login in easterEggs) {
const randomEasterEgg = easterEggs[login][Math.floor(Math.random() * easterEggs[login].length)];
const { path, startOffset, endOffset, ...strippedEasterEgg } = randomEasterEgg; // Path klient k ničemu nepotřebuje a nemá ho znát
return res.status(200).json({ ...strippedEasterEgg, ...getRandomPosition(startOffset, endOffset) });
}
}
return res.status(200).send();
} catch (e: any) { next(e) }
});

View File

@ -4,8 +4,7 @@ import { addChoice, getDateForWeekIndex, getToday, removeChoice, removeChoices,
import { getDayOfWeekIndex, parseToken } from "../utils";
import { getWebsocket } from "../websocket";
import { callNotifikace } from "../notifikace";
import { AddChoiceRequest, ChangeDepartureTimeRequest, IDayIndex, RemoveChoiceRequest, RemoveChoicesRequest, UpdateNoteRequest } from "../../../types";
import { UdalostEnum } from "../../../types";
import { AddChoiceRequest, ChangeDepartureTimeRequest, IDayIndex, RemoveChoiceRequest, RemoveChoicesRequest, UdalostEnum, UpdateNoteRequest } from "../../../types";
/**
* Ověří a vrátí index dne v týdnu z požadavku, za předpokladu, že byl předán, a je zároveň

View File

@ -2,8 +2,7 @@ import express, { Request, Response } from "express";
import { getLogin } from "../auth";
import { parseToken } from "../utils";
import { getUserVotes, updateFeatureVote } from "../voting";
import { UpdateFeatureVoteRequest } from "../../../types";
import { FeatureRequest } from "../../../types";
import { FeatureRequest, UpdateFeatureVoteRequest } from "../../../types";
const router = express.Router();

View File

@ -1,6 +1,6 @@
import { InsufficientPermissions, formatDate, getDayOfWeekIndex, getFirstWorkDayOfWeek, getHumanDate, getIsWeekend, getWeekNumber } from "./utils";
import getStorage from "./storage";
import { getMenuSladovnicka, getMenuTechTower, getMenuUMotliku, getMenuZastavkaUmichala, getMenuSenkSerikova } from "./restaurants";
import { getMenuSladovnicka, getMenuTechTower, getMenuZastavkaUmichala, getMenuSenkSerikova } from "./restaurants";
import { getTodayMock } from "./mock";
import { ClientData, DepartureTime, LunchChoice, Restaurant, RestaurantDayMenu, WeekMenu } from "../../types";
@ -152,10 +152,10 @@ export async function getRestaurantMenu(restaurant: keyof typeof Restaurant, dat
weekMenu[i][restaurant]!.closed = true;
}
}
break;
} catch (e: any) {
console.error("Selhalo načtení jídel pro podnik TechTower", e);
}
break;
case 'ZASTAVKAUMICHALA':
try {
const zastavkaUmichalaFood = await getMenuZastavkaUmichala(firstDay, mock);
@ -165,10 +165,10 @@ export async function getRestaurantMenu(restaurant: keyof typeof Restaurant, dat
weekMenu[i][restaurant]!.closed = true;
}
}
break;
} catch (e: any) {
console.error("Selhalo načtení jídel pro podnik Zastávka u Michala", e);
}
break;
case 'SENKSERIKOVA':
try {
const senkSerikovaFood = await getMenuSenkSerikova(firstDay, mock);
@ -178,10 +178,10 @@ export async function getRestaurantMenu(restaurant: keyof typeof Restaurant, dat
weekMenu[i][restaurant]!.closed = true;
}
}
break;
} catch (e: any) {
console.error("Selhalo načtení jídel pro podnik Pivovarský šenk Šeříková", e);
}
break;
}
await storage.setData(getMenuKey(usedDate), weekMenu);
}

View File

@ -2,7 +2,6 @@ import { DailyStats, LunchChoice, WeeklyStats } from "../../types";
import { getStatsMock } from "./mock";
import { getClientData } from "./service";
import getStorage from "./storage";
import { formatDate } from "./utils";
const storage = getStorage();

View File

@ -4,7 +4,7 @@ import { StorageInterface } from "./StorageInterface";
import JsonStorage from "./json";
import RedisStorage from "./redis";
const ENVIRONMENT = process.env.NODE_ENV || 'production';
const ENVIRONMENT = process.env.NODE_ENV ?? 'production';
dotenv.config({ path: path.resolve(__dirname, `../.env.${ENVIRONMENT}`) });
const JSON_KEY = 'json';

View File

@ -8,15 +8,15 @@ let client: RedisClientType;
*/
export default class RedisStorage implements StorageInterface {
constructor() {
const HOST = process.env.REDIS_HOST || 'localhost';
const PORT = process.env.REDIS_PORT || 6379;
const HOST = process.env.REDIS_HOST ?? 'localhost';
const PORT = process.env.REDIS_PORT ?? 6379;
client = createClient({ url: `redis://${HOST}:${PORT}` });
client.connect();
}
async hasData(key: string) {
const data = await client.json.get(key);
return (data ? true : false);
return (!!data);
}
async getData<Type>(key: string) {

View File

@ -8,7 +8,7 @@ export function formatDate(date: Date, format?: string) {
let month = String(date.getMonth() + 1).padStart(2, "0");
let year = String(date.getFullYear());
const f = (format === undefined) ? 'YYYY-MM-DD' : format;
const f = format ?? 'YYYY-MM-DD';
return f.replace('DD', day).replace('MM', month).replace('YYYY', year);
}
@ -61,10 +61,10 @@ export function getLastWorkDayOfWeek(date: Date) {
/** Vrátí pořadové číslo týdne předaného data v roce dle ISO 8601. */
export function getWeekNumber(inputDate: Date) {
var date = new Date(inputDate.getTime());
const date = new Date(inputDate.getTime());
date.setHours(0, 0, 0, 0);
date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7);
var week1 = new Date(date.getFullYear(), 0, 4);
const week1 = new Date(date.getFullYear(), 0, 4);
return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7);
}