Nahrazeni sablony kompletnim webem a klientskym portalem
Web a portal Automia v jednom containeru. Express obsluhuje API i zbuildovanou React aplikaci z dist/public. Obsah: - verejny web: homepage, sluzby, o nas, kontakt, 404 - prihlaseni pres JWT, demo ucty - portal: prehled s grafem, tickety, incidenty, automatizace, konektory - builder automatizaci: strom akci, vetveni podminkou - katalog 25 konektoru v 8 kategoriich - webhook s registrovanou adresou, token generuje server - zivy dashboard pres SSE vcetne simulace provozu - Swagger UI na /docs a OpenAPI na /openapi.json Soulad s AGENTS.md: - ROOT_PATH z prostredi, prefix proxy nikde nehardcodovan - mount na koren i na prefix, funguje s handle_path i bez nej - base tag a window.__BASE_PATH__ vkladane do index.html za behu - OpenAPI servers obsahuje prefix, Try it out vola spravnou adresu - povinne /health a /docs, port 3000, naslouchani na 0.0.0.0 - secrets jen z environment variables, nikdy v logu Dokumentace ve slozce documentation/.
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { config } from '../config.js';
|
||||
import { findUserById } from '../data/users.js';
|
||||
import type { JwtPayload, User } from '../types.js';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: User;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Overi Bearer token a doplni req.user. Bez tokenu / s neplatnym tokenem vraci 401. */
|
||||
export function requireAuth(req: Request, res: Response, next: NextFunction) {
|
||||
const header = req.header('authorization') ?? '';
|
||||
const [scheme, token] = header.split(' ');
|
||||
|
||||
if (scheme?.toLowerCase() !== 'bearer' || !token) {
|
||||
return res.status(401).json({ error: 'unauthorized', message: 'Chybí přihlašovací token.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const payload = jwt.verify(token, config.jwtSecret) as JwtPayload;
|
||||
const user = findUserById(payload.sub);
|
||||
if (!user) {
|
||||
console.warn(`[auth] token s platnym podpisem, ale neexistujici uzivatel: ${payload.sub}`);
|
||||
return res.status(401).json({ error: 'unauthorized', message: 'Uživatel neexistuje.' });
|
||||
}
|
||||
req.user = user;
|
||||
return next();
|
||||
} catch (err) {
|
||||
console.warn('[auth] neplatny token:', err instanceof Error ? err.message : err);
|
||||
return res.status(401).json({ error: 'unauthorized', message: 'Neplatný nebo expirovaný token.' });
|
||||
}
|
||||
}
|
||||
|
||||
/** Omezi pristup na konkretni role. Pouzivat vzdy AZ za requireAuth. */
|
||||
export function requireRole(...roles: User['role'][]) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (!req.user) {
|
||||
console.error('[auth] requireRole pouzito bez requireAuth');
|
||||
return res.status(401).json({ error: 'unauthorized' });
|
||||
}
|
||||
if (!roles.includes(req.user.role)) {
|
||||
return res.status(403).json({ error: 'forbidden', message: 'Nedostatečná oprávnění.' });
|
||||
}
|
||||
return next();
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user