Canvas: Support Anthropic Claude based AI agents

Change-Id: Ib74c9672da9a80a4f20d63741a471c728a435b8e
diff --git a/apps/canvas/back/prisma/migrations/20250703041848_anthropic_api_key/migration.sql b/apps/canvas/back/prisma/migrations/20250703041848_anthropic_api_key/migration.sql
new file mode 100644
index 0000000..d42d450
--- /dev/null
+++ b/apps/canvas/back/prisma/migrations/20250703041848_anthropic_api_key/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Project" ADD COLUMN "anthropicApiKey" TEXT;
diff --git a/apps/canvas/back/prisma/schema.prisma b/apps/canvas/back/prisma/schema.prisma
index adc03cf..cee6787 100644
--- a/apps/canvas/back/prisma/schema.prisma
+++ b/apps/canvas/back/prisma/schema.prisma
@@ -24,7 +24,8 @@
   deployKeyPublic String?
   githubToken     String?
   access          String?
-  geminiApiKey     String?
+  geminiApiKey    String?
+  anthropicApiKey String?
   logs            Log[]
 }
 
diff --git a/apps/canvas/back/src/index.ts b/apps/canvas/back/src/index.ts
index cd18249..b33cbbc 100644
--- a/apps/canvas/back/src/index.ts
+++ b/apps/canvas/back/src/index.ts
@@ -326,6 +326,7 @@
 				deployKeyPublic: true,
 				state: true,
 				geminiApiKey: true,
+				anthropicApiKey: true,
 			},
 		});
 		if (p === null) {
@@ -381,6 +382,7 @@
 					private: deployKey!,
 				},
 				geminiApiKey: p.geminiApiKey ?? undefined,
+				anthropicApiKey: p.anthropicApiKey ?? undefined,
 			},
 		};
 		try {
@@ -637,6 +639,26 @@
 	}
 };
 
+const handleUpdateAnthropicToken: express.Handler = async (req, resp) => {
+	try {
+		await db.project.update({
+			where: {
+				id: Number(req.params["projectId"]),
+				userId: resp.locals.userId,
+			},
+			data: {
+				anthropicApiKey: req.body.anthropicApiKey,
+			},
+		});
+		resp.status(200);
+	} catch (e) {
+		console.log(e);
+		resp.status(500);
+	} finally {
+		resp.end();
+	}
+};
+
 const getNetworks = (username?: string | undefined): Network[] => {
 	return [
 		{
@@ -673,6 +695,7 @@
 			deployKeyPublic: true,
 			githubToken: true,
 			geminiApiKey: true,
+			anthropicApiKey: true,
 			access: true,
 			instanceId: true,
 		},
@@ -698,6 +721,7 @@
 		integrations: {
 			github: !!project.githubToken,
 			gemini: !!project.geminiApiKey,
+			anthropic: !!project.anthropicApiKey,
 		},
 		networks: getNetworks(username),
 		services,
@@ -1074,6 +1098,7 @@
 	projectRouter.get("/:projectId/repos/github", handleGithubRepos);
 	projectRouter.post("/:projectId/github-token", handleUpdateGithubToken);
 	projectRouter.post("/:projectId/gemini-token", handleUpdateGeminiToken);
+	projectRouter.post("/:projectId/anthropic-token", handleUpdateAnthropicToken);
 	projectRouter.get("/:projectId/env", handleEnv);
 	projectRouter.post("/:projectId/reload/:serviceName/:workerId", handleReloadWorker);
 	projectRouter.post("/:projectId/reload", handleReload);