Canvas: Rework monitoring page
Display worker statuses with list of commands
Commit hash
Change-Id: I7054ecc5ce81f35cad3fe26fc20677b6f50d3147
diff --git a/apps/canvas/back/src/project_monitor.ts b/apps/canvas/back/src/project_monitor.ts
new file mode 100644
index 0000000..6c178f0
--- /dev/null
+++ b/apps/canvas/back/src/project_monitor.ts
@@ -0,0 +1,130 @@
+import { z } from "zod";
+
+export const WorkerSchema = z.object({
+ id: z.string(),
+ service: z.string(),
+ address: z.string().url(),
+ status: z.optional(
+ z.object({
+ repoOK: z.boolean(),
+ commit: z.string(),
+ commands: z.optional(
+ z.array(
+ z.object({
+ command: z.string(),
+ state: z.string(),
+ }),
+ ),
+ ),
+ }),
+ ),
+ logs: z.optional(z.string()),
+});
+
+export type Worker = z.infer<typeof WorkerSchema>;
+
+class ServiceMonitor {
+ private workers: Map<string, string> = new Map();
+ private logs: Map<string, string> = new Map();
+ private statuses: Map<string, Worker["status"]> = new Map();
+
+ constructor(public readonly serviceName: string) {}
+
+ registerWorker(workerId: string, workerAddress: string, workerLog?: string, workerStatus?: Worker["status"]): void {
+ this.workers.set(workerId, workerAddress);
+ if (workerLog) {
+ this.logs.set(workerId, workerLog);
+ }
+ if (workerStatus) {
+ this.statuses.set(workerId, workerStatus);
+ }
+ }
+
+ getWorkerAddress(workerId: string): string | undefined {
+ return this.workers.get(workerId);
+ }
+
+ getWorkerLog(workerId: string): string | undefined {
+ return this.logs.get(workerId);
+ }
+
+ getWorkerStatus(workerId: string): Worker["status"] | undefined {
+ return this.statuses.get(workerId);
+ }
+
+ getAllLogs(): Map<string, string> {
+ return new Map(this.logs);
+ }
+
+ getAllStatuses(): Map<string, Worker["status"]> {
+ return new Map(this.statuses);
+ }
+
+ getWorkerAddresses(): string[] {
+ return Array.from(this.workers.values());
+ }
+
+ getWorkerIds(): string[] {
+ return Array.from(this.workers.keys());
+ }
+
+ hasLogs(): boolean {
+ return this.logs.size > 0;
+ }
+}
+
+export class ProjectMonitor {
+ private serviceMonitors: Map<string, ServiceMonitor> = new Map();
+
+ constructor() {}
+
+ registerWorker(workerData: Worker): void {
+ let serviceMonitor = this.serviceMonitors.get(workerData.service);
+ if (!serviceMonitor) {
+ serviceMonitor = new ServiceMonitor(workerData.service);
+ this.serviceMonitors.set(workerData.service, serviceMonitor);
+ }
+ serviceMonitor.registerWorker(workerData.id, workerData.address, workerData.logs, workerData.status);
+ }
+
+ getWorkerAddresses(): string[] {
+ let allAddresses: string[] = [];
+ for (const serviceMonitor of this.serviceMonitors.values()) {
+ allAddresses = allAddresses.concat(serviceMonitor.getWorkerAddresses());
+ }
+ return Array.from(new Set(allAddresses));
+ }
+
+ getWorkerLog(serviceName: string, workerId: string): string | undefined {
+ const serviceMonitor = this.serviceMonitors.get(serviceName);
+ if (serviceMonitor) {
+ return serviceMonitor.getWorkerLog(workerId);
+ }
+ return undefined;
+ }
+
+ getAllServiceNames(): string[] {
+ return Array.from(this.serviceMonitors.keys());
+ }
+
+ hasLogs(): boolean {
+ for (const serviceMonitor of this.serviceMonitors.values()) {
+ if (serviceMonitor.hasLogs()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ getServiceMonitor(serviceName: string): ServiceMonitor | undefined {
+ return this.serviceMonitors.get(serviceName);
+ }
+
+ getWorkerStatusesForService(serviceName: string): Map<string, Worker["status"]> {
+ const serviceMonitor = this.serviceMonitors.get(serviceName);
+ if (serviceMonitor) {
+ return serviceMonitor.getAllStatuses();
+ }
+ return new Map();
+ }
+}