merge: bring notifications-realtime + modules into master (preserves files attachments)

This commit is contained in:
egutierrez
2026-05-27 18:43:54 +02:00
38 changed files with 6106 additions and 106 deletions
+88
View File
@@ -5,8 +5,12 @@ import type {
CardHistoryResponse,
CardMessage,
Column,
KanbanModule,
Metrics,
MetricsFilter,
ModuleLog,
ModuleTestResult,
Notification,
Sticker,
User,
} from "./types";
@@ -28,6 +32,10 @@ export function getFlags(): Promise<Record<string, boolean>> {
return fetchJSON("/flags");
}
export function getVersion(): Promise<{ version: string }> {
return fetchJSON("/version");
}
export function createColumn(name: string): Promise<Column> {
return fetchJSON("/columns", { method: "POST", body: JSON.stringify({ name }) });
}
@@ -292,6 +300,61 @@ export function chatWSURL(): string {
return `${proto}//${window.location.host}/api/chat/ws`;
}
export function cardChatWSURL(cardId: string): string {
const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
return `${proto}//${window.location.host}/api/cards/${cardId}/chat/ws`;
}
export function listNotifications(unreadOnly = false): Promise<Notification[]> {
return fetchJSON(`/notifications${unreadOnly ? "?unread=1" : ""}`);
}
export function unreadNotificationCount(): Promise<{ count: number }> {
return fetchJSON("/notifications/unread-count");
}
export function markNotificationRead(id: string): Promise<void> {
return fetchJSON(`/notifications/${id}/read`, { method: "POST" });
}
export function markAllNotificationsRead(): Promise<{ count: number }> {
return fetchJSON("/notifications/read-all", { method: "POST" });
}
export function listModules(): Promise<KanbanModule[]> {
return fetchJSON("/modules");
}
export interface ModuleInput {
name: string;
kind: string;
enabled: boolean;
event_filter: string[];
config: Record<string, unknown>;
}
export function createModule(body: ModuleInput): Promise<KanbanModule> {
return fetchJSON("/modules", { method: "POST", body: JSON.stringify(body) });
}
export function updateModule(id: string, patch: Partial<ModuleInput>): Promise<KanbanModule> {
return fetchJSON(`/modules/${id}`, { method: "PATCH", body: JSON.stringify(patch) });
}
export function deleteModule(id: string): Promise<void> {
return fetchJSON(`/modules/${id}`, { method: "DELETE" });
}
export function listModuleLogs(id: string, limit = 100): Promise<ModuleLog[]> {
return fetchJSON(`/modules/${id}/logs?limit=${limit}`);
}
export function testModule(idOrDraft: string, body?: ModuleInput): Promise<ModuleTestResult> {
const init: RequestInit = { method: "POST" };
if (body) init.body = JSON.stringify(body);
return fetchJSON(`/modules/${idOrDraft}/test`, init);
}
// streamChat opens a WebSocket, sends the message history, and streams events
// to onEvent. Returns a Promise that resolves when the server closes the
// connection (after a "done" event) and rejects on transport errors.
@@ -417,6 +480,31 @@ export function deleteCardFile(fileId: string): Promise<void> {
return fetchJSON(`/files/${fileId}`, { method: "DELETE" });
}
// --- MCP per-user tokens ----------------------------------------------------
export interface MCPToken {
id: string;
name: string;
created_at: string;
last_used_at?: string;
}
export interface MCPTokenCreated extends MCPToken {
token: string;
}
export function createMCPToken(name: string): Promise<MCPTokenCreated> {
return fetchJSON("/mcp-tokens", { method: "POST", body: JSON.stringify({ name }) });
}
export function listMCPTokens(): Promise<MCPToken[]> {
return fetchJSON("/mcp-tokens");
}
export function revokeMCPToken(id: string): Promise<void> {
return fetchJSON(`/mcp-tokens/${id}`, { method: "DELETE" });
}
export function getMetrics(f: MetricsFilter): Promise<Metrics> {
const qs = new URLSearchParams();
if (f.from) qs.set("from", f.from);