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 98 lines 2.5 kB view raw
1import { Hono } from "hono"; 2import { describeRoute, resolver, validator } from "hono-openapi"; 3import * as v from "valibot"; 4import { activitySchema, projectSchema, taskSchema } from "../schemas"; 5import { workspaceAccess } from "../utils/workspace-access-middleware"; 6import globalSearch from "./controllers/global-search"; 7 8const workspaceSchema = v.object({ 9 id: v.string(), 10 name: v.string(), 11 slug: v.string(), 12 logo: v.nullable(v.string()), 13 metadata: v.nullable(v.string()), 14 description: v.nullable(v.string()), 15 createdAt: v.date(), 16}); 17 18const searchResultSchema = v.object({ 19 tasks: v.optional(v.array(taskSchema)), 20 projects: v.optional(v.array(projectSchema)), 21 workspaces: v.optional(v.array(workspaceSchema)), 22 comments: v.optional(v.array(activitySchema)), 23 activities: v.optional(v.array(activitySchema)), 24}); 25 26const search = new Hono<{ 27 Variables: { 28 userId: string; 29 }; 30}>().get( 31 "/", 32 describeRoute({ 33 operationId: "globalSearch", 34 tags: ["Search"], 35 description: 36 "Search across tasks, projects, workspaces, comments, and activities", 37 responses: { 38 200: { 39 description: "Search results", 40 content: { 41 "application/json": { schema: resolver(searchResultSchema) }, 42 }, 43 }, 44 }, 45 }), 46 validator( 47 "query", 48 v.object({ 49 q: v.pipe( 50 v.string(), 51 v.minLength(1, "Query must be at least 1 character"), 52 ), 53 type: v.optional( 54 v.picklist([ 55 "all", 56 "tasks", 57 "projects", 58 "workspaces", 59 "comments", 60 "activities", 61 ]), 62 "all", 63 ), 64 workspaceId: v.optional(v.string()), 65 projectId: v.optional(v.string()), 66 limit: v.optional( 67 v.pipe( 68 v.string(), 69 v.transform(Number), 70 v.minValue(1, "Limit must be at least 1"), 71 v.maxValue(50, "Limit must not exceed 50"), 72 ), 73 "20", 74 ), 75 userEmail: v.optional(v.pipe(v.string(), v.email())), 76 }), 77 ), 78 workspaceAccess.fromQuery(), 79 async (c) => { 80 const { q, type, workspaceId, projectId, limit, userEmail } = 81 c.req.valid("query"); 82 const userId = c.get("userId"); 83 84 const results = await globalSearch({ 85 query: q, 86 userId, 87 userEmail, 88 type, 89 workspaceId, 90 projectId, 91 limit: typeof limit === "string" ? Number(limit) : limit, 92 }); 93 94 return c.json(results); 95 }, 96); 97 98export default search;