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,80 @@
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { randomUUID } from 'node:crypto';
|
||||
|
||||
/**
|
||||
* Sbernice udalosti dashboardu. Vse, co zmeni data, sem posle udalost
|
||||
* a pripojeni klienti ji dostanou pres SSE stream okamzite.
|
||||
*
|
||||
* Zamerne v pameti procesu - pri vice instancich API by tohle musel
|
||||
* nahradit Redis pub/sub nebo jina sdilena fronta (viz dokumentace).
|
||||
*/
|
||||
|
||||
export type DashboardEventType =
|
||||
| 'ticket.created'
|
||||
| 'ticket.updated'
|
||||
| 'ticket.resolved'
|
||||
| 'incident.started'
|
||||
| 'incident.updated'
|
||||
| 'incident.resolved'
|
||||
| 'automation.created'
|
||||
| 'automation.updated'
|
||||
| 'automation.deleted'
|
||||
| 'automation.run'
|
||||
| 'webhook.received';
|
||||
|
||||
export interface DashboardEvent {
|
||||
id: string;
|
||||
type: DashboardEventType;
|
||||
at: string;
|
||||
/** Kratka veta pro uzivatele, zobrazuje se v notifikaci. */
|
||||
message: string;
|
||||
/** Doplnkova data, napr. id ticketu. */
|
||||
payload?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const CHANNEL = 'dashboard';
|
||||
const MAX_RECENT = 50;
|
||||
|
||||
const emitter = new EventEmitter();
|
||||
// Kazdy pripojeny klient je jeden listener, vychozich 10 je malo.
|
||||
emitter.setMaxListeners(200);
|
||||
|
||||
const recent: DashboardEvent[] = [];
|
||||
|
||||
export function publish(
|
||||
type: DashboardEventType,
|
||||
message: string,
|
||||
payload?: Record<string, unknown>,
|
||||
): DashboardEvent {
|
||||
const event: DashboardEvent = {
|
||||
id: randomUUID(),
|
||||
type,
|
||||
at: new Date().toISOString(),
|
||||
message,
|
||||
payload,
|
||||
};
|
||||
|
||||
recent.unshift(event);
|
||||
if (recent.length > MAX_RECENT) recent.length = MAX_RECENT;
|
||||
|
||||
emitter.emit(CHANNEL, event);
|
||||
console.info(`[event] ${type}: ${message} (posluchacu: ${emitter.listenerCount(CHANNEL)})`);
|
||||
return event;
|
||||
}
|
||||
|
||||
/** Vraci funkci pro odhlaseni. Volajici ji MUSI zavolat pri odpojeni. */
|
||||
export function subscribe(listener: (event: DashboardEvent) => void): () => void {
|
||||
emitter.on(CHANNEL, listener);
|
||||
return () => {
|
||||
emitter.off(CHANNEL, listener);
|
||||
};
|
||||
}
|
||||
|
||||
/** Poslednich par udalosti - klient je dostane hned po pripojeni. */
|
||||
export function recentEvents(limit = 10): DashboardEvent[] {
|
||||
return recent.slice(0, limit);
|
||||
}
|
||||
|
||||
export function listenerCount(): number {
|
||||
return emitter.listenerCount(CHANNEL);
|
||||
}
|
||||
Reference in New Issue
Block a user