refactor: remove canvas, automations, and custom statuses; simplify notification and status model
This commit is contained in:
+11
-67
@@ -1,7 +1,7 @@
|
||||
import { Hono } from "hono";
|
||||
import { createHash } from "node:crypto";
|
||||
import { db, apiKeys, users, tasks, taskTags, tags as tagsTable, habits, habitCompletions, projects, notes, noteLinks, domains, activityFeed, webhooks, webhookDeliveries, statusDefinitions } from "@project-e/db";
|
||||
import { and, asc, desc, eq, ilike, inArray, isNull, or } from "drizzle-orm";
|
||||
import { db, apiKeys, users, tasks, taskTags, tags as tagsTable, habits, habitCompletions, projects, notes, noteLinks, domains, activityFeed, webhooks, webhookDeliveries } from "@project-e/db";
|
||||
import { and, asc, desc, eq, ilike, isNull, or } from "drizzle-orm";
|
||||
import { recordActivity } from "../middleware/activity";
|
||||
|
||||
export const mcpRoutes = new Hono();
|
||||
@@ -81,8 +81,7 @@ const tools: ToolDefinition[] = [
|
||||
type: "object",
|
||||
properties: {
|
||||
domain_id: { type: "string", description: "Workspace/domain ID" },
|
||||
status: { type: "string", description: "Filter by status category (todo, in_progress, done, cancelled)" },
|
||||
status_id: { type: "string", description: "Filter by a specific status definition ID" },
|
||||
status: { type: "string", enum: ["todo", "in_progress", "done", "cancelled"] },
|
||||
priority: { type: "string", enum: ["low", "medium", "high", "urgent"] },
|
||||
project_id: { type: "string" },
|
||||
search: { type: "string" },
|
||||
@@ -96,14 +95,7 @@ const tools: ToolDefinition[] = [
|
||||
eq(tasks.domainId, params.domain_id as string),
|
||||
isNull(tasks.deletedAt),
|
||||
];
|
||||
if (params.status_id) conditions.push(eq(tasks.statusId, params.status_id as string));
|
||||
if (params.status) {
|
||||
// Category filter — match every status definition in that category.
|
||||
const matching = await db.select({ id: statusDefinitions.id })
|
||||
.from(statusDefinitions)
|
||||
.where(eq(statusDefinitions.category, params.status as any));
|
||||
conditions.push(inArray(tasks.statusId, matching.map((s) => s.id)));
|
||||
}
|
||||
if (params.status) conditions.push(eq(tasks.status, params.status as any));
|
||||
if (params.priority) conditions.push(eq(tasks.priority, params.priority as any));
|
||||
if (params.project_id) conditions.push(eq(tasks.projectId, params.project_id as string));
|
||||
if (params.search) conditions.push(ilike(tasks.title, `%${params.search}%`));
|
||||
@@ -127,7 +119,7 @@ const tools: ToolDefinition[] = [
|
||||
domain_id: { type: "string", description: "Workspace/domain ID" },
|
||||
title: { type: "string" },
|
||||
description: { type: "string" },
|
||||
status_id: { type: "string", description: "Status definition ID (defaults to the project's default status)" },
|
||||
status: { type: "string", enum: ["todo", "in_progress", "done", "cancelled"] },
|
||||
priority: { type: "string", enum: ["low", "medium", "high", "urgent"] },
|
||||
due_date: { type: "string" },
|
||||
project_id: { type: "string" },
|
||||
@@ -135,33 +127,10 @@ const tools: ToolDefinition[] = [
|
||||
required: ["domain_id", "title"],
|
||||
},
|
||||
handler: async (params, auth) => {
|
||||
// Resolve status: explicit status_id, else the project's default status.
|
||||
let statusId = (params.status_id as string) ?? null;
|
||||
let completedAt = null;
|
||||
if (statusId) {
|
||||
const [status] = await db.select({ id: statusDefinitions.id, projectId: statusDefinitions.projectId, category: statusDefinitions.category })
|
||||
.from(statusDefinitions)
|
||||
.where(eq(statusDefinitions.id, statusId))
|
||||
.limit(1);
|
||||
if (!status) throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Status not found");
|
||||
if (!params.project_id || status.projectId !== params.project_id) {
|
||||
throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Status does not belong to the selected project");
|
||||
}
|
||||
completedAt = status.category === "done" ? new Date() : null;
|
||||
} else if (params.project_id) {
|
||||
const [defaultStatus] = await db.select({ id: statusDefinitions.id, category: statusDefinitions.category })
|
||||
.from(statusDefinitions)
|
||||
.where(and(eq(statusDefinitions.projectId, params.project_id as string), eq(statusDefinitions.isDefault, true)))
|
||||
.limit(1);
|
||||
statusId = defaultStatus?.id ?? null;
|
||||
completedAt = defaultStatus?.category === "done" ? new Date() : null;
|
||||
}
|
||||
|
||||
const [task] = await db.insert(tasks).values({
|
||||
title: params.title as string,
|
||||
description: (params.description as string) ?? null,
|
||||
statusId,
|
||||
completedAt,
|
||||
status: (params.status as any) ?? "todo",
|
||||
priority: (params.priority as any) ?? "medium",
|
||||
domainId: params.domain_id as string,
|
||||
projectId: (params.project_id as string) ?? null,
|
||||
@@ -173,7 +142,7 @@ const tools: ToolDefinition[] = [
|
||||
action: "created",
|
||||
entityType: "task",
|
||||
entityId: task.id,
|
||||
changes: { title: task.title, statusId: task.statusId },
|
||||
changes: { title: task.title, status: task.status },
|
||||
workspaceId: params.domain_id as string,
|
||||
});
|
||||
|
||||
@@ -189,7 +158,7 @@ const tools: ToolDefinition[] = [
|
||||
task_id: { type: "string" },
|
||||
title: { type: "string" },
|
||||
description: { type: "string" },
|
||||
status_id: { type: "string", description: "Status definition ID (must belong to the task's project)" },
|
||||
status: { type: "string", enum: ["todo", "in_progress", "done", "cancelled"] },
|
||||
priority: { type: "string", enum: ["low", "medium", "high", "urgent"] },
|
||||
due_date: { type: "string" },
|
||||
},
|
||||
@@ -203,19 +172,7 @@ const tools: ToolDefinition[] = [
|
||||
const updateData: Record<string, unknown> = {};
|
||||
if (params.title !== undefined) updateData.title = params.title;
|
||||
if (params.description !== undefined) updateData.description = params.description;
|
||||
if (params.status_id !== undefined) {
|
||||
const statusId = params.status_id as string;
|
||||
const [status] = await db.select({ id: statusDefinitions.id, projectId: statusDefinitions.projectId, category: statusDefinitions.category })
|
||||
.from(statusDefinitions)
|
||||
.where(eq(statusDefinitions.id, statusId))
|
||||
.limit(1);
|
||||
if (!status) throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Status not found");
|
||||
if (!existing.projectId || status.projectId !== existing.projectId) {
|
||||
throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Status does not belong to the task's project");
|
||||
}
|
||||
updateData.statusId = statusId;
|
||||
updateData.completedAt = status.category === "done" ? new Date() : null;
|
||||
}
|
||||
if (params.status !== undefined) updateData.status = params.status;
|
||||
if (params.priority !== undefined) updateData.priority = params.priority;
|
||||
if (params.due_date !== undefined) updateData.dueDate = params.due_date ? new Date(params.due_date as string) : null;
|
||||
updateData.updatedAt = new Date();
|
||||
@@ -279,21 +236,8 @@ const tools: ToolDefinition[] = [
|
||||
if (!existing) throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Task not found");
|
||||
await verifyDomainAccess(existing.domainId, auth.userId);
|
||||
|
||||
// Complete = move to the project's first "done"-category status.
|
||||
const [doneStatus] = await db.select({ id: statusDefinitions.id })
|
||||
.from(statusDefinitions)
|
||||
.where(and(
|
||||
eq(statusDefinitions.projectId, existing.projectId ?? ""),
|
||||
eq(statusDefinitions.category, "done"),
|
||||
))
|
||||
.orderBy(asc(statusDefinitions.sortOrder))
|
||||
.limit(1);
|
||||
if (!doneStatus) {
|
||||
throw new JsonRpcErrorResponse(JSONRPC_INTERNAL_ERROR, "Project has no done-category status");
|
||||
}
|
||||
|
||||
const [task] = await db.update(tasks)
|
||||
.set({ statusId: doneStatus.id, completedAt: new Date(), updatedAt: new Date() })
|
||||
.set({ status: "done", completedAt: new Date(), updatedAt: new Date() })
|
||||
.where(and(eq(tasks.id, params.task_id as string), isNull(tasks.deletedAt)))
|
||||
.returning();
|
||||
|
||||
@@ -622,7 +566,7 @@ const tools: ToolDefinition[] = [
|
||||
const results: Record<string, unknown[]> = {};
|
||||
|
||||
if (types.includes("tasks")) {
|
||||
results.tasks = await db.select({ id: tasks.id, title: tasks.title, statusId: tasks.statusId, priority: tasks.priority }).from(tasks)
|
||||
results.tasks = await db.select({ id: tasks.id, title: tasks.title, status: tasks.status, priority: tasks.priority }).from(tasks)
|
||||
.where(and(eq(tasks.domainId, domainId), isNull(tasks.deletedAt), ilike(tasks.title, `%${query}%`))).limit(limit);
|
||||
}
|
||||
if (types.includes("notes")) {
|
||||
|
||||
Reference in New Issue
Block a user