Cavnas: Implement basic service discovery logic

Change-Id: I71b25076dba94d6491ad4db748b259870991c526
diff --git a/apps/canvas/back/src/github.ts b/apps/canvas/back/src/github.ts
index b758aca..7657043 100644
--- a/apps/canvas/back/src/github.ts
+++ b/apps/canvas/back/src/github.ts
@@ -55,6 +55,27 @@
 	async addDeployKey(repoPath: string, key: string) {
 		const sshUrl = repoPath;
 		const repoOwnerAndName = sshUrl.replace("git@github.com:", "").replace(".git", "");
+		let existingKeys: z.infer<typeof DeployKeysSchema> = [];
+		const response = await axios.get(`https://api.github.com/repos/${repoOwnerAndName}/keys`, {
+			headers: this.getHeaders(),
+		});
+		const parsedResult = DeployKeysSchema.safeParse(response.data);
+		if (parsedResult.success) {
+			existingKeys = parsedResult.data;
+		} else {
+			console.error("Failed to parse existing deploy keys:", parsedResult.error);
+		}
+		const keyToAddParts = key.trim().split(" ");
+		const mainKeyPartToAdd = keyToAddParts.length > 1 ? keyToAddParts.slice(0, 2).join(" ") : key.trim();
+		const keyAlreadyExists = existingKeys.some((existingKeyObj) => {
+			const existingKeyParts = existingKeyObj.key.trim().split(" ");
+			const mainExistingKeyPart =
+				existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
+			return mainExistingKeyPart === mainKeyPartToAdd;
+		});
+		if (keyAlreadyExists) {
+			return;
+		}
 		await axios.post(
 			`https://api.github.com/repos/${repoOwnerAndName}/keys`,
 			{
@@ -76,15 +97,42 @@
 		});
 		const result = DeployKeysSchema.safeParse(response.data);
 		if (!result.success) {
-			throw new Error("Failed to parse deploy keys response");
+			console.error("Failed to parse deploy keys response for removal:", result.error);
+			// Depending on desired robustness, you might throw an error or return early.
+			// For now, if parsing fails, we can't identify keys to remove.
+			throw new Error("Failed to parse deploy keys response during removal process");
 		}
-		const deployKeys = result.data.filter((k) => k.key === key);
+
+		// Extract the main part of the key we intend to remove
+		const keyToRemoveParts = key.trim().split(" ");
+		const mainKeyPartToRemove = keyToRemoveParts.length > 1 ? keyToRemoveParts.slice(0, 2).join(" ") : key.trim();
+
+		const deployKeysToDelete = result.data.filter((existingKeyObj) => {
+			const existingKeyParts = existingKeyObj.key.trim().split(" ");
+			const mainExistingKeyPart =
+				existingKeyParts.length > 1 ? existingKeyParts.slice(0, 2).join(" ") : existingKeyObj.key.trim();
+			return mainExistingKeyPart === mainKeyPartToRemove;
+		});
+
+		if (deployKeysToDelete.length === 0) {
+			console.log(
+				`No deploy key matching '${mainKeyPartToRemove.substring(0, 50)}...' found in repo ${repoOwnerAndName} for removal.`,
+			);
+			return;
+		}
+
 		await Promise.all(
-			deployKeys.map((deployKey) =>
-				axios.delete(`https://api.github.com/repos/${repoOwnerAndName}/keys/${deployKey.id}`, {
+			deployKeysToDelete.map((deployKey) => {
+				console.log(
+					`Removing deploy key ID ${deployKey.id} ('${deployKey.key.substring(0, 50)}...') from repo ${repoOwnerAndName}`,
+				);
+				return axios.delete(`https://api.github.com/repos/${repoOwnerAndName}/keys/${deployKey.id}`, {
 					headers: this.getHeaders(),
-				}),
-			),
+				});
+			}),
+		);
+		console.log(
+			`Successfully initiated removal of ${deployKeysToDelete.length} matching deploy key(s) from ${repoOwnerAndName}.`,
 		);
 	}