feat: limity skupinových objednávek nastavitelné přes env
CI / Generate TypeScript types (push) Successful in 12s
CI / Server unit tests (push) Successful in 25s
CI / Build server (push) Successful in 30s
CI / Build client (push) Successful in 39s
CI / Playwright E2E tests (push) Successful in 3m55s
CI / Build and push Docker image (push) Successful in 49s
CI / Notify (push) Successful in 2s
CI / Generate TypeScript types (push) Successful in 12s
CI / Server unit tests (push) Successful in 25s
CI / Build server (push) Successful in 30s
CI / Build client (push) Successful in 39s
CI / Playwright E2E tests (push) Successful in 3m55s
CI / Build and push Docker image (push) Successful in 49s
CI / Notify (push) Successful in 2s
This commit is contained in:
@@ -5,9 +5,20 @@ import { getWebsocket } from "../websocket";
|
||||
import { createGroup, deleteGroup, addGroupMember, removeGroupMember, updateGroupMember, setGroupState, updateGroupTimes, updateGroupFees, setGroupTracking, getOrderDates } from "../groups";
|
||||
import { GroupState } from "../../../types/gen/types.gen";
|
||||
import { checkOrderTracking } from "../orderTracking";
|
||||
import { getGroupLimits } from "../limits";
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Pojistky proti překlepům (dvojklik, omylem vložený text) — limity viz limits.ts (lze přepsat env)
|
||||
|
||||
/** Vrátí chybovou hlášku, pokud částka (v haléřích) není celé číslo v rozsahu 0 až limit. */
|
||||
function invalidAmount(value: unknown, label: string): string | undefined {
|
||||
const { maxAmountKc } = getGroupLimits();
|
||||
if (!Number.isInteger(value) || (value as number) < 0) return `Neplatná výše: ${label}`;
|
||||
if ((value as number) > maxAmountKc * 100) return `${label} může být nejvýše ${maxAmountKc} Kč`;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function broadcastExtra(data: any) {
|
||||
getWebsocket().emit("message", data);
|
||||
}
|
||||
@@ -25,7 +36,11 @@ router.post("/create", async (req: Request, res, next) => {
|
||||
if (!name || typeof name !== 'string') {
|
||||
return res.status(400).json({ error: 'Nebyl předán název skupiny' });
|
||||
}
|
||||
if (url != null && typeof url !== 'string') {
|
||||
const limits = getGroupLimits();
|
||||
if (name.length > limits.maxNameLength) {
|
||||
return res.status(400).json({ error: `Název skupiny může mít nejvýše ${limits.maxNameLength} znaků` });
|
||||
}
|
||||
if (url != null && (typeof url !== 'string' || url.length > limits.maxUrlLength)) {
|
||||
return res.status(400).json({ error: 'Neplatná URL nabídky' });
|
||||
}
|
||||
try {
|
||||
@@ -80,23 +95,29 @@ router.post("/updateMember", async (req: Request, res, next) => {
|
||||
if (!targetLogin) return res.status(400).json({ error: 'Nebyl předán login uživatele' });
|
||||
const patch: Record<string, any> = {};
|
||||
if (amount !== undefined) {
|
||||
if (!Number.isInteger(amount) || amount < 0) {
|
||||
return res.status(400).json({ error: 'Neplatná částka' });
|
||||
}
|
||||
const error = invalidAmount(amount, 'Částka');
|
||||
if (error) return res.status(400).json({ error });
|
||||
patch.amount = amount;
|
||||
}
|
||||
if (note !== undefined) {
|
||||
if (typeof note !== 'string') return res.status(400).json({ error: 'Neplatná poznámka' });
|
||||
const { maxNoteLength } = getGroupLimits();
|
||||
if (note.length > maxNoteLength) {
|
||||
return res.status(400).json({ error: `Poznámka může mít nejvýše ${maxNoteLength} znaků` });
|
||||
}
|
||||
patch.note = note;
|
||||
}
|
||||
if (surchargeText !== undefined) {
|
||||
if (typeof surchargeText !== 'string') return res.status(400).json({ error: 'Neplatný text příplatku' });
|
||||
const { maxSurchargeTextLength } = getGroupLimits();
|
||||
if (surchargeText.length > maxSurchargeTextLength) {
|
||||
return res.status(400).json({ error: `Popis příplatku může mít nejvýše ${maxSurchargeTextLength} znaků` });
|
||||
}
|
||||
patch.surchargeText = surchargeText;
|
||||
}
|
||||
if (surchargeAmount !== undefined) {
|
||||
if (!Number.isInteger(surchargeAmount) || surchargeAmount < 0) {
|
||||
return res.status(400).json({ error: 'Neplatná výše příplatku' });
|
||||
}
|
||||
const error = invalidAmount(surchargeAmount, 'Příplatek');
|
||||
if (error) return res.status(400).json({ error });
|
||||
patch.surchargeAmount = surchargeAmount;
|
||||
}
|
||||
try {
|
||||
@@ -124,20 +145,25 @@ router.post("/updateFees", async (req: Request, res, next) => {
|
||||
const login = getLogin(parseToken(req));
|
||||
const { id, fees, shipping, tip, discountType, discountValue } = req.body ?? {};
|
||||
if (!id) return res.status(400).json({ error: 'Nebylo předáno ID skupiny' });
|
||||
if (fees !== undefined && (!Number.isInteger(fees) || fees < 0)) {
|
||||
return res.status(400).json({ error: 'Neplatná výše poplatků' });
|
||||
}
|
||||
if (shipping !== undefined && (!Number.isInteger(shipping) || shipping < 0)) {
|
||||
return res.status(400).json({ error: 'Neplatná výše dopravy' });
|
||||
}
|
||||
if (tip !== undefined && (!Number.isInteger(tip) || tip < 0)) {
|
||||
return res.status(400).json({ error: 'Neplatná výše spropitného' });
|
||||
for (const [value, label] of [[fees, 'Poplatky'], [shipping, 'Doprava'], [tip, 'Spropitné']] as const) {
|
||||
const error = value !== undefined ? invalidAmount(value, label) : undefined;
|
||||
if (error) return res.status(400).json({ error });
|
||||
}
|
||||
if (discountType !== undefined && discountType !== '' && !['percent', 'fixed'].includes(discountType)) {
|
||||
return res.status(400).json({ error: 'Neplatný typ slevy' });
|
||||
}
|
||||
if (discountValue !== undefined && (!Number.isInteger(discountValue) || discountValue < 0)) {
|
||||
return res.status(400).json({ error: 'Neplatná výše slevy' });
|
||||
if (discountValue !== undefined) {
|
||||
if (!Number.isInteger(discountValue) || discountValue < 0) {
|
||||
return res.status(400).json({ error: 'Neplatná výše slevy' });
|
||||
}
|
||||
// Procentní sleva nad 100 % by vedla k záporným částkám
|
||||
if (discountType === 'percent' && discountValue > 100) {
|
||||
return res.status(400).json({ error: 'Procentní sleva může být nejvýše 100 %' });
|
||||
}
|
||||
const { maxAmountKc } = getGroupLimits();
|
||||
if (discountType !== 'percent' && discountValue > maxAmountKc * 100) {
|
||||
return res.status(400).json({ error: `Sleva může být nejvýše ${maxAmountKc} Kč` });
|
||||
}
|
||||
}
|
||||
try {
|
||||
const data = await updateGroupFees(login, id, fees, shipping, tip, discountType, discountValue);
|
||||
@@ -168,7 +194,7 @@ router.post("/setTracking", async (req: Request, res, next) => {
|
||||
const login = getLogin(parseToken(req));
|
||||
const { id, shareUrl } = req.body ?? {};
|
||||
if (!id) return res.status(400).json({ error: 'Nebylo předáno ID skupiny' });
|
||||
if (shareUrl !== undefined && typeof shareUrl !== 'string') {
|
||||
if (shareUrl !== undefined && (typeof shareUrl !== 'string' || shareUrl.length > getGroupLimits().maxUrlLength)) {
|
||||
return res.status(400).json({ error: 'Neplatný odkaz na sledování objednávky' });
|
||||
}
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user