kaneo (minimalist kanban) fork to experiment adding a tangled integration github.com/usekaneo/kaneo
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 156 lines 3.9 kB view raw
1import { eq } from "drizzle-orm"; 2import db from "../../../database"; 3import { taskTable } from "../../../database/schema"; 4import { 5 findExternalLink, 6 updateExternalLink, 7} from "../../github/services/link-manager"; 8import { formatTaskDescriptionFromIssue } from "../../github/utils/format"; 9import { 10 findAllIntegrationsByGiteaRepo, 11 repoOwnerLogin, 12} from "../services/integration-lookup"; 13import { baseUrlFromRepositoryHtmlUrl } from "../utils/webhook-repo"; 14 15type IssueEditedPayload = { 16 action: string; 17 issue: { 18 number: number; 19 title: string; 20 body: string | null; 21 html_url: string; 22 }; 23 changes?: { 24 title?: { from: string }; 25 body?: { from: string }; 26 }; 27 repository: { 28 owner: { login?: string; username?: string }; 29 name: string; 30 html_url: string; 31 }; 32}; 33 34export async function handleGiteaIssueEdited(payload: IssueEditedPayload) { 35 const { issue, repository, changes } = payload; 36 37 if (!changes?.title && !changes?.body) { 38 return; 39 } 40 41 const baseUrl = baseUrlFromRepositoryHtmlUrl(repository.html_url); 42 if (!baseUrl) return; 43 44 const owner = repoOwnerLogin(repository); 45 const integrations = await findAllIntegrationsByGiteaRepo( 46 baseUrl, 47 owner, 48 repository.name, 49 ); 50 51 for (const integration of integrations) { 52 const externalLink = await findExternalLink( 53 integration.id, 54 "issue", 55 issue.number.toString(), 56 ); 57 58 if (!externalLink) { 59 continue; 60 } 61 62 const task = await db.query.taskTable.findFirst({ 63 where: eq(taskTable.id, externalLink.taskId), 64 }); 65 66 if (!task) { 67 continue; 68 } 69 70 const metadata = externalLink.metadata 71 ? JSON.parse(externalLink.metadata) 72 : {}; 73 74 const updateData: Record<string, unknown> = {}; 75 const updatedMetadata = { ...metadata }; 76 77 if (!updatedMetadata.lastSync) { 78 updatedMetadata.lastSync = {}; 79 } 80 81 if (changes.title) { 82 const lastTitleSync = metadata.lastSync?.title; 83 84 let shouldUpdateTitle = true; 85 86 if (lastTitleSync) { 87 if ( 88 lastTitleSync.value === issue.title && 89 lastTitleSync.source === "kaneo" 90 ) { 91 shouldUpdateTitle = false; 92 } 93 94 const timeSinceLastSync = 95 Date.now() - new Date(lastTitleSync.timestamp).getTime(); 96 if (timeSinceLastSync < 2000 && shouldUpdateTitle) { 97 shouldUpdateTitle = false; 98 } 99 } 100 101 if (shouldUpdateTitle) { 102 updateData.title = issue.title; 103 updatedMetadata.lastSync.title = { 104 timestamp: new Date().toISOString(), 105 source: "gitea", 106 value: issue.title, 107 }; 108 } 109 } 110 111 if (changes.body) { 112 const lastDescSync = metadata.lastSync?.description; 113 const formattedDescription = formatTaskDescriptionFromIssue(issue.body); 114 115 let shouldUpdateDescription = true; 116 117 if (lastDescSync) { 118 if ( 119 lastDescSync.value === formattedDescription && 120 lastDescSync.source === "kaneo" 121 ) { 122 shouldUpdateDescription = false; 123 } 124 125 const timeSinceLastSync = 126 Date.now() - new Date(lastDescSync.timestamp).getTime(); 127 if (timeSinceLastSync < 2000 && shouldUpdateDescription) { 128 shouldUpdateDescription = false; 129 } 130 } 131 132 if (shouldUpdateDescription) { 133 updateData.description = formattedDescription; 134 updatedMetadata.lastSync.description = { 135 timestamp: new Date().toISOString(), 136 source: "gitea", 137 value: formattedDescription, 138 }; 139 } 140 } 141 142 if (Object.keys(updateData).length > 0) { 143 await db 144 .update(taskTable) 145 .set(updateData) 146 .where(eq(taskTable.id, task.id)); 147 148 await updateExternalLink(externalLink.id, { 149 title: issue.title, 150 metadata: updatedMetadata, 151 }); 152 } 153 154 return; 155 } 156}