Zavedení podpory pro Redis, agnostické úložiště dat
This commit is contained in:
		
							parent
							
								
									8a75c98c9a
								
							
						
					
					
						commit
						37542499a9
					
				| @ -1,9 +1,25 @@ | |||||||
| version: '3.8' | version: '3.8' | ||||||
| 
 | 
 | ||||||
| services: | services: | ||||||
|  |   redis: | ||||||
|  |     image: redis/redis-stack-server:7.2.0-RC3 | ||||||
|  |     restart: always | ||||||
|  |     ports: | ||||||
|  |       - '6379:6379' | ||||||
|  |     #expose: | ||||||
|  |     #  - 6379 | ||||||
|  |     environment: | ||||||
|  |       - REDIS_ARGS=--save 43200 1 --loglevel warning | ||||||
|  |     volumes: | ||||||
|  |       - redis:/data | ||||||
|   luncher: |   luncher: | ||||||
|  |     depends_on: | ||||||
|  |       - redis | ||||||
|     restart: always |     restart: always | ||||||
|     build: |     build: | ||||||
|       context: ./ |       context: ./ | ||||||
|     ports: |     ports: | ||||||
|       - 3001:3001 |       - 3001:3001 | ||||||
|  | volumes: | ||||||
|  |   redis: | ||||||
|  |     driver: local | ||||||
|  | |||||||
| @ -1,6 +1,16 @@ | |||||||
| # Secret pro podepisování JWT tokenů. Minimální délka 32 znaků. | # Secret pro podepisování JWT tokenů. Minimální délka 32 znaků. | ||||||
| # JWT_SECRET='CHANGE_ME' | # JWT_SECRET='CHANGE_ME' | ||||||
| 
 | 
 | ||||||
|  | # Datové úložiště. Musí být 'json' nebo 'redis' (není case sensitive). | ||||||
|  | # json - Data jsou ukládána do JSON souboru. Pomalé (práce se souborem), ale vhodné pro vývoj (snadnější prohlížení dat). | ||||||
|  | # redis - Data jsou ukládána v Redis serveru. Dle potřeby může být nutné upravit REDIS_ proměnné viz dále. | ||||||
|  | # STORAGE='json' | ||||||
|  | 
 | ||||||
|  | # Hostname/IP Redis serveru, pokud je použit STORAGE='redis'. Výchozí hodnota je 'localhost'. | ||||||
|  | # REDIS_HOST='localhost' | ||||||
|  | # Port Redis serveru, pokud je použit STORAGE='redis', výchozí hodnota je 6379. | ||||||
|  | # REDIS_PORT=6379 | ||||||
|  | 
 | ||||||
| # Zapne režim mockování obědových menu. | # Zapne režim mockování obědových menu. | ||||||
| # Vhodné pro vývoj o víkendech, svátcích a dalších dnech, pro které podniky nenabízejí obědové menu. | # Vhodné pro vývoj o víkendech, svátcích a dalších dnech, pro které podniky nenabízejí obědové menu. | ||||||
| # V tomto režimu vrací server vždy falešné datum (pracovní den) a pevně nadefinovanou, smyšlenou nabídku jídel. | # V tomto režimu vrací server vždy falešné datum (pracovní den) a pevně nadefinovanou, smyšlenou nabídku jídel. | ||||||
|  | |||||||
| @ -25,6 +25,7 @@ | |||||||
|     "dotenv": "^16.1.3", |     "dotenv": "^16.1.3", | ||||||
|     "express": "^4.18.2", |     "express": "^4.18.2", | ||||||
|     "jsonwebtoken": "^9.0.0", |     "jsonwebtoken": "^9.0.0", | ||||||
|  |     "redis": "^4.6.7", | ||||||
|     "simple-json-db": "^2.0.0", |     "simple-json-db": "^2.0.0", | ||||||
|     "socket.io": "^4.6.1" |     "socket.io": "^4.6.1" | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -1,3 +0,0 @@ | |||||||
| import JSONdb from 'simple-json-db'; |  | ||||||
| 
 |  | ||||||
| export const db = new JSONdb('./data.json'); |  | ||||||
| @ -105,8 +105,8 @@ app.use((req, res, next) => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** Vrátí data pro aktuální den. */ | /** Vrátí data pro aktuální den. */ | ||||||
| app.get("/api/data", (req, res) => { | app.get("/api/data", async (req, res) => { | ||||||
|     res.status(200).json(getData()); |     res.status(200).json(await getData()); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** Vrátí obědové menu pro dostupné podniky. */ | /** Vrátí obědové menu pro dostupné podniky. */ | ||||||
| @ -122,29 +122,28 @@ app.get("/api/food", async (req, res) => { | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** Vrátí seznam dostupných pizz. */ | /** Vrátí seznam dostupných pizz. */ | ||||||
| app.get("/api/pizza", (req, res) => { | app.get("/api/pizza", async (req, res) => { | ||||||
|     fetchPizzy().then(pizzaList => { |     const pizzaList = await fetchPizzy(); | ||||||
|     // console.log("Výsledek", pizzaList);
 |     // console.log("Výsledek", pizzaList);
 | ||||||
|     res.status(200).json(pizzaList); |     res.status(200).json(pizzaList); | ||||||
|     }); |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** Založí pizza day pro aktuální den, za předpokladu že dosud neexistuje. */ | /** Založí pizza day pro aktuální den, za předpokladu že dosud neexistuje. */ | ||||||
| app.post("/api/createPizzaDay", (req, res) => { | app.post("/api/createPizzaDay", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = createPizzaDay(login); |     const data = await createPizzaDay(login); | ||||||
|     res.status(200).json(data); |     res.status(200).json(data); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| /** Smaže pizza day pro aktuální den, za předpokladu že existuje. */ | /** Smaže pizza day pro aktuální den, za předpokladu že existuje. */ | ||||||
| app.post("/api/deletePizzaDay", (req, res) => { | app.post("/api/deletePizzaDay", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = deletePizzaDay(login); |     const data = await deletePizzaDay(login); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/addPizza", (req, res) => { | app.post("/api/addPizza", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     if (isNaN(req.body?.pizzaIndex)) { |     if (isNaN(req.body?.pizzaIndex)) { | ||||||
|         throw Error("Nebyl předán index pizzy"); |         throw Error("Nebyl předán index pizzy"); | ||||||
| @ -154,88 +153,87 @@ app.post("/api/addPizza", (req, res) => { | |||||||
|         throw Error("Nebyl předán index velikosti pizzy"); |         throw Error("Nebyl předán index velikosti pizzy"); | ||||||
|     } |     } | ||||||
|     const pizzaSizeIndex = req.body.pizzaSizeIndex; |     const pizzaSizeIndex = req.body.pizzaSizeIndex; | ||||||
|     fetchPizzy().then(pizzy => { |     const pizzy = await fetchPizzy(); | ||||||
|     if (!pizzy[pizzaIndex]) { |     if (!pizzy[pizzaIndex]) { | ||||||
|         throw Error("Neplatný index pizzy: " + pizzaIndex); |         throw Error("Neplatný index pizzy: " + pizzaIndex); | ||||||
|     } |     } | ||||||
|     if (!pizzy[pizzaIndex].sizes[pizzaSizeIndex]) { |     if (!pizzy[pizzaIndex].sizes[pizzaSizeIndex]) { | ||||||
|         throw Error("Neplatný index velikosti pizzy: " + pizzaSizeIndex); |         throw Error("Neplatný index velikosti pizzy: " + pizzaSizeIndex); | ||||||
|     } |     } | ||||||
|         const data = addPizzaOrder(login, pizzy[pizzaIndex], pizzy[pizzaIndex].sizes[pizzaSizeIndex]); |     const data = await addPizzaOrder(login, pizzy[pizzaIndex], pizzy[pizzaIndex].sizes[pizzaSizeIndex]); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
|     }) |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/removePizza", (req, res) => { | app.post("/api/removePizza", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     if (!req.body?.pizzaOrder) { |     if (!req.body?.pizzaOrder) { | ||||||
|         throw Error("Nebyla předána objednávka"); |         throw Error("Nebyla předána objednávka"); | ||||||
|     } |     } | ||||||
|     const data = removePizzaOrder(login, req.body?.pizzaOrder); |     const data = await removePizzaOrder(login, req.body?.pizzaOrder); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/lockPizzaDay", (req, res) => { | app.post("/api/lockPizzaDay", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = lockPizzaDay(login); |     const data = await lockPizzaDay(login); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/unlockPizzaDay", (req, res) => { | app.post("/api/unlockPizzaDay", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = unlockPizzaDay(login); |     const data = await unlockPizzaDay(login); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/finishOrder", (req, res) => { | app.post("/api/finishOrder", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = finishPizzaOrder(login); |     const data = await finishPizzaOrder(login); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/finishDelivery", (req, res) => { | app.post("/api/finishDelivery", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = finishPizzaDelivery(login, req.body.bankAccount, req.body.bankAccountHolder); |     const data = await finishPizzaDelivery(login, req.body.bankAccount, req.body.bankAccountHolder); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json({}); |     res.status(200).json({}); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/addChoice", (req, res) => { | app.post("/api/addChoice", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const trusted = getTrusted(parseToken(req)); |     const trusted = getTrusted(parseToken(req)); | ||||||
|     if (req.body.locationIndex > -1) { |     if (req.body.locationIndex > -1) { | ||||||
|         const data = addChoice(login, trusted, req.body.locationIndex, req.body.foodIndex); |         const data = await addChoice(login, trusted, req.body.locationIndex, req.body.foodIndex); | ||||||
|         io.emit("message", data); |         io.emit("message", data); | ||||||
|         res.status(200).json(data); |         res.status(200).json(data); | ||||||
|     } |     } | ||||||
|     res.status(400); // TODO přidat popis chyby
 |     res.status(400); // TODO přidat popis chyby
 | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/removeChoices", (req, res) => { | app.post("/api/removeChoices", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = removeChoices(login, req.body.locationIndex); |     const data = await removeChoices(login, req.body.locationIndex); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json(data); |     res.status(200).json(data); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/removeChoice", (req, res) => { | app.post("/api/removeChoice", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     const data = removeChoice(login, req.body.locationIndex, req.body.foodIndex); |     const data = await removeChoice(login, req.body.locationIndex, req.body.foodIndex); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json(data); |     res.status(200).json(data); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| app.post("/api/updateNote", (req, res) => { | app.post("/api/updateNote", async (req, res) => { | ||||||
|     const login = getLogin(parseToken(req)); |     const login = getLogin(parseToken(req)); | ||||||
|     if (req.body.note && req.body.note.length > 100) { |     if (req.body.note && req.body.note.length > 100) { | ||||||
|         throw Error("Poznámka může mít maximálně 100 znaků"); |         throw Error("Poznámka může mít maximálně 100 znaků"); | ||||||
|     } |     } | ||||||
|     const data = updateNote(login, req.body.note); |     const data = await updateNote(login, req.body.note); | ||||||
|     io.emit("message", data); |     io.emit("message", data); | ||||||
|     res.status(200).json(data); |     res.status(200).json(data); | ||||||
| }); | }); | ||||||
|  | |||||||
| @ -1,8 +1,10 @@ | |||||||
| import { db } from "./database"; |  | ||||||
| import { formatDate, getHumanDate, getIsWeekend } from "./utils"; | import { formatDate, getHumanDate, getIsWeekend } from "./utils"; | ||||||
| import { callNotifikace } from "./notifikace"; | import { callNotifikace } from "./notifikace"; | ||||||
| import { generateQr } from "./qr"; | import { generateQr } from "./qr"; | ||||||
| import { ClientData, PizzaDayState, UdalostEnum, Pizza, PizzaSize, Order, PizzaOrder, Locations } from "../../types"; | import { ClientData, PizzaDayState, UdalostEnum, Pizza, PizzaSize, Order, PizzaOrder, Locations } from "../../types"; | ||||||
|  | import getStorage from "./storage"; | ||||||
|  | 
 | ||||||
|  | const storage = getStorage(); | ||||||
| 
 | 
 | ||||||
| /** Vrátí dnešní datum, případně fiktivní datum pro účely vývoje a testování. */ | /** Vrátí dnešní datum, případně fiktivní datum pro účely vývoje a testování. */ | ||||||
| function getToday(): Date { | function getToday(): Date { | ||||||
| @ -20,23 +22,22 @@ function getEmptyData(): ClientData { | |||||||
| /** | /** | ||||||
|  * Vrátí veškerá klientská data pro aktuální den. |  * Vrátí veškerá klientská data pro aktuální den. | ||||||
|  */ |  */ | ||||||
| export function getData(): ClientData { | export async function getData(): Promise<ClientData> { | ||||||
|     const data = db.get(formatDate(getToday())) || getEmptyData(); |     return await storage.getData(formatDate(getToday())) || getEmptyData(); | ||||||
|     return data; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Vytvoří pizza day pro aktuální den a vrátí data pro klienta. |  * Vytvoří pizza day pro aktuální den a vrátí data pro klienta. | ||||||
|  */ |  */ | ||||||
| export function createPizzaDay(creator: string): ClientData { | export async function createPizzaDay(creator: string): Promise<ClientData> { | ||||||
|     initIfNeeded(); |     await initIfNeeded(); | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (clientData.pizzaDay) { |     if (clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den již existuje"); |         throw Error("Pizza day pro dnešní den již existuje"); | ||||||
|     } |     } | ||||||
|     const data: ClientData = { pizzaDay: { state: PizzaDayState.CREATED, creator, orders: [] }, ...clientData }; |     const data: ClientData = { pizzaDay: { state: PizzaDayState.CREATED, creator, orders: [] }, ...clientData }; | ||||||
|     db.set(today, data); |     await storage.setData(today, data); | ||||||
|     callNotifikace({ input: { udalost: UdalostEnum.ZAHAJENA_PIZZA, user: creator } }) |     callNotifikace({ input: { udalost: UdalostEnum.ZAHAJENA_PIZZA, user: creator } }) | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| @ -44,9 +45,9 @@ export function createPizzaDay(creator: string): ClientData { | |||||||
| /** | /** | ||||||
|  * Smaže pizza day pro aktuální den. |  * Smaže pizza day pro aktuální den. | ||||||
|  */ |  */ | ||||||
| export function deletePizzaDay(login: string) { | export async function deletePizzaDay(login: string): Promise<ClientData> { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -54,7 +55,7 @@ export function deletePizzaDay(login: string) { | |||||||
|         throw Error("Login uživatele se neshoduje se zakladatelem Pizza Day"); |         throw Error("Login uživatele se neshoduje se zakladatelem Pizza Day"); | ||||||
|     } |     } | ||||||
|     delete clientData.pizzaDay; |     delete clientData.pizzaDay; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -65,9 +66,9 @@ export function deletePizzaDay(login: string) { | |||||||
|  * @param pizza zvolená pizza |  * @param pizza zvolená pizza | ||||||
|  * @param size zvolená velikost pizzy |  * @param size zvolená velikost pizzy | ||||||
|  */ |  */ | ||||||
| export function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) { | export async function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -91,7 +92,7 @@ export function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) { | |||||||
|     } |     } | ||||||
|     order.pizzaList.push(pizzaOrder); |     order.pizzaList.push(pizzaOrder); | ||||||
|     order.totalPrice += pizzaOrder.price; |     order.totalPrice += pizzaOrder.price; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -101,9 +102,9 @@ export function addPizzaOrder(login: string, pizza: Pizza, size: PizzaSize) { | |||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  * @param pizzaOrder objednávka pizzy |  * @param pizzaOrder objednávka pizzy | ||||||
|  */ |  */ | ||||||
| export function removePizzaOrder(login: string, pizzaOrder: PizzaOrder) { | export async function removePizzaOrder(login: string, pizzaOrder: PizzaOrder) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -122,7 +123,7 @@ export function removePizzaOrder(login: string, pizzaOrder: PizzaOrder) { | |||||||
|     if (order.pizzaList.length == 0) { |     if (order.pizzaList.length == 0) { | ||||||
|         clientData.pizzaDay.orders.splice(orderIndex, 1); |         clientData.pizzaDay.orders.splice(orderIndex, 1); | ||||||
|     } |     } | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -132,9 +133,9 @@ export function removePizzaOrder(login: string, pizzaOrder: PizzaOrder) { | |||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  * @returns aktuální data pro uživatele |  * @returns aktuální data pro uživatele | ||||||
|  */ |  */ | ||||||
| export function lockPizzaDay(login: string) { | export async function lockPizzaDay(login: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -145,7 +146,7 @@ export function lockPizzaDay(login: string) { | |||||||
|         throw Error("Pizza day není ve stavu " + PizzaDayState.CREATED + " nebo " + PizzaDayState.ORDERED); |         throw Error("Pizza day není ve stavu " + PizzaDayState.CREATED + " nebo " + PizzaDayState.ORDERED); | ||||||
|     } |     } | ||||||
|     clientData.pizzaDay.state = PizzaDayState.LOCKED; |     clientData.pizzaDay.state = PizzaDayState.LOCKED; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -155,9 +156,9 @@ export function lockPizzaDay(login: string) { | |||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  * @returns aktuální data pro uživatele |  * @returns aktuální data pro uživatele | ||||||
|  */ |  */ | ||||||
| export function unlockPizzaDay(login: string) { | export async function unlockPizzaDay(login: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -168,7 +169,7 @@ export function unlockPizzaDay(login: string) { | |||||||
|         throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED); |         throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED); | ||||||
|     } |     } | ||||||
|     clientData.pizzaDay.state = PizzaDayState.CREATED; |     clientData.pizzaDay.state = PizzaDayState.CREATED; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -178,9 +179,9 @@ export function unlockPizzaDay(login: string) { | |||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  * @returns aktuální data pro uživatele |  * @returns aktuální data pro uživatele | ||||||
|  */ |  */ | ||||||
| export function finishPizzaOrder(login: string) { | export async function finishPizzaOrder(login: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -191,7 +192,7 @@ export function finishPizzaOrder(login: string) { | |||||||
|         throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED); |         throw Error("Pizza day není ve stavu " + PizzaDayState.LOCKED); | ||||||
|     } |     } | ||||||
|     clientData.pizzaDay.state = PizzaDayState.ORDERED; |     clientData.pizzaDay.state = PizzaDayState.ORDERED; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     callNotifikace({ input: { udalost: UdalostEnum.OBJEDNANA_PIZZA, user: clientData?.pizzaDay?.creator } }) |     callNotifikace({ input: { udalost: UdalostEnum.OBJEDNANA_PIZZA, user: clientData?.pizzaDay?.creator } }) | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| @ -203,9 +204,9 @@ export function finishPizzaOrder(login: string) { | |||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  * @returns aktuální data pro uživatele |  * @returns aktuální data pro uživatele | ||||||
|  */ |  */ | ||||||
| export function finishPizzaDelivery(login: string, bankAccount?: string, bankAccountHolder?: string) { | export async function finishPizzaDelivery(login: string, bankAccount?: string, bankAccountHolder?: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     const clientData: ClientData = db.get(today); |     const clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -228,14 +229,15 @@ export function finishPizzaDelivery(login: string, bankAccount?: string, bankAcc | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function initIfNeeded() { | export async function initIfNeeded() { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     if (!db.has(today)) { |     const hasData = await storage.hasData(today); | ||||||
|         db.set(today, getEmptyData()); |     if (!hasData) { | ||||||
|  |         await storage.setData(today, getEmptyData()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -246,9 +248,9 @@ export function initIfNeeded() { | |||||||
|  * @param location vybrané "umístění" |  * @param location vybrané "umístění" | ||||||
|  * @returns  |  * @returns  | ||||||
|  */ |  */ | ||||||
| export function removeChoices(login: string, location: Locations) { | export async function removeChoices(login: string, location: Locations) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     let data: ClientData = db.get(today); |     let data: ClientData = await storage.getData(today); | ||||||
|     // TODO zajistit, že neověřený uživatel se stejným loginem nemůže mazat volby ověřeného
 |     // TODO zajistit, že neověřený uživatel se stejným loginem nemůže mazat volby ověřeného
 | ||||||
|     if (location in data.choices) { |     if (location in data.choices) { | ||||||
|         if (login in data.choices[location]) { |         if (login in data.choices[location]) { | ||||||
| @ -256,7 +258,7 @@ export function removeChoices(login: string, location: Locations) { | |||||||
|             if (Object.keys(data.choices[location]).length === 0) { |             if (Object.keys(data.choices[location]).length === 0) { | ||||||
|                 delete data.choices[location] |                 delete data.choices[location] | ||||||
|             } |             } | ||||||
|             db.set(today, data); |             await storage.setData(today, data); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     return data; |     return data; | ||||||
| @ -271,16 +273,16 @@ export function removeChoices(login: string, location: Locations) { | |||||||
|  * @param foodIndex index jídla v jídelním lístku daného umístění, pokud existuje |  * @param foodIndex index jídla v jídelním lístku daného umístění, pokud existuje | ||||||
|  * @returns  |  * @returns  | ||||||
|  */ |  */ | ||||||
| export function removeChoice(login: string, location: Locations, foodIndex: number) { | export async function removeChoice(login: string, location: Locations, foodIndex: number) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     let data: ClientData = db.get(today); |     let data: ClientData = await storage.getData(today); | ||||||
|     // TODO řešit ověření uživatele
 |     // TODO řešit ověření uživatele
 | ||||||
|     if (location in data.choices) { |     if (location in data.choices) { | ||||||
|         if (login in data.choices[location]) { |         if (login in data.choices[location]) { | ||||||
|             const index = data.choices[location][login].options.indexOf(foodIndex); |             const index = data.choices[location][login].options.indexOf(foodIndex); | ||||||
|             if (index > -1) { |             if (index > -1) { | ||||||
|                 data.choices[location][login].options.splice(index, 1) |                 data.choices[location][login].options.splice(index, 1) | ||||||
|                 db.set(today, data); |                 await storage.setData(today, data); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -292,17 +294,19 @@ export function removeChoice(login: string, location: Locations, foodIndex: numb | |||||||
|  *  |  *  | ||||||
|  * @param login login uživatele |  * @param login login uživatele | ||||||
|  */ |  */ | ||||||
| function removeChoiceIfPresent(login: string) { | async function removeChoiceIfPresent(login: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     let data: ClientData = db.get(today); |     let data: ClientData = await storage.getData(today); | ||||||
|     for (const key of Object.keys(data.choices)) { |     for (const key of Object.keys(data.choices)) { | ||||||
|         if (login in data.choices[key]) { |         if (login in data.choices[key]) { | ||||||
|             delete data.choices[key][login]; |             delete data.choices[key][login]; | ||||||
|             if (Object.keys(data.choices[key]).length === 0) { |             if (Object.keys(data.choices[key]).length === 0) { | ||||||
|                 delete data.choices[key]; |                 delete data.choices[key]; | ||||||
|             } |             } | ||||||
|  |             await storage.setData(today, data); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
| @ -314,10 +318,10 @@ function removeChoiceIfPresent(login: string) { | |||||||
|  * @param trusted příznak, zda se jedná o ověřeného uživatele |  * @param trusted příznak, zda se jedná o ověřeného uživatele | ||||||
|  * @returns aktuální data |  * @returns aktuální data | ||||||
|  */ |  */ | ||||||
| export function addChoice(login: string, trusted: boolean, location: Locations, foodIndex?: number) { | export async function addChoice(login: string, trusted: boolean, location: Locations, foodIndex?: number) { | ||||||
|     initIfNeeded(); |     await initIfNeeded(); | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     let data: ClientData = db.get(today); |     let data: ClientData = await storage.getData(today); | ||||||
|     // Ověření, že se neověřený užívatel nepokouší přepsat údaje ověřeného
 |     // Ověření, že se neověřený užívatel nepokouší přepsat údaje ověřeného
 | ||||||
|     const locations = Object.values(data?.choices); |     const locations = Object.values(data?.choices); | ||||||
|     let found = false; |     let found = false; | ||||||
| @ -333,7 +337,7 @@ export function addChoice(login: string, trusted: boolean, location: Locations, | |||||||
|     } |     } | ||||||
|     // Pokud měníme pouze lokaci, mažeme případné předchozí
 |     // Pokud měníme pouze lokaci, mažeme případné předchozí
 | ||||||
|     if (foodIndex == null) { |     if (foodIndex == null) { | ||||||
|         removeChoiceIfPresent(login); |         data = await removeChoiceIfPresent(login); | ||||||
|     } |     } | ||||||
|     if (!(location in data.choices)) { |     if (!(location in data.choices)) { | ||||||
|         data.choices[location] = {}; |         data.choices[location] = {}; | ||||||
| @ -347,14 +351,14 @@ export function addChoice(login: string, trusted: boolean, location: Locations, | |||||||
|     if (foodIndex != null && !data.choices[location][login].options.includes(foodIndex)) { |     if (foodIndex != null && !data.choices[location][login].options.includes(foodIndex)) { | ||||||
|         data.choices[location][login].options.push(foodIndex); |         data.choices[location][login].options.push(foodIndex); | ||||||
|     } |     } | ||||||
|     db.set(today, data); |     await storage.setData(today, data); | ||||||
|     return data; |     return data; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // TODO přejmenovat, ať je jasné že to patří k pizza day
 | // TODO přejmenovat, ať je jasné že to patří k pizza day
 | ||||||
| export function updateNote(login: string, note?: string) { | export async function updateNote(login: string, note?: string) { | ||||||
|     const today = formatDate(getToday()); |     const today = formatDate(getToday()); | ||||||
|     let clientData: ClientData = db.get(today); |     let clientData: ClientData = await storage.getData(today); | ||||||
|     if (!clientData.pizzaDay) { |     if (!clientData.pizzaDay) { | ||||||
|         throw Error("Pizza day pro dnešní den neexistuje"); |         throw Error("Pizza day pro dnešní den neexistuje"); | ||||||
|     } |     } | ||||||
| @ -366,6 +370,6 @@ export function updateNote(login: string, note?: string) { | |||||||
|         throw Error("Pizza day neobsahuje žádné objednávky uživatele " + login); |         throw Error("Pizza day neobsahuje žádné objednávky uživatele " + login); | ||||||
|     } |     } | ||||||
|     myOrder.note = note; |     myOrder.note = note; | ||||||
|     db.set(today, clientData); |     await storage.setData(today, clientData); | ||||||
|     return clientData; |     return clientData; | ||||||
| } | } | ||||||
							
								
								
									
										28
									
								
								server/src/storage/StorageInterface.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								server/src/storage/StorageInterface.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | import { ClientData } from "../../../types"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Interface pro úložiště dat. | ||||||
|  |  *  | ||||||
|  |  * Aktuálně pouze "primitivní" has, get a set odrážející původní JSON DB. | ||||||
|  |  * Postupem času lze předělat pro efektivnější využití Redis. | ||||||
|  |  */ | ||||||
|  | export interface StorageInterface { | ||||||
|  |     /** | ||||||
|  |      * Vrátí příznak, zda existují data pro předané datum. | ||||||
|  |      * @param date datum, pro které zjišťujeme data | ||||||
|  |      */ | ||||||
|  |     hasData(date: string): Promise<boolean>; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Vrátí veškerá data pro předané datum. | ||||||
|  |      * @param date datum, pro které vrátit data | ||||||
|  |      */ | ||||||
|  |     getData(date: string): Promise<ClientData>; | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Uloží data pro předané datum. | ||||||
|  |      * @param date datum, kterému patří ukládaná data | ||||||
|  |      * @param data data pro uložení | ||||||
|  |      */ | ||||||
|  |     setData(date: string, data: ClientData): Promise<void>; | ||||||
|  | } | ||||||
							
								
								
									
										19
									
								
								server/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								server/src/storage/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | import { StorageInterface } from "./StorageInterface"; | ||||||
|  | import JsonStorage from "./json"; | ||||||
|  | import RedisStorage from "./redis"; | ||||||
|  | 
 | ||||||
|  | const JSON_KEY = 'json'; | ||||||
|  | const REDIS_KEY = 'redis'; | ||||||
|  | 
 | ||||||
|  | let storage: StorageInterface; | ||||||
|  | if (!process.env.STORAGE || process.env.STORAGE?.toLowerCase() === JSON_KEY) { | ||||||
|  |     storage = new JsonStorage(); | ||||||
|  | } else if (process.env.STORAGE?.toLowerCase() === REDIS_KEY) { | ||||||
|  |     storage = new RedisStorage(); | ||||||
|  | } else { | ||||||
|  |     throw Error("Nepodporovaná hodnota proměnné STORAGE: " + process.env.STORAGE + ", podporované jsou 'json' nebo 'redis'"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default function getStorage(): StorageInterface { | ||||||
|  |     return storage; | ||||||
|  | } | ||||||
							
								
								
									
										24
									
								
								server/src/storage/json.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								server/src/storage/json.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | import JSONdb from 'simple-json-db'; | ||||||
|  | import { ClientData } from "../../../types"; | ||||||
|  | import { StorageInterface } from "./StorageInterface"; | ||||||
|  | 
 | ||||||
|  | const db = new JSONdb('./data.json'); | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implementace úložiště používající JSON soubor. | ||||||
|  |  */ | ||||||
|  | export default class JsonStorage implements StorageInterface { | ||||||
|  | 
 | ||||||
|  |     hasData(date: string): Promise<boolean> { | ||||||
|  |         return Promise.resolve(db.has(date)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     getData(date: string): Promise<ClientData> { | ||||||
|  |         return db.get(date); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     setData(date: string, data: ClientData): Promise<void> { | ||||||
|  |         db.set(date, data); | ||||||
|  |         return Promise.resolve(); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										32
									
								
								server/src/storage/redis.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								server/src/storage/redis.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | |||||||
|  | import { RedisClientType, createClient } from 'redis'; | ||||||
|  | import { StorageInterface } from "./StorageInterface"; | ||||||
|  | import { ClientData } from '../../../types'; | ||||||
|  | 
 | ||||||
|  | let client: RedisClientType; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Implementace úložiště využívající Redis server. | ||||||
|  |  */ | ||||||
|  | export default class RedisStorage implements StorageInterface { | ||||||
|  |     constructor() { | ||||||
|  |         const HOST = process.env.REDIS_HOST || 'localhost'; | ||||||
|  |         const PORT = process.env.REDIS_PORT || 6379; | ||||||
|  |         client = createClient({ url: `redis://${HOST}:${PORT}` }); | ||||||
|  |         client.connect(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async hasData(date: string) { | ||||||
|  |         const data = await client.json.get(date); | ||||||
|  |         return (data ? true : false); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async getData(date: string) { | ||||||
|  |         const data = await client.json.get(date, { path: '.' }); | ||||||
|  |         return data as unknown as ClientData; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     async setData(date: string, data: ClientData) { | ||||||
|  |         await client.json.set(date, '.', data as any); | ||||||
|  |         const check = await client.json.get(date); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								yarn.lock
									
									
									
									
									
								
							| @ -1738,6 +1738,40 @@ | |||||||
|   dependencies: |   dependencies: | ||||||
|     "@swc/helpers" "^0.5.0" |     "@swc/helpers" "^0.5.0" | ||||||
| 
 | 
 | ||||||
|  | "@redis/bloom@1.2.0": | ||||||
|  |   version "1.2.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" | ||||||
|  |   integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== | ||||||
|  | 
 | ||||||
|  | "@redis/client@1.5.8": | ||||||
|  |   version "1.5.8" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.8.tgz#a375ba7861825bd0d2dc512282b8bff7b98dbcb1" | ||||||
|  |   integrity sha512-xzElwHIO6rBAqzPeVnCzgvrnBEcFL1P0w8P65VNLRkdVW8rOE58f52hdj0BDgmsdOm4f1EoXPZtH4Fh7M/qUpw== | ||||||
|  |   dependencies: | ||||||
|  |     cluster-key-slot "1.1.2" | ||||||
|  |     generic-pool "3.9.0" | ||||||
|  |     yallist "4.0.0" | ||||||
|  | 
 | ||||||
|  | "@redis/graph@1.1.0": | ||||||
|  |   version "1.1.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.0.tgz#cc2b82e5141a29ada2cce7d267a6b74baa6dd519" | ||||||
|  |   integrity sha512-16yZWngxyXPd+MJxeSr0dqh2AIOi8j9yXKcKCwVaKDbH3HTuETpDVPcLujhFYVPtYrngSco31BUcSa9TH31Gqg== | ||||||
|  | 
 | ||||||
|  | "@redis/json@1.0.4": | ||||||
|  |   version "1.0.4" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.4.tgz#f372b5f93324e6ffb7f16aadcbcb4e5c3d39bda1" | ||||||
|  |   integrity sha512-LUZE2Gdrhg0Rx7AN+cZkb1e6HjoSKaeeW8rYnt89Tly13GBI5eP4CwDVr+MY8BAYfCg4/N15OUrtLoona9uSgw== | ||||||
|  | 
 | ||||||
|  | "@redis/search@1.1.3": | ||||||
|  |   version "1.1.3" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.1.3.tgz#b5a6837522ce9028267fe6f50762a8bcfd2e998b" | ||||||
|  |   integrity sha512-4Dg1JjvCevdiCBTZqjhKkGoC5/BcB7k9j99kdMnaXFXg8x4eyOIVg9487CMv7/BUVkFLZCaIh8ead9mU15DNng== | ||||||
|  | 
 | ||||||
|  | "@redis/time-series@1.0.4": | ||||||
|  |   version "1.0.4" | ||||||
|  |   resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.4.tgz#af85eb080f6934580e4d3b58046026b6c2b18717" | ||||||
|  |   integrity sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng== | ||||||
|  | 
 | ||||||
| "@restart/hooks@^0.4.9": | "@restart/hooks@^0.4.9": | ||||||
|   version "0.4.9" |   version "0.4.9" | ||||||
|   resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.9.tgz#ad858fb39d99e252cccce19416adc18fc3f18fcb" |   resolved "https://registry.yarnpkg.com/@restart/hooks/-/hooks-0.4.9.tgz#ad858fb39d99e252cccce19416adc18fc3f18fcb" | ||||||
| @ -3461,6 +3495,11 @@ clsx@^1.1.1: | |||||||
|   resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" |   resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" | ||||||
|   integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== |   integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== | ||||||
| 
 | 
 | ||||||
|  | cluster-key-slot@1.1.2: | ||||||
|  |   version "1.1.2" | ||||||
|  |   resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" | ||||||
|  |   integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== | ||||||
|  | 
 | ||||||
| co@^4.6.0: | co@^4.6.0: | ||||||
|   version "4.6.0" |   version "4.6.0" | ||||||
|   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" |   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" | ||||||
| @ -5097,6 +5136,11 @@ functions-have-names@^1.2.2, functions-have-names@^1.2.3: | |||||||
|   resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" |   resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" | ||||||
|   integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== |   integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== | ||||||
| 
 | 
 | ||||||
|  | generic-pool@3.9.0: | ||||||
|  |   version "3.9.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" | ||||||
|  |   integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== | ||||||
|  | 
 | ||||||
| gensync@^1.0.0-beta.2: | gensync@^1.0.0-beta.2: | ||||||
|   version "1.0.0-beta.2" |   version "1.0.0-beta.2" | ||||||
|   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" |   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" | ||||||
| @ -8308,6 +8352,18 @@ redent@^3.0.0: | |||||||
|     indent-string "^4.0.0" |     indent-string "^4.0.0" | ||||||
|     strip-indent "^3.0.0" |     strip-indent "^3.0.0" | ||||||
| 
 | 
 | ||||||
|  | redis@^4.6.7: | ||||||
|  |   version "4.6.7" | ||||||
|  |   resolved "https://registry.yarnpkg.com/redis/-/redis-4.6.7.tgz#c73123ad0b572776223f172ec78185adb72a6b57" | ||||||
|  |   integrity sha512-KrkuNJNpCwRm5vFJh0tteMxW8SaUzkm5fBH7eL5hd/D0fAkzvapxbfGPP/r+4JAXdQuX7nebsBkBqA2RHB7Usw== | ||||||
|  |   dependencies: | ||||||
|  |     "@redis/bloom" "1.2.0" | ||||||
|  |     "@redis/client" "1.5.8" | ||||||
|  |     "@redis/graph" "1.1.0" | ||||||
|  |     "@redis/json" "1.0.4" | ||||||
|  |     "@redis/search" "1.1.3" | ||||||
|  |     "@redis/time-series" "1.0.4" | ||||||
|  | 
 | ||||||
| regenerate-unicode-properties@^10.1.0: | regenerate-unicode-properties@^10.1.0: | ||||||
|   version "10.1.0" |   version "10.1.0" | ||||||
|   resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" |   resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" | ||||||
| @ -10090,16 +10146,16 @@ y18n@^5.0.5: | |||||||
|   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" |   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" | ||||||
|   integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== |   integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== | ||||||
| 
 | 
 | ||||||
|  | yallist@4.0.0, yallist@^4.0.0: | ||||||
|  |   version "4.0.0" | ||||||
|  |   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" | ||||||
|  |   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== | ||||||
|  | 
 | ||||||
| yallist@^3.0.2: | yallist@^3.0.2: | ||||||
|   version "3.1.1" |   version "3.1.1" | ||||||
|   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" |   resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" | ||||||
|   integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== |   integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== | ||||||
| 
 | 
 | ||||||
| yallist@^4.0.0: |  | ||||||
|   version "4.0.0" |  | ||||||
|   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" |  | ||||||
|   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== |  | ||||||
| 
 |  | ||||||
| yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: | yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: | ||||||
|   version "1.10.2" |   version "1.10.2" | ||||||
|   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" |   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user