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 150 lines 4.7 kB view raw
1import { eq } from "drizzle-orm"; 2import { Hono } from "hono"; 3import { HTTPException } from "hono/http-exception"; 4import { describeRoute, resolver, validator } from "hono-openapi"; 5import * as v from "valibot"; 6import db from "../database"; 7import { projectTable, taskRelationTable, taskTable } from "../database/schema"; 8import { validateWorkspaceAccess } from "../utils/validate-workspace-access"; 9import { workspaceAccess } from "../utils/workspace-access-middleware"; 10import createTaskRelation from "./controllers/create-task-relation"; 11import deleteTaskRelation from "./controllers/delete-task-relation"; 12import getTaskRelations from "./controllers/get-task-relations"; 13 14const taskRelationSchema = v.object({ 15 id: v.string(), 16 sourceTaskId: v.string(), 17 targetTaskId: v.string(), 18 relationType: v.string(), 19 createdAt: v.date(), 20}); 21 22const taskRelation = new Hono<{ 23 Variables: { 24 userId: string; 25 }; 26}>() 27 .get( 28 "/:taskId", 29 describeRoute({ 30 operationId: "getTaskRelations", 31 tags: ["Task Relations"], 32 description: "Get all relations for a task", 33 responses: { 34 200: { 35 description: "Task relations with associated task data", 36 content: { 37 "application/json": { schema: resolver(v.any()) }, 38 }, 39 }, 40 }, 41 }), 42 validator("param", v.object({ taskId: v.string() })), 43 workspaceAccess.fromTaskId("taskId"), 44 async (c) => { 45 const { taskId } = c.req.valid("param"); 46 const relations = await getTaskRelations(taskId); 47 return c.json(relations); 48 }, 49 ) 50 .post( 51 "/", 52 describeRoute({ 53 operationId: "createTaskRelation", 54 tags: ["Task Relations"], 55 description: "Create a relation between two tasks", 56 responses: { 57 200: { 58 description: "Task relation created successfully", 59 content: { 60 "application/json": { schema: resolver(taskRelationSchema) }, 61 }, 62 }, 63 }, 64 }), 65 validator( 66 "json", 67 v.object({ 68 sourceTaskId: v.string(), 69 targetTaskId: v.string(), 70 relationType: v.picklist(["subtask", "blocks", "related"]), 71 }), 72 ), 73 async (c, next) => { 74 const userId = c.get("userId"); 75 if (!userId) { 76 throw new HTTPException(401, { message: "Unauthorized" }); 77 } 78 const { sourceTaskId } = c.req.valid("json"); 79 const [task] = await db 80 .select({ workspaceId: projectTable.workspaceId }) 81 .from(taskTable) 82 .innerJoin(projectTable, eq(taskTable.projectId, projectTable.id)) 83 .where(eq(taskTable.id, sourceTaskId)) 84 .limit(1); 85 if (!task) { 86 throw new HTTPException(404, { message: "Source task not found" }); 87 } 88 await validateWorkspaceAccess(userId, task.workspaceId); 89 return next(); 90 }, 91 async (c) => { 92 const { sourceTaskId, targetTaskId, relationType } = c.req.valid("json"); 93 const relation = await createTaskRelation({ 94 sourceTaskId, 95 targetTaskId, 96 relationType, 97 }); 98 return c.json(relation); 99 }, 100 ) 101 .delete( 102 "/:id", 103 describeRoute({ 104 operationId: "deleteTaskRelation", 105 tags: ["Task Relations"], 106 description: "Delete a task relation", 107 responses: { 108 200: { 109 description: "Task relation deleted successfully", 110 content: { 111 "application/json": { schema: resolver(taskRelationSchema) }, 112 }, 113 }, 114 }, 115 }), 116 validator("param", v.object({ id: v.string() })), 117 async (c, next) => { 118 const userId = c.get("userId"); 119 if (!userId) { 120 throw new HTTPException(401, { message: "Unauthorized" }); 121 } 122 const { id } = c.req.valid("param"); 123 const [rel] = await db 124 .select({ sourceTaskId: taskRelationTable.sourceTaskId }) 125 .from(taskRelationTable) 126 .where(eq(taskRelationTable.id, id)) 127 .limit(1); 128 if (!rel) { 129 throw new HTTPException(404, { message: "Task relation not found" }); 130 } 131 const [task] = await db 132 .select({ workspaceId: projectTable.workspaceId }) 133 .from(taskTable) 134 .innerJoin(projectTable, eq(taskTable.projectId, projectTable.id)) 135 .where(eq(taskTable.id, rel.sourceTaskId)) 136 .limit(1); 137 if (!task) { 138 throw new HTTPException(404, { message: "Task not found" }); 139 } 140 await validateWorkspaceAccess(userId, task.workspaceId); 141 return next(); 142 }, 143 async (c) => { 144 const { id } = c.req.valid("param"); 145 const relation = await deleteTaskRelation(id); 146 return c.json(relation); 147 }, 148 ); 149 150export default taskRelation;