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 39e2dfae265f26c8d6d888a560f50ab2d5d58b3f 174 lines 4.7 kB view raw
1import { eq } from "drizzle-orm"; 2import db from "../../../database"; 3import { taskTable } from "../../../database/schema"; 4import { findExternalLink, updateExternalLink } from "../services/link-manager"; 5import { findAllIntegrationsByRepo } from "../services/task-service"; 6import { formatTaskDescriptionFromIssue } from "../utils/format"; 7 8type IssueEditedPayload = { 9 action: string; 10 issue: { 11 number: number; 12 title: string; 13 body: string | null; 14 html_url: string; 15 }; 16 changes?: { 17 title?: { 18 from: string; 19 }; 20 body?: { 21 from: string; 22 }; 23 }; 24 repository: { 25 owner: { login: string }; 26 name: string; 27 full_name: string; 28 }; 29}; 30 31export async function handleIssueEdited(payload: IssueEditedPayload) { 32 const { issue, repository, changes } = payload; 33 34 if (!changes?.title && !changes?.body) { 35 console.log( 36 `Issue #${issue.number} edited but no title/body changes detected`, 37 ); 38 return; 39 } 40 41 const integrations = await findAllIntegrationsByRepo( 42 repository.owner.login, 43 repository.name, 44 ); 45 46 for (const integration of integrations) { 47 const externalLink = await findExternalLink( 48 integration.id, 49 "issue", 50 issue.number.toString(), 51 ); 52 53 if (!externalLink) { 54 continue; 55 } 56 57 const task = await db.query.taskTable.findFirst({ 58 where: eq(taskTable.id, externalLink.taskId), 59 }); 60 61 if (!task) { 62 console.error(`Task ${externalLink.taskId} not found`); 63 continue; 64 } 65 66 const metadata = externalLink.metadata 67 ? JSON.parse(externalLink.metadata) 68 : {}; 69 70 const updateData: Record<string, unknown> = {}; 71 const updatedMetadata = { ...metadata }; 72 73 if (!updatedMetadata.lastSync) { 74 updatedMetadata.lastSync = {}; 75 } 76 77 if (changes.title) { 78 const lastTitleSync = metadata.lastSync?.title; 79 80 let shouldUpdateTitle = true; 81 82 if (lastTitleSync) { 83 if ( 84 lastTitleSync.value === issue.title && 85 lastTitleSync.source === "kaneo" 86 ) { 87 console.log("Skipping title update - already synced from Kaneo"); 88 shouldUpdateTitle = false; 89 } 90 91 const timeSinceLastSync = 92 Date.now() - new Date(lastTitleSync.timestamp).getTime(); 93 if (timeSinceLastSync < 2000 && shouldUpdateTitle) { 94 console.log( 95 `Skipping title update - recent sync detected (${timeSinceLastSync}ms ago)`, 96 ); 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: "github", 106 value: issue.title, 107 }; 108 console.log( 109 `Updating task title from GitHub: "${changes.title.from}" → "${issue.title}"`, 110 ); 111 } 112 } 113 114 if (changes.body) { 115 const lastDescSync = metadata.lastSync?.description; 116 const formattedDescription = formatTaskDescriptionFromIssue(issue.body); 117 118 let shouldUpdateDescription = true; 119 120 if (lastDescSync) { 121 if ( 122 lastDescSync.value === formattedDescription && 123 lastDescSync.source === "kaneo" 124 ) { 125 console.log( 126 "Skipping description update - already synced from Kaneo", 127 ); 128 shouldUpdateDescription = false; 129 } 130 131 const timeSinceLastSync = 132 Date.now() - new Date(lastDescSync.timestamp).getTime(); 133 if (timeSinceLastSync < 2000 && shouldUpdateDescription) { 134 console.log( 135 `Skipping description update - recent sync detected (${timeSinceLastSync}ms ago)`, 136 ); 137 shouldUpdateDescription = false; 138 } 139 } 140 141 if (shouldUpdateDescription) { 142 updateData.description = formattedDescription; 143 updatedMetadata.lastSync.description = { 144 timestamp: new Date().toISOString(), 145 source: "github", 146 value: formattedDescription, 147 }; 148 console.log("Updating task description from GitHub"); 149 } 150 } 151 152 if (Object.keys(updateData).length > 0) { 153 await db 154 .update(taskTable) 155 .set(updateData) 156 .where(eq(taskTable.id, task.id)); 157 158 await updateExternalLink(externalLink.id, { 159 title: issue.title, 160 metadata: updatedMetadata, 161 }); 162 163 console.log( 164 `Synced ${Object.keys(updateData).join(", ")} from GitHub issue #${issue.number} to task ${task.id}`, 165 ); 166 } else { 167 console.log( 168 `No updates needed for task ${task.id} from issue #${issue.number}`, 169 ); 170 } 171 172 return; 173 } 174}