ocean-brain 0.13.3 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -2
- package/dist/mcp.js +439 -480
- package/package.json +3 -2
- package/server/client/dist/assets/ArrowClockwise.es-BwtKWcmj.js +1 -0
- package/server/client/dist/assets/{ArrowRight.es-BlNsZQFs.js → ArrowRight.es-E09Lfefr.js} +1 -1
- package/server/client/dist/assets/{AuxiliaryPanelHeader-HAttwVby.js → AuxiliaryPanelHeader-Bys98BJJ.js} +1 -1
- package/server/client/dist/assets/Calendar-BOhJYi8K.js +25 -0
- package/server/client/dist/assets/Callout-CAH1ua9B.js +1 -0
- package/server/client/dist/assets/{CaretUp.es-C8iY5OlB.js → CaretUp.es-DSWjIa-J.js} +1 -1
- package/server/client/dist/assets/{CheckCircle.es-Dqr3WImn.js → CheckCircle.es-Cou6rItm.js} +1 -1
- package/server/client/dist/assets/{Copy.es-fMv0ETir.js → Copy.es-ftEldGIL.js} +1 -1
- package/server/client/dist/assets/{File.es-CHtXXafR.js → File.es-DlKAX1uO.js} +1 -1
- package/server/client/dist/assets/{FileText.es-DPIwDCEe.js → FileText.es-D6JM_MqM.js} +1 -1
- package/server/client/dist/assets/{FlowArrow.es-Dxee98ir.js → FlowArrow.es-BRWXqf9A.js} +1 -1
- package/server/client/dist/assets/{Graph-hVj6vAYJ.js → Graph-BaqGi3M7.js} +1 -1
- package/server/client/dist/assets/{Graph-CdEu8eAn.js → Graph-L-d0cdMk.js} +1 -1
- package/server/client/dist/assets/{Image.es-CjXY1JVf.js → Image.es-Bh2mTNtr.js} +1 -1
- package/server/client/dist/assets/{Info.es-CYxg-Vku.js → Info.es-Bwz4CSvS.js} +1 -1
- package/server/client/dist/assets/Integration-C-rva2X9.js +1 -0
- package/server/client/dist/assets/{LinkSimple.es-B-P-7Iem.js → LinkSimple.es-iJvLf-MB.js} +1 -1
- package/server/client/dist/assets/Note-OO_uh2a7.js +19 -0
- package/server/client/dist/assets/{NotePropertiesPanel-6P7UfRBR.js → NotePropertiesPanel-BFWjxL_w.js} +1 -1
- package/server/client/dist/assets/{Plus.es-Dy6onD-Z.js → Plus.es-Dmb3oijD.js} +1 -1
- package/server/client/dist/assets/{Reminders-DnuALBL6.js → Reminders-CtpFtvu6.js} +1 -1
- package/server/client/dist/assets/{Search-AaD1Mt0d.js → Search-t48pkUg-.js} +2 -2
- package/server/client/dist/assets/{SurfaceCard-D5QxOte_.js → SurfaceCard-zdJ8k1o0.js} +1 -1
- package/server/client/dist/assets/{Tag-B2Qiq1Hv.js → Tag-BL2h3ZaX.js} +1 -1
- package/server/client/dist/assets/TagNotes-BpRTCEBf.js +1 -0
- package/server/client/dist/assets/Trash.es-DFnO8iF5.js +1 -0
- package/server/client/dist/assets/{ViewChip-BVkZAXyx.js → ViewChip-BdPhSEvG.js} +1 -1
- package/server/client/dist/assets/{Views-B5oh8GVM.js → Views-BoO6vgnF.js} +1 -1
- package/server/client/dist/assets/WarningCircle.es-BAvgGuQK.js +1 -0
- package/server/client/dist/assets/{abnfDiagram-N423BO3Z-D44O4T7q.js → abnfDiagram-N423BO3Z-CPgGmCbn.js} +1 -1
- package/server/client/dist/assets/app-OGsLwSqJ.js +151 -0
- package/server/client/dist/assets/app-yYMsgUrz.css +1 -0
- package/server/client/dist/assets/{arc-Cil9fXuW.js → arc-DqTgRpuF.js} +1 -1
- package/server/client/dist/assets/{architectureDiagram-T3A2C74G-Cjq6ayF2.js → architectureDiagram-T3A2C74G-rTYMAQ-J.js} +1 -1
- package/server/client/dist/assets/{blockDiagram-VBNYF7ZC-B2Xm5N0i.js → blockDiagram-VBNYF7ZC-BOluytVz.js} +1 -1
- package/server/client/dist/assets/{c4Diagram-5PPSVZJV-Dp0y-lCR.js → c4Diagram-5PPSVZJV-C8gbPpcb.js} +1 -1
- package/server/client/dist/assets/{calendar-data-wB0ifXdo.js → calendar-data-CwVF7Pz3.js} +1 -1
- package/server/client/dist/assets/channel--aI9fJWX.js +1 -0
- package/server/client/dist/assets/{chunk-2GRJ4B5K-CWaaNkFx.js → chunk-2GRJ4B5K-CRER5Ka0.js} +1 -1
- package/server/client/dist/assets/{chunk-2Q5K7J3B-Ces-xz2S.js → chunk-2Q5K7J3B-hxmJWGu_.js} +1 -1
- package/server/client/dist/assets/{chunk-5RXB4S5H-B-4Chlfc.js → chunk-5RXB4S5H-Bpr5Us8Y.js} +1 -1
- package/server/client/dist/assets/{chunk-5VM5RSS4-C8_HVwuz.js → chunk-5VM5RSS4-CKUz9fAb.js} +1 -1
- package/server/client/dist/assets/{chunk-6Q2QTUOP-xZ8ofRqk.js → chunk-6Q2QTUOP-B8XEOHPN.js} +1 -1
- package/server/client/dist/assets/{chunk-GF5L2VYU-Bpa43a7s.js → chunk-GF5L2VYU-BBUD-HJi.js} +1 -1
- package/server/client/dist/assets/{chunk-JWPE2WC7-DsyTXbuq.js → chunk-JWPE2WC7-Dq6_B8-d.js} +1 -1
- package/server/client/dist/assets/{chunk-KBJHAD2P-BDhjm9-8.js → chunk-KBJHAD2P--2VZEwNJ.js} +1 -1
- package/server/client/dist/assets/{chunk-RYQCIY6F-Cn-jdp6I.js → chunk-RYQCIY6F-CKCwnKiC.js} +1 -1
- package/server/client/dist/assets/{chunk-XXDRQBXY-BKsc_0wh.js → chunk-XXDRQBXY-VDufR1Oi.js} +1 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-BsbwE38V.js +1 -0
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-BsbwE38V.js +1 -0
- package/server/client/dist/assets/{cose-bilkent-JH36ORCC-jmn3Q4zj.js → cose-bilkent-JH36ORCC-D4nqOx8S.js} +1 -1
- package/server/client/dist/assets/{cynefinDiagram-MW4NZA55-BpLstuBJ.js → cynefinDiagram-MW4NZA55-CUuLMs-6.js} +1 -1
- package/server/client/dist/assets/{dagre-VZM6K2ZE-B5ft5Yzm.js → dagre-VZM6K2ZE-CUSkhZgS.js} +1 -1
- package/server/client/dist/assets/{diagram-7IWD3JNH-B_W9IsWZ.js → diagram-7IWD3JNH-D95eVcKx.js} +4 -4
- package/server/client/dist/assets/{diagram-B4RE2ZJO-32chS3GX.js → diagram-B4RE2ZJO-7OH_fb0K.js} +1 -1
- package/server/client/dist/assets/{diagram-LBJQPF4R-Bqupdb3-.js → diagram-LBJQPF4R-D9UwEIMd.js} +1 -1
- package/server/client/dist/assets/{diagram-Q27KOJAE-BD8jKBsv.js → diagram-Q27KOJAE-Dx7WFDuX.js} +1 -1
- package/server/client/dist/assets/{diagram-UB23O5K3-C40jAU8D.js → diagram-UB23O5K3-D2BYwAKT.js} +1 -1
- package/server/client/dist/assets/{ebnfDiagram-BXEA7PRR-BQfW9mFA.js → ebnfDiagram-BXEA7PRR-DbeA-fk4.js} +1 -1
- package/server/client/dist/assets/{erDiagram-JOGREHBK-ILZnLzz2.js → erDiagram-JOGREHBK-jWW-ICGO.js} +1 -1
- package/server/client/dist/assets/{flowDiagram-UKHOOZJN-CDmwr2dI.js → flowDiagram-UKHOOZJN-68iZdxxw.js} +1 -1
- package/server/client/dist/assets/{ganttDiagram-PKOTCBZU-DBcrUc49.js → ganttDiagram-PKOTCBZU-CxwY3VXg.js} +1 -1
- package/server/client/dist/assets/{gitGraphDiagram-DS77QQ5N-_xO6__FK.js → gitGraphDiagram-DS77QQ5N-Ci6o5MfZ.js} +1 -1
- package/server/client/dist/assets/{image.api-ChvlfuZg.js → image.api-CHuhmq_G.js} +1 -1
- package/server/client/dist/assets/index-CuCzOHJ6.js +1 -0
- package/server/client/dist/assets/{infoDiagram-6WML65LV-CuIIcu-L.js → infoDiagram-6WML65LV-DY7aMdjV.js} +1 -1
- package/server/client/dist/assets/integrations-B5SIEnG_.js +1 -0
- package/server/client/dist/assets/{ishikawaDiagram-WSZJBQD7-BVtRGTOj.js → ishikawaDiagram-WSZJBQD7-Bh5qKh1d.js} +1 -1
- package/server/client/dist/assets/{journeyDiagram-NVQOT4AX-D5-Nzb2N.js → journeyDiagram-NVQOT4AX-B_svXTTT.js} +1 -1
- package/server/client/dist/assets/{kanban-definition-27J2QSJJ-DmOhgC8H.js → kanban-definition-27J2QSJJ-CaJslHGg.js} +1 -1
- package/server/client/dist/assets/manage-image-Kd-7IGfB.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-Dw3tAW3h.js → manage-image-detail-HU0QLP-t.js} +1 -1
- package/server/client/dist/assets/mcp-BQ418pyx.js +5 -0
- package/server/client/dist/assets/{mcp-admin-adapter-D0R8p9az.js → mcp-admin-adapter-CUakQ_Xm.js} +1 -1
- package/server/client/dist/assets/{mermaid.core-C2tL9tq7.js → mermaid.core-BfUQM5vF.js} +4 -4
- package/server/client/dist/assets/{mindmap-definition-FAOFIHXS-DuvsPqrh.js → mindmap-definition-FAOFIHXS-DGbkJda2.js} +1 -1
- package/server/client/dist/assets/{note-core-YhYEmI_R.js → note-core-DRBsBf9H.js} +9 -9
- package/server/client/dist/assets/{note-runtime-B3ats-dC.js → note-runtime-ztZvXRIu.js} +16 -16
- package/server/client/dist/assets/{note-ui-BtzPk8gA.js → note-ui-CRcP5BjO.js} +1 -1
- package/server/client/dist/assets/{pegDiagram-VL7TDLO6-D64crEZV.js → pegDiagram-VL7TDLO6-Bip6zQct.js} +1 -1
- package/server/client/dist/assets/{pieDiagram-7S7Q4E2Y--kl7vopC.js → pieDiagram-7S7Q4E2Y-CjfjGY8Y.js} +1 -1
- package/server/client/dist/assets/{placeholder-B5uPsE7-.js → placeholder-D7JKnpuh.js} +1 -1
- package/server/client/dist/assets/{properties-B7Fv95qY.js → properties-CtagYHMr.js} +1 -1
- package/server/client/dist/assets/{quadrantDiagram-CIZ2JOQS-DuvJ4M2y.js → quadrantDiagram-CIZ2JOQS-B6F1yUT-.js} +1 -1
- package/server/client/dist/assets/{railroadDiagram-AXF67PYL-BMKmCOY7.js → railroadDiagram-AXF67PYL-D-0fLe0F.js} +1 -1
- package/server/client/dist/assets/{requirementDiagram-LRYGKXZP-DY1eJcy0.js → requirementDiagram-LRYGKXZP-tm4mnCyJ.js} +1 -1
- package/server/client/dist/assets/route-preload.js +2 -2
- package/server/client/dist/assets/{sankeyDiagram-W5VNT64P-C3Ka4GKM.js → sankeyDiagram-W5VNT64P-DSBWFurb.js} +1 -1
- package/server/client/dist/assets/search-B5tCkhwj.js +1 -0
- package/server/client/dist/assets/{search-admin-adapter-Daf7e2YQ.js → search-admin-adapter-Be9xSY88.js} +1 -1
- package/server/client/dist/assets/{sequenceDiagram-SI44F4Z6-Bhpwtk2s.js → sequenceDiagram-SI44F4Z6-KrwjjJIV.js} +1 -1
- package/server/client/dist/assets/{sizeCapture-X5ZJPWSS-BFta71TW.js → sizeCapture-X5ZJPWSS-DL2cn_Q6.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-OKZ733FA-D6GCn1io.js → stateDiagram-OKZ733FA-CKuNDH85.js} +1 -1
- package/server/client/dist/assets/{stateDiagram-v2-UEYNNEHI-g4yinGDn.js → stateDiagram-v2-UEYNNEHI-BFaj2wbM.js} +1 -1
- package/server/client/dist/assets/{swimlanes-SLNWSIFB-DLFvOjUl.js → swimlanes-SLNWSIFB-5S17nqhK.js} +2 -2
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-dexsHJq2.js +8 -0
- package/server/client/dist/assets/{tag.api-DEUROVKQ.js → tag.api-DGfi8hWL.js} +1 -1
- package/server/client/dist/assets/time-Kh6oav5R.js +361 -0
- package/server/client/dist/assets/{timeline-definition-Z64GVDOM-DTSIekSM.js → timeline-definition-Z64GVDOM-CU1hp_7d.js} +3 -3
- package/server/client/dist/assets/{trash-BHida7BG.js → trash-Di2BWyUO.js} +1 -1
- package/server/client/dist/assets/url-Bp8sXMb4.js +1 -0
- package/server/client/dist/assets/{useNoteMutate-BjvZUmtX.js → useNoteMutate-BQj6jHhV.js} +3 -3
- package/server/client/dist/assets/{useReminderMutate-C5n-5LM-.js → useReminderMutate-jubqwSD9.js} +1 -1
- package/server/client/dist/assets/{vennDiagram-T6HMQDX7-CMNqSI2X.js → vennDiagram-T6HMQDX7-MoiVLnil.js} +1 -1
- package/server/client/dist/assets/{wardleyDiagram-T6FBY63Y-DV-JOYqB.js → wardleyDiagram-T6FBY63Y-cZkfdI6C.js} +1 -1
- package/server/client/dist/assets/{xychartDiagram-ELKLHX3M-BUIniF4m.js → xychartDiagram-ELKLHX3M-B63hxD-r.js} +1 -1
- package/server/client/dist/index.html +6 -6
- package/server/dist/app.js +11 -1
- package/server/dist/features/app-gateway/access.js +73 -0
- package/server/dist/features/app-gateway/database.js +20 -0
- package/server/dist/features/app-gateway/gateway.js +208 -0
- package/server/dist/features/integration/access-events.js +17 -0
- package/server/dist/features/integration/auth.js +21 -0
- package/server/dist/features/integration/manifest.js +116 -0
- package/server/dist/features/integration/note-sync.js +156 -0
- package/server/dist/features/integration/schema.js +54 -0
- package/server/dist/features/integration/service.js +246 -0
- package/server/dist/features/integration/status-report.js +22 -0
- package/server/dist/features/mcp-admin/service.js +19 -83
- package/server/dist/features/note/graphql/note.field.resolver.js +1 -1
- package/server/dist/features/note/graphql/note.mutation.resolver.js +20 -12
- package/server/dist/features/note/graphql/note.query.resolver.js +10 -1
- package/server/dist/features/note/graphql/note.type-defs.js +13 -1
- package/server/dist/features/note/http/authoring.js +493 -0
- package/server/dist/features/note/http/mcp.js +15 -463
- package/server/dist/features/note/services/authoring.js +17 -1
- package/server/dist/features/note/services/change-events.js +26 -0
- package/server/dist/features/note/services/markdown-intent-write.js +1 -0
- package/server/dist/features/note/services/markdown-read.js +85 -0
- package/server/dist/features/note/services/properties.js +36 -30
- package/server/dist/features/note/services/search.js +17 -0
- package/server/dist/features/search/graphql/resolver.js +10 -1
- package/server/dist/features/search/graphql/type-defs.js +3 -0
- package/server/dist/features/search/search-manager.js +4 -7
- package/server/dist/features/tag/graphql/tag.query.resolver.js +10 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js +1 -0
- package/server/dist/features/view/graphql/mcp.query.resolver.js +22 -0
- package/server/dist/features/view/graphql/view.query.resolver.js +2 -0
- package/server/dist/features/view/graphql/view.type-defs.js +27 -0
- package/server/dist/features/view/services/mcp-read.js +142 -0
- package/server/dist/features/view/services/workspace.js +12 -4
- package/server/dist/modules/auth-guard.js +24 -1
- package/server/dist/modules/integration-token.js +27 -0
- package/server/dist/modules/mcp-auth.js +37 -23
- package/server/dist/modules/mcp-token.js +6 -25
- package/server/dist/modules/server-events.js +3 -0
- package/server/dist/routes/api.js +2 -0
- package/server/dist/routes/apps.js +163 -0
- package/server/dist/routes/graphql.js +2 -1
- package/server/dist/routes/index.js +2 -0
- package/server/dist/routes/integrations.js +137 -0
- package/server/dist/routes/mcp.js +29 -9
- package/server/prisma/migrations/20260917120000_0020_plugin_installations/migration.sql +39 -0
- package/server/prisma/migrations/20260917150000_0021_integration_connections/migration.sql +29 -0
- package/server/prisma/migrations/20260919090000_0022_integration_proxy_url/migration.sql +2 -0
- package/server/prisma/schema.prisma +36 -10
- package/server/client/dist/assets/Calendar-DwQBllfw.js +0 -25
- package/server/client/dist/assets/Callout-CcYyvHQP.js +0 -1
- package/server/client/dist/assets/Note-CpzLtGVM.js +0 -19
- package/server/client/dist/assets/TagNotes-B27G1ZVg.js +0 -1
- package/server/client/dist/assets/Trash.es-XEcWG_aq.js +0 -1
- package/server/client/dist/assets/app-EwvT7bMw.css +0 -1
- package/server/client/dist/assets/app-NgiBDISG.js +0 -151
- package/server/client/dist/assets/channel-DZFalh-h.js +0 -1
- package/server/client/dist/assets/classDiagram-JCYQIIEL-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/classDiagram-v2-OCEON4UE-CkEhFvVV.js +0 -1
- package/server/client/dist/assets/index-CIEap4bu.js +0 -1
- package/server/client/dist/assets/manage-image-CSJ3Flgc.js +0 -1
- package/server/client/dist/assets/mcp-D8footel.js +0 -5
- package/server/client/dist/assets/search-ddYl3qH-.js +0 -1
- package/server/client/dist/assets/swimlanesDiagram-ULZ7WXOC-Ci4mcvmo.js +0 -8
- package/server/client/dist/assets/time-PxWila4-.js +0 -361
- package/server/client/dist/assets/url-DKUMNVCI.js +0 -1
- package/server/dist/features/search/note-change.js +0 -16
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
+
import { emitNoteChange } from "../features/note/services/change-events.js";
|
|
2
3
|
const SERVER_EVENT_CHANNEL = "server-event";
|
|
3
4
|
const serverEventEmitter = new EventEmitter();
|
|
4
5
|
serverEventEmitter.setMaxListeners(0);
|
|
@@ -9,6 +10,8 @@ const emitServerEvent = (event) => {
|
|
|
9
10
|
...event
|
|
10
11
|
};
|
|
11
12
|
serverEventEmitter.emit(SERVER_EVENT_CHANNEL, nextEvent);
|
|
13
|
+
const type = event.type.replace(/^(mcp|integration)\./, "");
|
|
14
|
+
emitNoteChange({ type, noteId: Number(event.noteId) });
|
|
12
15
|
return nextEvent;
|
|
13
16
|
};
|
|
14
17
|
const subscribeServerEvents = (listener) => {
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { createCsrfProtection, requireSessionForWrite } from "../modules/auth-guard.js";
|
|
17
17
|
import { createAuthAttemptRateLimit, createSessionAccessRateLimit } from "../modules/rate-limit.js";
|
|
18
18
|
import { createServerEventsHandler } from "../modules/server-events-handler.js";
|
|
19
|
+
import { createIntegrationRouter } from "./integrations.js";
|
|
19
20
|
import { createMcpRouter } from "./mcp.js";
|
|
20
21
|
const createApiRouter = (authConfig, mcpAdminService) => {
|
|
21
22
|
return async (app) => {
|
|
@@ -23,6 +24,7 @@ const createApiRouter = (authConfig, mcpAdminService) => {
|
|
|
23
24
|
const requireSession = requireSessionForWrite(authConfig);
|
|
24
25
|
const sessionAccessRateLimit = app.rateLimit(createSessionAccessRateLimit());
|
|
25
26
|
app.register(createMcpRouter(authConfig, mcpAdminService), { prefix: "/mcp" });
|
|
27
|
+
app.register(createIntegrationRouter(authConfig));
|
|
26
28
|
app.get("/auth/session", createSessionStatusHandler(authConfig));
|
|
27
29
|
app.post(
|
|
28
30
|
"/auth/login",
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { buildUnauthorizedPayload } from "@baejino/auth";
|
|
2
|
+
import fastifyHttpProxy from "@fastify/http-proxy";
|
|
3
|
+
import {
|
|
4
|
+
APP_GATEWAY_ACCESS_COOKIE_NAME,
|
|
5
|
+
APP_GATEWAY_ACCESS_HEADER,
|
|
6
|
+
APP_GATEWAY_ACCESS_TTL_MS,
|
|
7
|
+
createAppGatewayAccessService,
|
|
8
|
+
extractAppGatewayAccessCookie,
|
|
9
|
+
extractAppGatewayAccessToken,
|
|
10
|
+
selectAppGatewayProtocol
|
|
11
|
+
} from "../features/app-gateway/access.js";
|
|
12
|
+
import {
|
|
13
|
+
APP_GATEWAY_PUBLIC_PREFIX,
|
|
14
|
+
createGatewayResponseHeaders,
|
|
15
|
+
createProxyRequestHeaders,
|
|
16
|
+
createProxyWebSocketHeaders,
|
|
17
|
+
getAppGatewayUpstream,
|
|
18
|
+
resolveAppGatewayConnection,
|
|
19
|
+
resolveAppGatewayOptions,
|
|
20
|
+
validateAppGatewayRequestPath
|
|
21
|
+
} from "../features/app-gateway/gateway.js";
|
|
22
|
+
import { createCsrfProtection, requireSessionForWrite } from "../modules/auth-guard.js";
|
|
23
|
+
import { createAppError } from "../modules/error-handler.js";
|
|
24
|
+
import { createSessionAccessRateLimit } from "../modules/rate-limit.js";
|
|
25
|
+
const PROXY_PREFIX = `${APP_GATEWAY_PUBLIC_PREFIX}/:connectionId`;
|
|
26
|
+
const SUPPORTED_METHODS = "DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT";
|
|
27
|
+
const CORS_ASSET_DESTINATIONS = /* @__PURE__ */ new Set(["audio", "font", "image", "manifest", "script", "style", "video", "worker"]);
|
|
28
|
+
const getConnectionId = (request) => {
|
|
29
|
+
const params = request.params;
|
|
30
|
+
return typeof params.connectionId === "string" ? params.connectionId : "";
|
|
31
|
+
};
|
|
32
|
+
const isWebSocketUpgrade = (request) => request.headers.upgrade?.toLowerCase() === "websocket";
|
|
33
|
+
const getHeaderValue = (value) => Array.isArray(value) ? value[0] : value;
|
|
34
|
+
const getPublicProtocol = (request) => {
|
|
35
|
+
const origin = getHeaderValue(request.headers.origin);
|
|
36
|
+
if (origin && origin !== "null") {
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(origin);
|
|
39
|
+
if (url.host === request.headers.host && (url.protocol === "http:" || url.protocol === "https:")) {
|
|
40
|
+
return url.protocol.slice(0, -1);
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return request.protocol === "https" ? "https" : "http";
|
|
46
|
+
};
|
|
47
|
+
const requireAllowedOrigin = (request) => {
|
|
48
|
+
const origin = request.headers.origin;
|
|
49
|
+
if (!origin || origin === "null") return;
|
|
50
|
+
try {
|
|
51
|
+
if (new URL(origin).host === request.headers.host) return;
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
throw createAppError(403, "APP_ORIGIN_FORBIDDEN", "App requests must originate from Ocean Brain.");
|
|
55
|
+
};
|
|
56
|
+
const isCorsPreflight = (request) => request.method === "OPTIONS" && request.headers.origin === "null" && typeof request.headers["access-control-request-method"] === "string";
|
|
57
|
+
const sendCorsPreflight = (request, reply) => {
|
|
58
|
+
const requestedHeaders = request.headers["access-control-request-headers"];
|
|
59
|
+
return reply.status(204).headers({
|
|
60
|
+
"Access-Control-Allow-Credentials": "true",
|
|
61
|
+
"Access-Control-Allow-Headers": requestedHeaders ?? "Content-Type",
|
|
62
|
+
"Access-Control-Allow-Methods": SUPPORTED_METHODS,
|
|
63
|
+
"Access-Control-Allow-Origin": "null",
|
|
64
|
+
"Access-Control-Max-Age": "600",
|
|
65
|
+
Vary: "Origin, Access-Control-Request-Headers"
|
|
66
|
+
}).send();
|
|
67
|
+
};
|
|
68
|
+
const createAppGatewayRouter = (authConfig, gatewayOptions) => {
|
|
69
|
+
return async (app) => {
|
|
70
|
+
const accessRouteOptions = {
|
|
71
|
+
preHandler: [
|
|
72
|
+
app.rateLimit(createSessionAccessRateLimit()),
|
|
73
|
+
requireSessionForWrite(authConfig),
|
|
74
|
+
createCsrfProtection(authConfig)
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
const options = resolveAppGatewayOptions(gatewayOptions);
|
|
78
|
+
const access = createAppGatewayAccessService();
|
|
79
|
+
const wsClientOptions = {
|
|
80
|
+
headers: {},
|
|
81
|
+
rewriteRequestHeaders: (_headers, request) => createProxyWebSocketHeaders(request)
|
|
82
|
+
};
|
|
83
|
+
app.post(
|
|
84
|
+
"/api/app-gateway/connections/:connectionId/access",
|
|
85
|
+
accessRouteOptions,
|
|
86
|
+
async (request, reply) => {
|
|
87
|
+
requireAllowedOrigin(request);
|
|
88
|
+
const connection = await resolveAppGatewayConnection(options, request.params.connectionId);
|
|
89
|
+
const grant = access.issue(connection.id, getPublicProtocol(request));
|
|
90
|
+
return reply.header("Cache-Control", "no-store").setCookie(APP_GATEWAY_ACCESS_COOKIE_NAME, grant.token, {
|
|
91
|
+
httpOnly: true,
|
|
92
|
+
maxAge: APP_GATEWAY_ACCESS_TTL_MS / 1e3,
|
|
93
|
+
path: `${APP_GATEWAY_PUBLIC_PREFIX}/${connection.id}`,
|
|
94
|
+
// The sandbox has an opaque origin, so app subresources need an explicitly cross-site cookie.
|
|
95
|
+
sameSite: "none",
|
|
96
|
+
secure: true
|
|
97
|
+
}).send({
|
|
98
|
+
connectionId: connection.id,
|
|
99
|
+
...grant
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
);
|
|
103
|
+
app.removeContentTypeParser("application/x-www-form-urlencoded");
|
|
104
|
+
app.decorateRequest("appGatewayCorsAllowed", false);
|
|
105
|
+
app.decorateRequest("appGatewayConnection", null);
|
|
106
|
+
app.decorateRequest("appGatewayPublicProtocol", null);
|
|
107
|
+
app.register(fastifyHttpProxy, {
|
|
108
|
+
upstream: "",
|
|
109
|
+
prefix: PROXY_PREFIX,
|
|
110
|
+
rewritePrefix: "/",
|
|
111
|
+
websocket: true,
|
|
112
|
+
preHandler: async (request, reply) => {
|
|
113
|
+
validateAppGatewayRequestPath(request.url);
|
|
114
|
+
requireAllowedOrigin(request);
|
|
115
|
+
if (isCorsPreflight(request)) return sendCorsPreflight(request, reply);
|
|
116
|
+
const connectionId = getConnectionId(request);
|
|
117
|
+
const headerToken = getHeaderValue(request.headers[APP_GATEWAY_ACCESS_HEADER]);
|
|
118
|
+
const accessToken = isWebSocketUpgrade(request) ? extractAppGatewayAccessToken(request.headers["sec-websocket-protocol"]) : headerToken ?? extractAppGatewayAccessCookie(request.headers.cookie);
|
|
119
|
+
const accessGrant = access.resolve(connectionId, accessToken);
|
|
120
|
+
if (!accessGrant) {
|
|
121
|
+
const unauthorized = buildUnauthorizedPayload();
|
|
122
|
+
throw createAppError(401, unauthorized.code, unauthorized.message);
|
|
123
|
+
}
|
|
124
|
+
request.appGatewayPublicProtocol = accessGrant.publicProtocol;
|
|
125
|
+
request.appGatewayConnection = await resolveAppGatewayConnection(options, connectionId);
|
|
126
|
+
const requestUrl = new URL(request.url, "http://ocean-brain.invalid");
|
|
127
|
+
const publicRoot = `${APP_GATEWAY_PUBLIC_PREFIX}/${connectionId}`;
|
|
128
|
+
if (!isWebSocketUpgrade(request) && ["GET", "HEAD"].includes(request.method) && requestUrl.pathname === publicRoot) {
|
|
129
|
+
return reply.redirect(`${publicRoot}/${requestUrl.search}`, 308);
|
|
130
|
+
}
|
|
131
|
+
if (request.headers.origin === "null" && !["GET", "HEAD"].includes(request.method) && !headerToken && !isWebSocketUpgrade(request)) {
|
|
132
|
+
throw createAppError(
|
|
133
|
+
403,
|
|
134
|
+
"APP_ACCESS_HEADER_REQUIRED",
|
|
135
|
+
"Sandboxed app writes require the app access header."
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const fetchDestination = request.headers["sec-fetch-dest"];
|
|
139
|
+
request.appGatewayCorsAllowed = request.headers.origin === "null" && (Boolean(headerToken) || typeof fetchDestination === "string" && CORS_ASSET_DESTINATIONS.has(fetchDestination));
|
|
140
|
+
},
|
|
141
|
+
replyOptions: {
|
|
142
|
+
timeout: options.requestTimeoutMs,
|
|
143
|
+
getUpstream: (request) => getAppGatewayUpstream(request),
|
|
144
|
+
rewriteRequestHeaders: (request, headers) => createProxyRequestHeaders(request, headers),
|
|
145
|
+
rewriteHeaders: (headers, request) => request ? createGatewayResponseHeaders(headers, request) : headers,
|
|
146
|
+
onError: (reply, { error }) => {
|
|
147
|
+
const timeout = error.name === "TimeoutError" || error.message.toLowerCase().includes("timeout");
|
|
148
|
+
return reply.status(timeout ? 504 : 502).send({
|
|
149
|
+
code: timeout ? "APP_PROXY_TIMEOUT" : "APP_PROXY_UNAVAILABLE",
|
|
150
|
+
message: timeout ? "The proxied app did not respond in time." : "The proxied app could not be reached."
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
wsClientOptions,
|
|
155
|
+
wsServerOptions: {
|
|
156
|
+
handleProtocols: selectAppGatewayProtocol
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
export {
|
|
162
|
+
createAppGatewayRouter
|
|
163
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import mercurius from "mercurius";
|
|
2
|
+
import { integrationReadSchema } from "../features/integration/schema.js";
|
|
2
3
|
import { createCsrfProtection, isAuthenticatedRequest, requireSessionForGraphql } from "../modules/auth-guard.js";
|
|
3
4
|
import { createMcpAuthMiddleware, createReadOnlyMcpValidationRule } from "../modules/mcp-auth.js";
|
|
4
5
|
import schema from "../schema/index.js";
|
|
@@ -13,7 +14,7 @@ const createGraphqlRouter = (authConfig, mcpAdminService) => {
|
|
|
13
14
|
app.register(async (mcpEndpoint) => {
|
|
14
15
|
mcpEndpoint.addHook("preHandler", createMcpAuthMiddleware(authConfig, mcpAdminService));
|
|
15
16
|
mcpEndpoint.register(mercurius, {
|
|
16
|
-
schema,
|
|
17
|
+
schema: integrationReadSchema,
|
|
17
18
|
path: "/graphql/mcp",
|
|
18
19
|
graphiql: false,
|
|
19
20
|
errorFormatter: (execution, context) => ({
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createApiRouter } from "./api.js";
|
|
2
|
+
import { createAppGatewayRouter } from "./apps.js";
|
|
2
3
|
import { createAuthPagesRouter } from "./auth-pages.js";
|
|
3
4
|
import { createClientRouter } from "./client.js";
|
|
4
5
|
import { createGraphqlRouter } from "./graphql.js";
|
|
5
6
|
export {
|
|
6
7
|
createApiRouter,
|
|
8
|
+
createAppGatewayRouter,
|
|
7
9
|
createAuthPagesRouter,
|
|
8
10
|
createClientRouter,
|
|
9
11
|
createGraphqlRouter
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import mercurius from "mercurius";
|
|
2
|
+
import { requireIntegrationPermission } from "../features/integration/auth.js";
|
|
3
|
+
import {
|
|
4
|
+
createIntegrationNoteCatalogHandler,
|
|
5
|
+
createIntegrationNoteEventsHandler
|
|
6
|
+
} from "../features/integration/note-sync.js";
|
|
7
|
+
import { integrationReadSchema } from "../features/integration/schema.js";
|
|
8
|
+
import { createIntegrationService } from "../features/integration/service.js";
|
|
9
|
+
import {
|
|
10
|
+
createAppendNoteMarkdownHandler,
|
|
11
|
+
createCreateNoteHandler,
|
|
12
|
+
createDeleteNoteHandler,
|
|
13
|
+
createNoteWriteBaselineHandler,
|
|
14
|
+
createPatchNoteMarkdownHandler,
|
|
15
|
+
createReplaceNoteMarkdownHandler,
|
|
16
|
+
createUpdateNoteMetadataHandler
|
|
17
|
+
} from "../features/note/http/authoring.js";
|
|
18
|
+
import { createCsrfProtection, requireSessionForWrite } from "../modules/auth-guard.js";
|
|
19
|
+
import { createAppError } from "../modules/error-handler.js";
|
|
20
|
+
import { createReadOnlyMcpValidationRule } from "../modules/mcp-auth.js";
|
|
21
|
+
import { createSessionAccessRateLimit } from "../modules/rate-limit.js";
|
|
22
|
+
const createIntegrationRouter = (authConfig) => async (app) => {
|
|
23
|
+
const service = createIntegrationService();
|
|
24
|
+
app.register(
|
|
25
|
+
async (api) => {
|
|
26
|
+
api.register(async (read) => {
|
|
27
|
+
read.addHook("preHandler", requireIntegrationPermission("notes:read", service));
|
|
28
|
+
read.register(mercurius, {
|
|
29
|
+
schema: integrationReadSchema,
|
|
30
|
+
path: "/graphql",
|
|
31
|
+
graphiql: false,
|
|
32
|
+
queryDepth: 16,
|
|
33
|
+
validationRules: [createReadOnlyMcpValidationRule("Integration GraphQL endpoint is read-only")],
|
|
34
|
+
context: (req) => ({ integration: req.integration })
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
api.get(
|
|
38
|
+
"/me",
|
|
39
|
+
{ preHandler: requireIntegrationPermission("notes:read", service) },
|
|
40
|
+
async (request) => ({
|
|
41
|
+
apiVersion: 1,
|
|
42
|
+
...request.integration
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
api.post(
|
|
46
|
+
"/status",
|
|
47
|
+
{ bodyLimit: 2048, preHandler: requireIntegrationPermission("notes:read", service) },
|
|
48
|
+
async (request) => service.reportStatus(request.headers.authorization?.slice(7).trim() ?? "", request.body)
|
|
49
|
+
);
|
|
50
|
+
api.get(
|
|
51
|
+
"/events",
|
|
52
|
+
{ preHandler: requireIntegrationPermission("notes:read", service) },
|
|
53
|
+
createIntegrationNoteEventsHandler()
|
|
54
|
+
);
|
|
55
|
+
api.post(
|
|
56
|
+
"/notes/catalog",
|
|
57
|
+
{ preHandler: requireIntegrationPermission("notes:read", service) },
|
|
58
|
+
createIntegrationNoteCatalogHandler()
|
|
59
|
+
);
|
|
60
|
+
api.post(
|
|
61
|
+
"/notes/create",
|
|
62
|
+
{ preHandler: requireIntegrationPermission("notes:create", service) },
|
|
63
|
+
createCreateNoteHandler()
|
|
64
|
+
);
|
|
65
|
+
api.post(
|
|
66
|
+
"/notes/baseline",
|
|
67
|
+
{ preHandler: requireIntegrationPermission("notes:read", service) },
|
|
68
|
+
createNoteWriteBaselineHandler()
|
|
69
|
+
);
|
|
70
|
+
api.post(
|
|
71
|
+
"/notes/patch-markdown",
|
|
72
|
+
{ preHandler: requireIntegrationPermission("notes:update", service) },
|
|
73
|
+
createPatchNoteMarkdownHandler()
|
|
74
|
+
);
|
|
75
|
+
api.post(
|
|
76
|
+
"/notes/append-markdown",
|
|
77
|
+
{ preHandler: requireIntegrationPermission("notes:update", service) },
|
|
78
|
+
createAppendNoteMarkdownHandler()
|
|
79
|
+
);
|
|
80
|
+
api.post(
|
|
81
|
+
"/notes/replace-markdown",
|
|
82
|
+
{ preHandler: requireIntegrationPermission("notes:update", service) },
|
|
83
|
+
createReplaceNoteMarkdownHandler()
|
|
84
|
+
);
|
|
85
|
+
api.post(
|
|
86
|
+
"/notes/metadata",
|
|
87
|
+
{ preHandler: requireIntegrationPermission("notes:update", service) },
|
|
88
|
+
createUpdateNoteMetadataHandler()
|
|
89
|
+
);
|
|
90
|
+
api.post(
|
|
91
|
+
"/notes/delete",
|
|
92
|
+
{ preHandler: requireIntegrationPermission("notes:delete", service) },
|
|
93
|
+
createDeleteNoteHandler()
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
{ prefix: "/integrations/v1" }
|
|
97
|
+
);
|
|
98
|
+
app.register(
|
|
99
|
+
async (admin) => {
|
|
100
|
+
admin.addHook("preHandler", app.rateLimit(createSessionAccessRateLimit()));
|
|
101
|
+
admin.addHook("preHandler", requireSessionForWrite(authConfig));
|
|
102
|
+
admin.addHook("preHandler", createCsrfProtection(authConfig));
|
|
103
|
+
admin.get("/connections", async () => ({ connections: await service.list() }));
|
|
104
|
+
admin.post("/connections", { bodyLimit: 64 * 1024 }, async (request, reply) => {
|
|
105
|
+
if (!request.body)
|
|
106
|
+
throw createAppError(400, "INVALID_INTEGRATION_MANIFEST", "A manifest is required.");
|
|
107
|
+
const connection = await service.connect({
|
|
108
|
+
manifest: request.body.manifest,
|
|
109
|
+
grantedPermissions: request.body.grantedPermissions,
|
|
110
|
+
proxyUrl: request.body.proxyUrl
|
|
111
|
+
});
|
|
112
|
+
return reply.status(201).send(connection);
|
|
113
|
+
});
|
|
114
|
+
admin.patch(
|
|
115
|
+
"/connections/:id",
|
|
116
|
+
{ bodyLimit: 64 * 1024 },
|
|
117
|
+
async (request) => service.update(request.params.id, request.body ?? {})
|
|
118
|
+
);
|
|
119
|
+
admin.delete("/connections/:id", async (request) => {
|
|
120
|
+
await service.disconnect(request.params.id);
|
|
121
|
+
return { disconnected: true };
|
|
122
|
+
});
|
|
123
|
+
admin.post(
|
|
124
|
+
"/connections/:id/token/rotate",
|
|
125
|
+
async (request) => service.rotateToken(request.params.id)
|
|
126
|
+
);
|
|
127
|
+
admin.post("/connections/:id/token/revoke", async (request) => {
|
|
128
|
+
await service.revokeToken(request.params.id);
|
|
129
|
+
return { revoked: true };
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
{ prefix: "/integration-admin" }
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
export {
|
|
136
|
+
createIntegrationRouter
|
|
137
|
+
};
|
|
@@ -11,27 +11,47 @@ import { createMcpCreateTagHandler } from "../features/tag/http/mcp.js";
|
|
|
11
11
|
import { createMcpAuthMiddleware } from "../modules/mcp-auth.js";
|
|
12
12
|
const createMcpRouter = (authConfig, mcpAdminService) => {
|
|
13
13
|
return async (app) => {
|
|
14
|
-
const requireMcpAuth = createMcpAuthMiddleware(authConfig, mcpAdminService);
|
|
15
|
-
app.post(
|
|
16
|
-
|
|
14
|
+
const requireMcpAuth = (permission) => createMcpAuthMiddleware(authConfig, mcpAdminService, permission);
|
|
15
|
+
app.post(
|
|
16
|
+
"/notes/create",
|
|
17
|
+
{ preHandler: requireMcpAuth("notes:create") },
|
|
18
|
+
createMcpCreateNoteHandler()
|
|
19
|
+
);
|
|
20
|
+
app.post(
|
|
21
|
+
"/notes/baseline",
|
|
22
|
+
{ preHandler: requireMcpAuth("notes:read") },
|
|
23
|
+
createMcpNoteWriteBaselineHandler()
|
|
24
|
+
);
|
|
17
25
|
app.post(
|
|
18
26
|
"/notes/patch-markdown",
|
|
19
|
-
{ preHandler: requireMcpAuth },
|
|
27
|
+
{ preHandler: requireMcpAuth("notes:update") },
|
|
20
28
|
createMcpPatchNoteMarkdownHandler()
|
|
21
29
|
);
|
|
22
30
|
app.post(
|
|
23
31
|
"/notes/append-markdown",
|
|
24
|
-
{ preHandler: requireMcpAuth },
|
|
32
|
+
{ preHandler: requireMcpAuth("notes:update") },
|
|
25
33
|
createMcpAppendNoteMarkdownHandler()
|
|
26
34
|
);
|
|
27
|
-
app.post(
|
|
35
|
+
app.post(
|
|
36
|
+
"/notes/metadata",
|
|
37
|
+
{ preHandler: requireMcpAuth("notes:update") },
|
|
38
|
+
createMcpUpdateNoteMetadataHandler()
|
|
39
|
+
);
|
|
28
40
|
app.post(
|
|
29
41
|
"/notes/replace-markdown",
|
|
30
|
-
{ preHandler: requireMcpAuth },
|
|
42
|
+
{ preHandler: requireMcpAuth("notes:update") },
|
|
31
43
|
createMcpReplaceNoteMarkdownHandler()
|
|
32
44
|
);
|
|
33
|
-
app.post(
|
|
34
|
-
|
|
45
|
+
app.post(
|
|
46
|
+
"/notes/delete",
|
|
47
|
+
{ preHandler: requireMcpAuth("notes:delete") },
|
|
48
|
+
createMcpDeleteNoteHandler()
|
|
49
|
+
);
|
|
50
|
+
app.post(
|
|
51
|
+
"/tags/create",
|
|
52
|
+
{ preHandler: requireMcpAuth("notes:update") },
|
|
53
|
+
createMcpCreateTagHandler()
|
|
54
|
+
);
|
|
35
55
|
};
|
|
36
56
|
};
|
|
37
57
|
export {
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
CREATE TABLE "PluginInstallation" (
|
|
2
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
3
|
+
"pluginId" TEXT NOT NULL,
|
|
4
|
+
"native" BOOLEAN NOT NULL DEFAULT false,
|
|
5
|
+
"manifest" TEXT NOT NULL,
|
|
6
|
+
"grantedPermissions" TEXT NOT NULL DEFAULT '[]',
|
|
7
|
+
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
|
8
|
+
"pinned" BOOLEAN NOT NULL DEFAULT false,
|
|
9
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
10
|
+
"updatedAt" DATETIME NOT NULL
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
CREATE INDEX "PluginInstallation_pluginId_idx" ON "PluginInstallation"("pluginId");
|
|
14
|
+
|
|
15
|
+
CREATE TABLE "PluginCredential" (
|
|
16
|
+
"installationId" TEXT NOT NULL PRIMARY KEY,
|
|
17
|
+
"id" TEXT NOT NULL,
|
|
18
|
+
"tokenHash" TEXT NOT NULL,
|
|
19
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
20
|
+
"lastUsedAt" DATETIME,
|
|
21
|
+
CONSTRAINT "PluginCredential_installationId_fkey" FOREIGN KEY ("installationId") REFERENCES "PluginInstallation" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE UNIQUE INDEX "PluginCredential_id_key" ON "PluginCredential"("id");
|
|
25
|
+
CREATE UNIQUE INDEX "PluginCredential_tokenHash_key" ON "PluginCredential"("tokenHash");
|
|
26
|
+
|
|
27
|
+
-- Keep legacy tables intact. Only the latest active token was accepted by MCP.
|
|
28
|
+
INSERT INTO "PluginInstallation" ("id", "pluginId", "native", "manifest", "grantedPermissions", "enabled", "updatedAt")
|
|
29
|
+
VALUES (
|
|
30
|
+
'mcp', 'ocean-brain.mcp', true,
|
|
31
|
+
'{"schemaVersion":1,"apiVersion":1,"id":"ocean-brain.mcp","name":"MCP","version":"1.0.0","description":"Connect AI clients to Ocean Brain.","permissions":["notes:read","notes:create","notes:update","notes:delete"]}',
|
|
32
|
+
'["notes:read","notes:create","notes:update","notes:delete"]',
|
|
33
|
+
COALESCE((SELECT "value" = 'true' FROM "Cache" WHERE "key" = 'MCP_ENABLED'), false),
|
|
34
|
+
CURRENT_TIMESTAMP
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
INSERT INTO "PluginCredential" ("installationId", "id", "tokenHash", "createdAt", "lastUsedAt")
|
|
38
|
+
SELECT 'mcp', CAST("id" AS TEXT), "tokenHash", "createdAt", "lastUsedAt"
|
|
39
|
+
FROM "McpToken" WHERE "revokedAt" IS NULL ORDER BY "createdAt" DESC, "id" DESC LIMIT 1;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
-- Rename the existing platform in place: preserve connection IDs, grants,
|
|
2
|
+
-- credential hashes and timestamps, including MCP credentials migrated by 0020.
|
|
3
|
+
BEGIN IMMEDIATE;
|
|
4
|
+
|
|
5
|
+
ALTER TABLE "PluginInstallation" RENAME TO "IntegrationConnection";
|
|
6
|
+
ALTER TABLE "IntegrationConnection" RENAME COLUMN "pluginId" TO "integrationId";
|
|
7
|
+
|
|
8
|
+
-- SQLite cannot rename a foreign-key constraint; copy the credential rows
|
|
9
|
+
-- unchanged so the resulting table also matches Prisma's canonical schema.
|
|
10
|
+
CREATE TABLE "IntegrationCredential" (
|
|
11
|
+
"connectionId" TEXT NOT NULL PRIMARY KEY,
|
|
12
|
+
"id" TEXT NOT NULL,
|
|
13
|
+
"tokenHash" TEXT NOT NULL,
|
|
14
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
15
|
+
"lastUsedAt" DATETIME,
|
|
16
|
+
CONSTRAINT "IntegrationCredential_connectionId_fkey" FOREIGN KEY ("connectionId") REFERENCES "IntegrationConnection" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
INSERT INTO "IntegrationCredential" ("connectionId", "id", "tokenHash", "createdAt", "lastUsedAt")
|
|
20
|
+
SELECT "installationId", "id", "tokenHash", "createdAt", "lastUsedAt" FROM "PluginCredential";
|
|
21
|
+
DROP TABLE "PluginCredential";
|
|
22
|
+
|
|
23
|
+
DROP INDEX "PluginInstallation_pluginId_idx";
|
|
24
|
+
CREATE INDEX "IntegrationConnection_integrationId_idx" ON "IntegrationConnection"("integrationId");
|
|
25
|
+
|
|
26
|
+
CREATE UNIQUE INDEX "IntegrationCredential_id_key" ON "IntegrationCredential"("id");
|
|
27
|
+
CREATE UNIQUE INDEX "IntegrationCredential_tokenHash_key" ON "IntegrationCredential"("tokenHash");
|
|
28
|
+
|
|
29
|
+
COMMIT;
|
|
@@ -25,19 +25,45 @@ model McpToken {
|
|
|
25
25
|
@@index([revokedAt, createdAt])
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
model IntegrationConnection {
|
|
29
|
+
id String @id @default(cuid())
|
|
30
|
+
integrationId String
|
|
31
|
+
native Boolean @default(false)
|
|
32
|
+
manifest String
|
|
33
|
+
proxyUrl String?
|
|
34
|
+
grantedPermissions String @default("[]")
|
|
35
|
+
enabled Boolean @default(false)
|
|
36
|
+
pinned Boolean @default(false)
|
|
37
|
+
createdAt DateTime @default(now())
|
|
38
|
+
updatedAt DateTime @updatedAt
|
|
39
|
+
credential IntegrationCredential?
|
|
40
|
+
|
|
41
|
+
@@index([integrationId])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
model IntegrationCredential {
|
|
45
|
+
connectionId String @id
|
|
46
|
+
connection IntegrationConnection @relation(fields: [connectionId], references: [id], onDelete: Cascade)
|
|
47
|
+
id String @unique @default(cuid())
|
|
48
|
+
tokenHash String @unique
|
|
49
|
+
createdAt DateTime @default(now())
|
|
50
|
+
lastUsedAt DateTime?
|
|
51
|
+
statusReport String?
|
|
52
|
+
}
|
|
53
|
+
|
|
28
54
|
model Note {
|
|
29
|
-
id Int
|
|
55
|
+
id Int @id @default(autoincrement())
|
|
30
56
|
title String
|
|
31
57
|
content String
|
|
32
|
-
searchableText String
|
|
33
|
-
searchableTextVersion Int
|
|
34
|
-
createdAt DateTime
|
|
35
|
-
updatedAt DateTime
|
|
36
|
-
pinned Boolean
|
|
37
|
-
order Int
|
|
38
|
-
layout NoteLayout
|
|
39
|
-
tags Tag[]
|
|
40
|
-
reminders Reminder[]
|
|
58
|
+
searchableText String @default("")
|
|
59
|
+
searchableTextVersion Int @default(0)
|
|
60
|
+
createdAt DateTime @default(now())
|
|
61
|
+
updatedAt DateTime @updatedAt
|
|
62
|
+
pinned Boolean @default(false)
|
|
63
|
+
order Int @default(0)
|
|
64
|
+
layout NoteLayout @default(wide)
|
|
65
|
+
tags Tag[] @relation("NoteToTag")
|
|
66
|
+
reminders Reminder[] @relation("NoteToReminder")
|
|
41
67
|
snapshots NoteSnapshot[]
|
|
42
68
|
properties NoteProperty[]
|
|
43
69
|
outgoingReferences NoteReference[] @relation("NoteReferenceSource")
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import{j as e,r as j}from"./floating-ui.react-dom-BWTnA00G.js";import{T as p,x as H,y as E,M as C,q as S,g as Y,P as Q,h as V}from"./time-PxWila4-.js";import{d as b,s as F,a as $}from"./useNoteMutate-BjvZUmtX.js";import{C as U}from"./url-DKUMNVCI.js";import{C as z,a as P,b as B,c as J,g as G,s as W,d as X}from"./calendar-data-wB0ifXdo.js";import{d as K}from"./app-NgiBDISG.js";import{s as Z}from"./FileText.es-DPIwDCEe.js";import{C as ee}from"./Callout-CcYyvHQP.js";import"./preload-helper-BXl3LOEh.js";import"./Graph-hVj6vAYJ.js";import"./Info.es-CYxg-Vku.js";const D=2,te=({year:t,month:a,day:n,isCurrentMonth:r,isSunday:l,isToday:m,isSelected:d,isPast:c,notes:h,reminders:g,onSelect:s})=>{const f=h.slice(0,D).map(y=>({key:`note-${y.id}`,type:"note",title:y.title})),N=g.slice(0,D).map(y=>({key:`reminder-${y.id}`,type:"reminder",title:y.content||y.note?.title||"No title",isCompleted:y.completed})),M=(c?[...f,...N]:[...N,...f]).slice(0,D),R=()=>r?d?"bg-elevated shadow-[inset_0_0_0_1px_var(--border-secondary)] hover:bg-surface":"bg-surface hover:bg-muted":"cursor-default bg-[var(--page-bg)] text-fg-disabled",k=()=>m?"bg-cta font-semibold text-fg-on-filled":r?l?"font-semibold text-fg-weekend":"font-semibold text-fg-secondary":"font-medium text-fg-tertiary";return e.jsx(z,{day:n,dateLabel:b(new Date(t,a-1,n)).format("dddd, MMMM D, YYYY"),cellClassName:R(),dayNumberClassName:k(),isCurrentMonth:r,isToday:m,isSelected:d,noteCount:h.length,reminderCount:g.length,previewItems:M,overflowCount:Math.max(0,h.length+g.length-D),onSelect:s})},ae=te,ne=({note:t,type:a})=>e.jsx(P,{params:{id:t.id},header:e.jsx(Z,{size:12}),title:t.title,meta:b(Number(a==="create"?t.createdAt:t.updatedAt)).format("HH:mm")}),se=({reminder:t})=>e.jsx(P,{params:{id:String(t.note?.id??t.noteId)},header:e.jsx(F,{size:12}),title:t.content||t.note?.title||"No title",titleClassName:t.completed?"line-through":"",meta:b(Number(t.reminderDate)).format("HH:mm")}),re=(t,a)=>t.map(n=>n.type==="note"?e.jsx(ne,{note:n.item,type:a},`note-${n.item.id}`):e.jsx(se,{reminder:n.item},`reminder-${n.item.id}`)),I=(t,a)=>`${t} ${t===1?a:`${a}s`}`,oe="mt-3 flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto overscroll-y-contain pr-2 [scrollbar-color:var(--border-secondary)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-border-secondary [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar]:w-1.5",L=t=>b(new Date(t.year,t.month-1,t.day)).format("dddd, MMMM D"),T=({day:t,type:a,isLoading:n,presentation:r="rail"})=>{const l=j.useMemo(()=>{if(!t)return[];const h=t.notes.map(s=>({type:"note",item:s})),g=t.reminders.map(s=>({type:"reminder",item:s}));return t.isPast?[...h,...g]:[...g,...h]},[t]);if(!t)return e.jsx("section",{"aria-label":"Day details",className:"flex h-full min-h-40 items-center justify-center text-center",children:e.jsx(p,{as:"p",variant:"meta",weight:"medium",tone:"secondary",children:"Select a day to see its activity."})});const m=L(t),d=n?"Loading activity...":[I(t.notes.length,"note"),I(t.reminders.length,"reminder")].join(" · "),c=n?null:e.jsx("div",{className:r==="rail"?oe:"flex flex-col gap-1",children:l.length>0?re(l,a):e.jsx(p,{as:"p",variant:"meta",weight:"medium",tone:"secondary",className:"py-8 text-center",children:"No activity on this day."})});return r==="modal"?e.jsxs("div",{"aria-label":`${m} activity`,children:[e.jsx(p,{as:"p",variant:"label",weight:"medium",tone:"secondary",className:"mb-3",children:d}),c]}):e.jsxs("section",{"aria-label":`${m} details`,className:"flex h-full min-h-0 flex-col",children:[e.jsxs("div",{className:"flex shrink-0 items-start gap-2 border-b border-border-subtle/80 pb-3",children:[e.jsx(K,{"aria-hidden":"true",className:"mt-0.5 shrink-0 text-fg-tertiary",size:15}),e.jsxs("div",{className:"min-w-0",children:[e.jsx(p,{as:"h2",variant:"subheading",weight:"semibold",tracking:"tight",children:m}),e.jsx(p,{as:"p",variant:"label",weight:"medium",tone:"secondary",className:"mt-0.5",children:d})]})]}),c]})},ie=["January","February","March","April","May","June","July","August","September","October","November","December"],le=({month:t,year:a,type:n,onPrevMonth:r,onNextMonth:l,onToday:m,onTypeChange:d})=>e.jsxs("div",{className:"flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between",children:[e.jsxs("div",{className:"space-y-1.5",children:[e.jsxs("div",{className:"flex flex-wrap items-end gap-x-2.5 gap-y-1",children:[e.jsx(p,{as:"h1",variant:"display",weight:"bold",tracking:"tighter",className:"text-2xl leading-none sm:text-[2.15rem]",children:ie[t-1]}),e.jsx(p,{as:"span",variant:"subheading",weight:"medium",tone:"secondary",tracking:"tight",className:"pb-0.5",children:a})]}),e.jsx(p,{as:"p",variant:"meta",weight:"medium",tone:"secondary",className:"hidden sm:block",children:"Track note activity and reminders across the month"})]}),e.jsx("div",{className:"flex lg:justify-end",children:e.jsxs("div",{className:"flex w-full flex-col gap-2 sm:inline-flex sm:w-auto sm:flex-row sm:items-center sm:gap-3",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx(K,{className:"h-4 w-4 shrink-0 text-fg-tertiary"}),e.jsx(p,{id:"calendar-note-date-label",as:"span",variant:"label",weight:"medium",tone:"secondary",children:"Note date"}),e.jsxs(H,{value:n,"aria-labelledby":"calendar-note-date-label",onValueChange:h=>d(h),variant:"ghost",size:"sm",children:[e.jsx(E,{value:"create",children:"Created"}),e.jsx(E,{value:"update",children:"Updated"})]})]}),e.jsx("div",{className:"hidden h-5 w-px bg-divider sm:block"}),e.jsx(B,{onPrevMonth:r,onNextMonth:l,onToday:m})]})})]}),_="(min-width: 1280px)",de=({days:t,type:a,isLoading:n,selectedDayKey:r,onSelectedDayChange:l})=>{const[m,d]=j.useState(!1),c=j.useMemo(()=>t.find(s=>s.key===r),[t,r]);j.useEffect(()=>{const s=window.matchMedia(_),f=N=>{N.matches&&d(!1)};return s.addEventListener("change",f),()=>s.removeEventListener("change",f)},[]);const h=s=>{l(s),window.matchMedia(_).matches||d(!0)},g=c?L(c):"Day details";return e.jsxs(e.Fragment,{children:[e.jsxs("div",{className:"min-w-0 xl:grid xl:grid-cols-[minmax(0,1fr)_20rem] 2xl:grid-cols-[minmax(0,1fr)_22rem]",children:[e.jsx(J,{isLoading:n,className:"xl:pr-5",children:t.map(s=>e.jsx(ae,{year:s.year,month:s.month,day:s.day,isCurrentMonth:s.isCurrentMonth,isSunday:s.isSunday,isToday:s.isToday,isSelected:s.key===r,isPast:s.isPast,notes:s.notes,reminders:s.reminders,onSelect:()=>h(s.key)},s.key))}),e.jsx("aside",{className:"relative hidden min-h-0 border-l border-border-subtle xl:block","aria-label":"Selected day",children:e.jsx("div",{className:"absolute inset-y-0 right-0 left-5",children:e.jsx(T,{day:c,type:a,isLoading:n})})})]}),e.jsxs(C,{isOpen:m&&!!c,onClose:()=>d(!1),variant:"inspect",className:"xl:hidden",children:[e.jsx(C.Header,{title:g,onClose:()=>d(!1)}),e.jsxs(C.Body,{children:[e.jsxs(C.Description,{className:"sr-only",children:["View notes and reminders for ",g,"."]}),e.jsx(T,{day:c,type:a,isLoading:n,presentation:"modal"})]})]})]})},ce=`
|
|
2
|
-
query NotesInDateRange($dateRange: DateRangeInput) {
|
|
3
|
-
notesInDateRange(dateRange: $dateRange) {
|
|
4
|
-
id
|
|
5
|
-
title
|
|
6
|
-
createdAt
|
|
7
|
-
updatedAt
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
`,me=`
|
|
11
|
-
query RemindersInDateRange($dateRange: DateRangeInput) {
|
|
12
|
-
remindersInDateRange(dateRange: $dateRange) {
|
|
13
|
-
id
|
|
14
|
-
noteId
|
|
15
|
-
reminderDate
|
|
16
|
-
completed
|
|
17
|
-
priority
|
|
18
|
-
content
|
|
19
|
-
note {
|
|
20
|
-
id
|
|
21
|
-
title
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
`,he=async t=>{const a=await Y(ce,{dateRange:t});if(a.type==="error")throw a;return a.notesInDateRange},ue=async t=>{const a=await Y(me,{dateRange:t});if(a.type==="error")throw a;return a.remindersInDateRange},ge=({year:t,month:a})=>{const n=G(t,a),r=$({queryKey:S.calendar.notesInDateRange(t,a),queryFn:()=>he(n)}),l=$({queryKey:S.reminders.inDateRange(t,a),queryFn:()=>ue(n)});return{notes:r.data??[],reminders:l.data??[],isLoading:r.isLoading||l.isLoading,isError:r.isError||l.isError}},xe=[],ye=[],A=V(U);function ke(){const t=A.useNavigate(),{year:a,month:n,type:r}=A.useSearch(),l=`${a}-${n}`,[m,d]=j.useState(()=>({monthKey:l})),{notes:c,reminders:h,isLoading:g,isError:s}=ge({year:a,month:n}),f=j.useMemo(()=>{const o=new Map;for(const i of c){const x=r==="create"?b(Number(i.createdAt)):b(Number(i.updatedAt)),v=`${x.year()}-${x.month()+1}-${x.date()}`,w=o.get(v)||[];w.push(i),o.set(v,w)}for(const[i,x]of o)o.set(i,W(x,r));const u=new Map;for(const i of h){const x=b(Number(i.reminderDate)),v=`${x.year()}-${x.month()+1}-${x.date()}`,w=u.get(v)||[];w.push(i),u.set(v,w)}return X(a,n).map(i=>({...i,notes:o.get(i.key)||xe,reminders:u.get(i.key)||ye}))},[a,n,c,h,r]),N=f.find(o=>o.isCurrentMonth&&o.isToday)?.key,M=m.monthKey===l?m.dayKey??N:N,R=()=>{const o=n===1?12:n-1,u=n===1?a-1:a;t({search:i=>({...i,month:o,year:u})})},k=()=>{const o=n===12?1:n+1,u=n===12?a+1:a;t({search:i=>({...i,month:o,year:u})})},y=()=>{const o=b(),u=o.month()+1,i=o.year();d({monthKey:`${i}-${u}`,dayKey:`${i}-${u}-${o.date()}`}),t({search:x=>({...x,month:u,year:i})})},O=o=>{o&&t({search:u=>({...u,type:o})})},q=o=>{d({monthKey:l,dayKey:o})};return e.jsx(Q,{title:"Calendar",variant:"none",children:s?e.jsx(ee,{children:"Failed to load calendar data. Please try again later."}):e.jsxs("div",{className:"sm:-mr-4",children:[e.jsx("div",{className:"border-b border-border-subtle/80 pb-4",children:e.jsx(le,{month:n,year:a,type:r,onPrevMonth:R,onNextMonth:k,onToday:y,onTypeChange:O})}),e.jsx("div",{className:"pt-4",children:e.jsx(de,{days:f,type:r,isLoading:g,selectedDayKey:M,onSelectedDayChange:q})})]})})}export{ke as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{r as e,j as n}from"./floating-ui.react-dom-BWTnA00G.js";import{A as d,T as Z}from"./time-PxWila4-.js";import{c as i}from"./Info.es-CYxg-Vku.js";const p=new Map([["bold",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z"}))],["duotone",e.createElement(e.Fragment,null,e.createElement("path",{d:"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z",opacity:"0.2"}),e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"}))],["fill",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z"}))],["light",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm0,192a90,90,0,1,1,90-90A90.1,90.1,0,0,1,128,218Zm-6-82V80a6,6,0,0,1,12,0v56a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,172Z"}))],["regular",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"}))],["thin",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm0,192a92,92,0,1,1,92-92A92.1,92.1,0,0,1,128,220Zm-4-84V80a4,4,0,0,1,8,0v56a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,172Z"}))]]),m=e.forwardRef((t,r)=>e.createElement(d,{ref:r,...t,weights:p}));m.displayName="WarningCircleIcon";const g=m,u=({children:t,className:r="",tone:a="info"})=>{const l=a==="danger"?g:i,o=a==="danger"?"border-border-error/70 bg-accent-soft-danger/60":"border-border-subtle bg-hover-subtle/50",s=a==="danger"?"text-fg-error":"text-fg-tertiary",c=a==="danger"?"error":"secondary";return n.jsxs("div",{className:`flex items-center gap-2.5 rounded-[12px] border px-4 py-3 ${o} ${r}`,children:[n.jsx(l,{className:`h-4 w-4 shrink-0 ${s}`}),n.jsx(Z,{as:"div",variant:"meta",weight:"medium",tone:c,children:t})]})};export{u as C,g as m};
|