kaneo (minimalist kanban) fork to experiment adding a tangled integration
github.com/usekaneo/kaneo
1import { Hono } from "hono";
2import { describeRoute, resolver, validator } from "hono-openapi";
3import * as v from "valibot";
4import { labelSchema } from "../schemas";
5import { workspaceAccess } from "../utils/workspace-access-middleware";
6import assignLabelToTask from "./controllers/assign-label-to-task";
7import createLabel from "./controllers/create-label";
8import deleteLabel from "./controllers/delete-label";
9import getLabel from "./controllers/get-label";
10import getLabelsByTaskId from "./controllers/get-labels-by-task-id";
11import getLabelsByWorkspaceId from "./controllers/get-labels-by-workspace-id";
12import unassignLabelFromTask from "./controllers/unassign-label-from-task";
13import updateLabel from "./controllers/update-label";
14
15const label = new Hono()
16 .get(
17 "/task/:taskId",
18 describeRoute({
19 operationId: "getTaskLabels",
20 tags: ["Labels"],
21 description: "Get all labels assigned to a specific task",
22 responses: {
23 200: {
24 description: "List of labels for the task",
25 content: {
26 "application/json": { schema: resolver(v.array(labelSchema)) },
27 },
28 },
29 },
30 }),
31 validator("param", v.object({ taskId: v.string() })),
32 workspaceAccess.fromTaskId(),
33 async (c) => {
34 const { taskId } = c.req.valid("param");
35 const labels = await getLabelsByTaskId(taskId);
36 return c.json(labels);
37 },
38 )
39 .get(
40 "/workspace/:workspaceId",
41 describeRoute({
42 operationId: "getWorkspaceLabels",
43 tags: ["Labels"],
44 description: "Get all labels for a specific workspace",
45 responses: {
46 200: {
47 description: "List of labels in the workspace",
48 content: {
49 "application/json": { schema: resolver(v.array(labelSchema)) },
50 },
51 },
52 },
53 }),
54 validator("param", v.object({ workspaceId: v.string() })),
55 workspaceAccess.fromParam(),
56 async (c) => {
57 const { workspaceId } = c.req.valid("param");
58 const labels = await getLabelsByWorkspaceId(workspaceId);
59 return c.json(labels);
60 },
61 )
62 .post(
63 "/",
64 describeRoute({
65 operationId: "createLabel",
66 tags: ["Labels"],
67 description: "Create a new label in a workspace",
68 responses: {
69 200: {
70 description: "Label created successfully",
71 content: {
72 "application/json": { schema: resolver(labelSchema) },
73 },
74 },
75 },
76 }),
77 validator(
78 "json",
79 v.object({
80 name: v.string(),
81 color: v.string(),
82 workspaceId: v.string(),
83 taskId: v.optional(v.string()),
84 }),
85 ),
86 workspaceAccess.fromBody(),
87 async (c) => {
88 const { name, color, workspaceId, taskId } = c.req.valid("json");
89 const label = await createLabel(name, color, taskId, workspaceId);
90 return c.json(label);
91 },
92 )
93 .get(
94 "/:id",
95 describeRoute({
96 operationId: "getLabel",
97 tags: ["Labels"],
98 description: "Get a specific label by ID",
99 responses: {
100 200: {
101 description: "Label details",
102 content: {
103 "application/json": { schema: resolver(labelSchema) },
104 },
105 },
106 },
107 }),
108 validator("param", v.object({ id: v.string() })),
109 workspaceAccess.fromLabel(),
110 async (c) => {
111 const { id } = c.req.valid("param");
112 const label = await getLabel(id);
113 return c.json(label);
114 },
115 )
116 .put(
117 "/:id/task",
118 describeRoute({
119 operationId: "attachLabelToTask",
120 tags: ["Labels"],
121 description: "Attach an existing label to a task",
122 responses: {
123 200: {
124 description: "Label attached to task successfully",
125 content: {
126 "application/json": { schema: resolver(labelSchema) },
127 },
128 },
129 },
130 }),
131 validator("param", v.object({ id: v.string() })),
132 validator("json", v.object({ taskId: v.string() })),
133 workspaceAccess.fromLabel(),
134 async (c) => {
135 const { id } = c.req.valid("param");
136 const { taskId } = c.req.valid("json");
137 const label = await assignLabelToTask(id, taskId);
138 return c.json(label);
139 },
140 )
141 .delete(
142 "/:id/task",
143 describeRoute({
144 operationId: "detachLabelFromTask",
145 tags: ["Labels"],
146 description: "Detach a label from its current task",
147 responses: {
148 200: {
149 description: "Label detached from task successfully",
150 content: {
151 "application/json": { schema: resolver(labelSchema) },
152 },
153 },
154 },
155 }),
156 validator("param", v.object({ id: v.string() })),
157 workspaceAccess.fromLabel(),
158 async (c) => {
159 const { id } = c.req.valid("param");
160 const label = await unassignLabelFromTask(id);
161 return c.json(label);
162 },
163 )
164 .put(
165 "/:id",
166 describeRoute({
167 operationId: "updateLabel",
168 tags: ["Labels"],
169 description: "Update an existing label",
170 responses: {
171 200: {
172 description: "Label updated successfully",
173 content: {
174 "application/json": { schema: resolver(labelSchema) },
175 },
176 },
177 },
178 }),
179 validator("param", v.object({ id: v.string() })),
180 validator(
181 "json",
182 v.object({
183 name: v.string(),
184 color: v.string(),
185 }),
186 ),
187 workspaceAccess.fromLabel(),
188 async (c) => {
189 const { id } = c.req.valid("param");
190 const { name, color } = c.req.valid("json");
191 const label = await updateLabel(id, name, color);
192 return c.json(label);
193 },
194 )
195 .delete(
196 "/:id",
197 describeRoute({
198 operationId: "deleteLabel",
199 tags: ["Labels"],
200 description: "Delete a label by ID",
201 responses: {
202 200: {
203 description: "Label deleted successfully",
204 content: {
205 "application/json": { schema: resolver(labelSchema) },
206 },
207 },
208 },
209 }),
210 validator("param", v.object({ id: v.string() })),
211 workspaceAccess.fromLabel(),
212 async (c) => {
213 const { id } = c.req.valid("param");
214 const label = await deleteLabel(id);
215 return c.json(label);
216 },
217 );
218
219export default label;