kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
at main 135 lines 3.5 kB view raw
1import { and, eq } from "drizzle-orm"; 2import { HTTPException } from "hono/http-exception"; 3import db from "../../database"; 4import { integrationTable, projectTable } from "../../database/schema"; 5import { defaultGitHubConfig } from "../../plugins/github/config"; 6import { getGithubApp } from "../../plugins/github/utils/github-app"; 7 8async function createGithubIntegration({ 9 projectId, 10 repositoryOwner, 11 repositoryName, 12}: { 13 projectId: string; 14 repositoryOwner: string; 15 repositoryName: string; 16}) { 17 const githubApp = getGithubApp(); 18 19 if (!githubApp) { 20 throw new HTTPException(500, { 21 message: "GitHub app not configured", 22 }); 23 } 24 25 const project = await db.query.projectTable.findFirst({ 26 where: eq(projectTable.id, projectId), 27 }); 28 29 if (!project) { 30 throw new HTTPException(404, { message: "Project not found" }); 31 } 32 33 const allGitHubIntegrations = await db.query.integrationTable.findMany({ 34 where: eq(integrationTable.type, "github"), 35 }); 36 37 for (const integration of allGitHubIntegrations) { 38 if (integration.projectId === projectId) { 39 continue; 40 } 41 42 try { 43 const config = JSON.parse(integration.config); 44 if ( 45 config.repositoryOwner === repositoryOwner && 46 config.repositoryName === repositoryName 47 ) { 48 throw new HTTPException(409, { 49 message: `Repository ${repositoryOwner}/${repositoryName} is already linked to another project`, 50 }); 51 } 52 } catch (error) { 53 if (error instanceof HTTPException) { 54 throw error; 55 } 56 } 57 } 58 59 let installationId: number | null = null; 60 try { 61 const { data: installation } = 62 await githubApp.octokit.rest.apps.getRepoInstallation({ 63 owner: repositoryOwner, 64 repo: repositoryName, 65 }); 66 installationId = installation.id; 67 } catch (error) { 68 console.warn("Could not get installation ID for repository:", error); 69 } 70 71 const existingIntegration = await db.query.integrationTable.findFirst({ 72 where: and( 73 eq(integrationTable.projectId, projectId), 74 eq(integrationTable.type, "github"), 75 ), 76 }); 77 78 const config = { 79 repositoryOwner, 80 repositoryName, 81 installationId, 82 ...defaultGitHubConfig, 83 }; 84 85 if (existingIntegration) { 86 const [updatedIntegration] = await db 87 .update(integrationTable) 88 .set({ 89 config: JSON.stringify(config), 90 isActive: true, 91 updatedAt: new Date(), 92 }) 93 .where( 94 and( 95 eq(integrationTable.projectId, projectId), 96 eq(integrationTable.type, "github"), 97 ), 98 ) 99 .returning(); 100 101 return { 102 id: updatedIntegration?.id, 103 projectId: updatedIntegration?.projectId, 104 repositoryOwner, 105 repositoryName, 106 installationId, 107 isActive: updatedIntegration?.isActive, 108 createdAt: updatedIntegration?.createdAt, 109 updatedAt: updatedIntegration?.updatedAt, 110 }; 111 } 112 113 const [newIntegration] = await db 114 .insert(integrationTable) 115 .values({ 116 projectId, 117 type: "github", 118 config: JSON.stringify(config), 119 isActive: true, 120 }) 121 .returning(); 122 123 return { 124 id: newIntegration?.id, 125 projectId: newIntegration?.projectId, 126 repositoryOwner, 127 repositoryName, 128 installationId, 129 isActive: newIntegration?.isActive, 130 createdAt: newIntegration?.createdAt, 131 updatedAt: newIntegration?.updatedAt, 132 }; 133} 134 135export default createGithubIntegration;