Canvas: Persistent log storage

Change-Id: I3eac705329e6d68d8e4b9a371c6e9b9807f357ec
diff --git a/apps/canvas/back/src/index.ts b/apps/canvas/back/src/index.ts
index fdba5a0..f14cfd1 100644
--- a/apps/canvas/back/src/index.ts
+++ b/apps/canvas/back/src/index.ts
@@ -23,6 +23,7 @@
 	GithubRepository,
 } from "config";
 import { Instant, DateTimeFormatter, ZoneId } from "@js-joda/core";
+import LogStore from "./log.js";
 
 async function generateKey(root: string): Promise<[string, string]> {
 	const privKeyPath = path.join(root, "key");
@@ -36,6 +37,7 @@
 }
 
 const db = new PrismaClient();
+const logStore = new LogStore(db);
 const appManager = new AppManager();
 
 const projectMonitors = new Map<number, ProjectMonitor>();
@@ -763,18 +765,19 @@
 			return;
 		}
 
-		let lastLogCount = 0;
-		const initialLogs = monitor.getWorkerLog(service, workerId) || [];
+		let lastLogId: number | undefined = undefined;
+		const initialLogs = (await logStore.get(projectId, service, workerId)) || [];
 		sendLogs(initialLogs);
-		lastLogCount = initialLogs.length;
+		if (initialLogs.length > 0) {
+			lastLogId = initialLogs[initialLogs.length - 1].id;
+		}
 		resp.flushHeaders();
 
-		const intervalId = setInterval(() => {
-			const currentLogs = monitor.getWorkerLog(service, workerId) || [];
-			if (currentLogs.length > lastLogCount) {
-				const newLogs = currentLogs.slice(lastLogCount);
-				sendLogs(newLogs);
-				lastLogCount = currentLogs.length;
+		const intervalId = setInterval(async () => {
+			const currentLogs = (await logStore.get(projectId, service, workerId, lastLogId)) || [];
+			if (currentLogs.length > 0) {
+				sendLogs(currentLogs);
+				lastLogId = currentLogs[currentLogs.length - 1].id;
 			}
 		}, 500);
 
@@ -808,6 +811,9 @@
 			projectMonitors.set(projectId, monitor);
 		}
 		monitor.registerWorker(result.data);
+		if (result.data.logs) {
+			await logStore.store(projectId, result.data.service, result.data.id, result.data.logs);
+		}
 		resp.status(200);
 		resp.write(
 			JSON.stringify({