ocean-brain 0.14.0 → 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/package.json +2 -1
- package/server/client/dist/assets/ArrowClockwise.es-BwtKWcmj.js +1 -0
- package/server/client/dist/assets/{ArrowRight.es-nBJws3ws.js → ArrowRight.es-E09Lfefr.js} +1 -1
- package/server/client/dist/assets/{AuxiliaryPanelHeader-BN_2axQu.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-DDbqKn78.js → CaretUp.es-DSWjIa-J.js} +1 -1
- package/server/client/dist/assets/{CheckCircle.es-CyY9aLBF.js → CheckCircle.es-Cou6rItm.js} +1 -1
- package/server/client/dist/assets/{Copy.es-BwX4tEoU.js → Copy.es-ftEldGIL.js} +1 -1
- package/server/client/dist/assets/{File.es-DeTgvUnI.js → File.es-DlKAX1uO.js} +1 -1
- package/server/client/dist/assets/{FileText.es-C-bpw8dQ.js → FileText.es-D6JM_MqM.js} +1 -1
- package/server/client/dist/assets/{FlowArrow.es-CKcMBOmX.js → FlowArrow.es-BRWXqf9A.js} +1 -1
- package/server/client/dist/assets/{Graph-BNoSRJP3.js → Graph-BaqGi3M7.js} +1 -1
- package/server/client/dist/assets/{Graph-DYYxNAWn.js → Graph-L-d0cdMk.js} +1 -1
- package/server/client/dist/assets/{Image.es-d4jWnijM.js → Image.es-Bh2mTNtr.js} +1 -1
- package/server/client/dist/assets/{Info.es-DVzN2joA.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-BD4aGTc3.js → LinkSimple.es-iJvLf-MB.js} +1 -1
- package/server/client/dist/assets/{Note-FL31jb2U.js → Note-OO_uh2a7.js} +4 -4
- package/server/client/dist/assets/{NotePropertiesPanel-os22Ppnu.js → NotePropertiesPanel-BFWjxL_w.js} +1 -1
- package/server/client/dist/assets/{Plus.es-Cl8zOOZa.js → Plus.es-Dmb3oijD.js} +1 -1
- package/server/client/dist/assets/Reminders-CtpFtvu6.js +1 -0
- package/server/client/dist/assets/{Search-CPPTW2PO.js → Search-t48pkUg-.js} +2 -2
- package/server/client/dist/assets/{SurfaceCard-jL2qzrzF.js → SurfaceCard-zdJ8k1o0.js} +1 -1
- package/server/client/dist/assets/{Tag-3DbGmuxa.js → Tag-BL2h3ZaX.js} +1 -1
- package/server/client/dist/assets/TagNotes-BpRTCEBf.js +1 -0
- package/server/client/dist/assets/{Trash.es-fxukjAO6.js → Trash.es-DFnO8iF5.js} +1 -1
- package/server/client/dist/assets/{ViewChip-CVe_HNyd.js → ViewChip-BdPhSEvG.js} +1 -1
- package/server/client/dist/assets/Views-BoO6vgnF.js +1 -0
- package/server/client/dist/assets/WarningCircle.es-BAvgGuQK.js +1 -0
- 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/{calendar-data-BQ24n348.js → calendar-data-CwVF7Pz3.js} +1 -1
- package/server/client/dist/assets/{image.api-CyIIEGkG.js → image.api-CHuhmq_G.js} +1 -1
- package/server/client/dist/assets/index-CuCzOHJ6.js +1 -0
- package/server/client/dist/assets/integrations-B5SIEnG_.js +1 -0
- package/server/client/dist/assets/{manage-image-DXedfr7U.js → manage-image-Kd-7IGfB.js} +1 -1
- package/server/client/dist/assets/{manage-image-detail-DmifOPNU.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-CmKsg1vd.js → mcp-admin-adapter-CUakQ_Xm.js} +1 -1
- package/server/client/dist/assets/{note-ui-DRnAnwKv.js → note-ui-CRcP5BjO.js} +1 -1
- package/server/client/dist/assets/{placeholder-G6iEBbDy.js → placeholder-D7JKnpuh.js} +1 -1
- package/server/client/dist/assets/{properties-BPobx_Qk.js → properties-CtagYHMr.js} +1 -1
- package/server/client/dist/assets/route-preload.js +2 -2
- package/server/client/dist/assets/search-B5tCkhwj.js +1 -0
- package/server/client/dist/assets/{search-admin-adapter-7sTcWTmv.js → search-admin-adapter-Be9xSY88.js} +1 -1
- package/server/client/dist/assets/{tag.api-kKZl04yY.js → tag.api-DGfi8hWL.js} +1 -1
- package/server/client/dist/assets/{time-BwwQGbK-.js → time-Kh6oav5R.js} +9 -9
- package/server/client/dist/assets/{trash-Cepvi7G8.js → trash-Di2BWyUO.js} +1 -1
- package/server/client/dist/assets/{useNoteMutate-D-mZKXjl.js → useNoteMutate-BQj6jHhV.js} +1 -1
- package/server/client/dist/assets/{useReminderMutate-Cexk1ydX.js → useReminderMutate-jubqwSD9.js} +1 -1
- package/server/client/dist/index.html +5 -5
- package/server/dist/app.js +9 -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/manifest.js +31 -2
- package/server/dist/features/integration/note-sync.js +156 -0
- package/server/dist/features/integration/service.js +80 -7
- package/server/dist/features/integration/status-report.js +22 -0
- package/server/dist/features/note/graphql/note.mutation.resolver.js +20 -12
- package/server/dist/features/note/graphql/note.query.resolver.js +4 -1
- package/server/dist/features/note/services/change-events.js +26 -0
- package/server/dist/features/search/search-manager.js +4 -7
- package/server/dist/modules/server-events.js +3 -0
- package/server/dist/routes/apps.js +163 -0
- package/server/dist/routes/index.js +2 -0
- package/server/dist/routes/integrations.js +21 -1
- package/server/prisma/migrations/20260919090000_0022_integration_proxy_url/migration.sql +2 -0
- package/server/prisma/schema.prisma +2 -0
- package/server/client/dist/assets/Calendar-vA4Xa4Qr.js +0 -25
- package/server/client/dist/assets/Callout-Bo2P960P.js +0 -1
- package/server/client/dist/assets/Integration-gDDtIJYJ.js +0 -1
- package/server/client/dist/assets/Reminders-Dt2JuGci.js +0 -1
- package/server/client/dist/assets/TagNotes-DLwRF8pT.js +0 -1
- package/server/client/dist/assets/Views-8ghLRax7.js +0 -1
- package/server/client/dist/assets/app-Bcl0U7r9.css +0 -1
- package/server/client/dist/assets/app-DzChh-V0.js +0 -151
- package/server/client/dist/assets/index-Iy_BgP1R.js +0 -1
- package/server/client/dist/assets/integrations-LOdTs1T6.js +0 -1
- package/server/client/dist/assets/mcp-d8IzsoRz.js +0 -5
- package/server/client/dist/assets/search-DanMKCC8.js +0 -1
- package/server/dist/features/search/note-change.js +0 -16
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as e,r as E}from"./floating-ui.react-dom-BWTnA00G.js";import{u as M,b as k,i as L,m as _,N as I,d as Q,a as q}from"./useNoteMutate-
|
|
1
|
+
import{j as e,r as E}from"./floating-ui.react-dom-BWTnA00G.js";import{u as M,b as k,i as L,m as _,N as I,d as Q,a as q}from"./useNoteMutate-BQj6jHhV.js";import{Q as B,k as O,u as H,l as K,m as W,q as y,r as Y,p as $,P as w,E as z,T as o,B as j,n as G,o as i,e as U,t as V,v as Z,M as x}from"./time-Kh6oav5R.js";import{S as C}from"./SurfaceCard-zdJ8k1o0.js";import{a as J,N as X}from"./url-Bp8sXMb4.js";import"./NotePropertiesPanel-BFWjxL_w.js";import"./AuxiliaryPanelHeader-Bys98BJJ.js";import"./LinkSimple.es-iJvLf-MB.js";import"./Plus.es-Dmb3oijD.js";const m=25,v=O(J),S=30,R=`Deleted notes stay here for ${S} days before permanent removal`,g={title:"Permanently delete note?",description:"This note is referenced by the notes below. If you permanently delete it, those links will stay broken. Other notes will not be edited automatically.",confirmLabel:"Delete now",confirmVariant:"danger"},P=s=>Q(s).format("YYYY-MM-DD HH:mm"),ee=({note:s,onClose:n})=>{const h=s?.id??"",l=s?.title||"Untitled note",a=q({queryKey:y.notes.trashDetail(h),enabled:!!s,queryFn:async()=>{const d=await Z(h);if(d.type==="error")throw d;if(!d.trashedNote)throw new Error("Trashed note not found");return d.trashedNote}}),u=a.data?.contentAsMarkdown.trim()??"";return e.jsxs(x,{isOpen:!!s,onClose:n,variant:"inspect",children:[e.jsx(x.Header,{title:l,onClose:n}),e.jsx(x.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[s&&e.jsxs(o,{as:"p",variant:"meta",tone:"secondary",children:["Deleted ",P(s.deletedAt)]}),a.isLoading&&e.jsx(o,{as:"p",variant:"meta",tone:"secondary",children:"Loading deleted note content..."}),a.isError&&e.jsx(o,{as:"p",variant:"meta",tone:"error",children:"Failed to load deleted note content."}),!a.isLoading&&!a.isError&&u&&e.jsx("pre",{className:"max-h-[60vh] overflow-auto whitespace-pre-wrap break-words rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3 text-sm leading-6 text-fg-secondary",children:u}),!a.isLoading&&!a.isError&&!u&&e.jsx(o,{as:"p",variant:"meta",tone:"secondary",className:"rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-4 py-3",children:"No readable content was found for this deleted note."})]})}),e.jsx(x.Footer,{children:e.jsx(j,{variant:"ghost",size:"sm",onClick:n,children:"Close"})})]})},te=()=>e.jsx(w,{title:"Trash",variant:"default",heading:e.jsx(i,{width:118,height:24,className:"rounded-full"}),description:e.jsx(i,{width:352,height:16,className:"rounded-full"}),children:e.jsx("div",{className:"grid gap-3",children:Array.from({length:3},(s,n)=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-2",children:[e.jsx(i,{width:"40%",height:18,className:"rounded-full"}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(i,{width:168,height:14,className:"rounded-full"}),e.jsx(i,{width:176,height:14,className:"rounded-full"})]}),e.jsxs("div",{className:"flex flex-wrap gap-2",children:[e.jsx(i,{width:56,height:24,className:"rounded-full"}),e.jsx(i,{width:72,height:24,className:"rounded-full"})]})]}),e.jsx(i,{width:92,height:32,className:"rounded-[12px]"})]})},n))})}),se=()=>{const{page:s}=v.useSearch(),n=v.useNavigate(),h=H(),l=U(),a=M(),[u,d]=E.useState(null),[f,b]=E.useState(null),{data:c}=K({queryKey:y.notes.trash({limit:m,offset:(s-1)*m}),queryFn:async()=>{const t=await W({limit:m,offset:(s-1)*m});if(t.type==="error")throw t;return t.trashedNotes}}),N=k({mutationFn:Y,onSuccess:async t=>{if(t.type==="error")throw t;await L(h),l("The note has been restored."),n({to:X,params:{id:t.restoreTrashedNote.id}})},onError:()=>{l("Failed to restore the note.")}}),p=k({mutationFn:$,onSuccess:async t=>{if(t.type==="error")throw t;await h.invalidateQueries({queryKey:y.notes.trashAll(),exact:!1}),l("The note has been permanently deleted.")},onError:()=>{l("Failed to permanently delete the note.")}}),D=()=>{if(!f)return;const{id:t}=f;b(null),p.mutate(t)},A=async t=>{const r=await V(t);if(r.type==="error"){l("Failed to check linked notes before permanently deleting this note.");return}if(h.setQueryData(y.notes.backReferences(t),r.backReferences),r.backReferences.length>0){b({id:t,backReferences:r.backReferences});return}await a("Permanently delete this note? This cannot be undone.")&&p.mutate(t)},T=c.totalCount>0?`Trash (${c.totalCount})`:void 0,F=c.notes.find(t=>t.id===u)??null;return c.notes.length===0?e.jsx(w,{title:"Trash",variant:"default",heading:T,description:R,children:e.jsx(z,{title:"Trash is empty",description:`Delete a note and you can restore it here for ${S} days before permanent removal`})}):e.jsxs(w,{title:"Trash",variant:"default",heading:T,description:R,children:[e.jsxs("div",{className:"flex flex-col gap-4",children:[e.jsx("div",{className:"grid gap-3",children:c.notes.map(t=>e.jsx(C,{padding:"compact",children:e.jsxs("div",{className:"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",children:[e.jsxs("div",{className:"min-w-0 flex-1",children:[e.jsxs("div",{className:"flex min-w-0 items-start gap-1.5",children:[e.jsx(o,{as:"h2",variant:"body",weight:"medium",className:"min-w-0 flex-1 break-words leading-[1.25]",children:t.title||"Untitled note"}),t.pinned&&e.jsx("span",{title:"Pinned",className:"inline-flex h-5 w-5 shrink-0 items-center justify-center text-fg-tertiary",children:e.jsx(_,{className:"h-3.5 w-3.5"})})]}),e.jsx("div",{className:"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1",children:e.jsxs(o,{as:"div",variant:"meta",weight:"medium",tone:"secondary",children:["Deleted ",P(t.deletedAt)]})}),t.tagNames.length>0&&e.jsx("div",{className:"mt-2 flex flex-wrap gap-1.5",children:t.tagNames.map(r=>e.jsx(o,{as:"span",variant:"label",weight:"medium",tone:"tertiary",className:"rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5",children:r},`${t.id}-${r}`))}),t.contentPreview&&e.jsx("div",{className:"mt-3 rounded-[14px] border border-border-subtle bg-hover-subtle/50 px-3 py-2",children:e.jsx(o,{as:"p",variant:"meta",tone:"secondary",className:"line-clamp-3 whitespace-pre-wrap break-words",children:t.contentPreview})})]}),e.jsxs("div",{className:"flex shrink-0 flex-wrap gap-2 sm:justify-end",children:[e.jsx(j,{variant:"subtle",size:"sm",onClick:()=>d(t.id),children:"View content"}),e.jsx(j,{variant:"subtle",size:"sm",isLoading:N.isPending&&N.variables===t.id,onClick:()=>N.mutate(t.id),children:"Restore"}),e.jsx(j,{variant:"soft-danger",size:"sm",isLoading:p.isPending&&p.variables===t.id,onClick:()=>A(t.id),children:"Delete now"})]})]})},t.id))}),c.totalCount>m&&e.jsx(G,{page:s,last:Math.ceil(c.totalCount/m),onChange:t=>{n({search:r=>({...r,page:t})})}})]}),e.jsx(ee,{note:F,onClose:()=>d(null)}),e.jsx(I,{isOpen:!!f,title:g.title,description:g.description,references:f?.backReferences??[],confirmLabel:g.confirmLabel,confirmVariant:g.confirmVariant,onClose:()=>b(null),onConfirm:D})]})},me=()=>{const{page:s}=v.useSearch();return e.jsx(B,{fallback:e.jsx(te,{}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes",resetKeys:[s],children:e.jsx(se,{})})};export{me as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{t as o3,v as v3,g as O4,r,j as vl}from"./floating-ui.react-dom-BWTnA00G.js";import{
|
|
1
|
+
import{t as o3,v as v3,g as O4,r,j as vl}from"./floating-ui.react-dom-BWTnA00G.js";import{ak as r3,al as g3,am as b4,an as S3,ao as H4,ah as b3,ad as A3,u as U4,ap as M3,aq as E3,C as Ja,ar as z3,q as hl,A as T3,M as zu,T as sc,B as mc,F as p3,Y as D3,t as O3,as as H3,at as U3,e as _3,au as N3}from"./time-Kh6oav5R.js";import{N as _4}from"./url-Bp8sXMb4.js";var hc={exports:{}},Tu={},dc={exports:{}},yc={};var A4;function Z3(){return A4||(A4=1,(function(d){function T(m,A){var z=m.length;m.push(A);l:for(;0<z;){var D=z-1>>>1,O=m[D];if(0<G(O,A))m[D]=A,m[z]=O,z=D;else break l}}function R(m){return m.length===0?null:m[0]}function v(m){if(m.length===0)return null;var A=m[0],z=m.pop();if(z!==A){m[0]=z;l:for(var D=0,O=m.length,L=O>>>1;D<L;){var B=2*(D+1)-1,C=m[B],J=B+1,ol=m[J];if(0>G(C,z))J<O&&0>G(ol,C)?(m[D]=ol,m[J]=z,D=J):(m[D]=C,m[B]=z,D=B);else if(J<O&&0>G(ol,z))m[D]=ol,m[J]=z,D=J;else break l}}return A}function G(m,A){var z=m.sortIndex-A.sortIndex;return z!==0?z:m.id-A.id}if(d.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var nl=performance;d.unstable_now=function(){return nl.now()}}else{var Sl=Date,_l=Sl.now();d.unstable_now=function(){return Sl.now()-_l}}var cl=[],il=[],mt=1,U=null,$=3,I=!1,dl=!1,Nl=!1,kl=!1,It=typeof setTimeout=="function"?setTimeout:null,Ta=typeof clearTimeout=="function"?clearTimeout:null,Rl=typeof setImmediate<"u"?setImmediate:null;function Ot(m){for(var A=R(il);A!==null;){if(A.callback===null)v(il);else if(A.startTime<=m)v(il),A.sortIndex=A.expirationTime,T(cl,A);else break;A=R(il)}}function Il(m){if(Nl=!1,Ot(m),!dl)if(R(cl)!==null)dl=!0,Dl||(Dl=!0,Zl());else{var A=R(il);A!==null&&p(Il,A.startTime-m)}}var Dl=!1,ht=-1,Ql=5,zt=-1;function yl(){return kl?!0:!(d.unstable_now()-zt<Ql)}function K(){if(kl=!1,Dl){var m=d.unstable_now();zt=m;var A=!0;try{l:{dl=!1,Nl&&(Nl=!1,Ta(ht),ht=-1),I=!0;var z=$;try{t:{for(Ot(m),U=R(cl);U!==null&&!(U.expirationTime>m&&yl());){var D=U.callback;if(typeof D=="function"){U.callback=null,$=U.priorityLevel;var O=D(U.expirationTime<=m);if(m=d.unstable_now(),typeof O=="function"){U.callback=O,Ot(m),A=!0;break t}U===R(cl)&&v(cl),Ot(m)}else v(cl);U=R(cl)}if(U!==null)A=!0;else{var L=R(il);L!==null&&p(Il,L.startTime-m),A=!1}}break l}finally{U=null,$=z,I=!1}A=void 0}}finally{A?Zl():Dl=!1}}}var Zl;if(typeof Rl=="function")Zl=function(){Rl(K)};else if(typeof MessageChannel<"u"){var pa=new MessageChannel,q=pa.port2;pa.port1.onmessage=K,Zl=function(){q.postMessage(null)}}else Zl=function(){It(K,0)};function p(m,A){ht=It(function(){m(d.unstable_now())},A)}d.unstable_IdlePriority=5,d.unstable_ImmediatePriority=1,d.unstable_LowPriority=4,d.unstable_NormalPriority=3,d.unstable_Profiling=null,d.unstable_UserBlockingPriority=2,d.unstable_cancelCallback=function(m){m.callback=null},d.unstable_forceFrameRate=function(m){0>m||125<m?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):Ql=0<m?Math.floor(1e3/m):5},d.unstable_getCurrentPriorityLevel=function(){return $},d.unstable_next=function(m){switch($){case 1:case 2:case 3:var A=3;break;default:A=$}var z=$;$=A;try{return m()}finally{$=z}},d.unstable_requestPaint=function(){kl=!0},d.unstable_runWithPriority=function(m,A){switch(m){case 1:case 2:case 3:case 4:case 5:break;default:m=3}var z=$;$=m;try{return A()}finally{$=z}},d.unstable_scheduleCallback=function(m,A,z){var D=d.unstable_now();switch(typeof z=="object"&&z!==null?(z=z.delay,z=typeof z=="number"&&0<z?D+z:D):z=D,m){case 1:var O=-1;break;case 2:O=250;break;case 5:O=1073741823;break;case 4:O=1e4;break;default:O=5e3}return O=z+O,m={id:mt++,callback:A,priorityLevel:m,startTime:z,expirationTime:O,sortIndex:-1},z>D?(m.sortIndex=z,T(il,m),R(cl)===null&&m===R(il)&&(Nl?(Ta(ht),ht=-1):Nl=!0,p(Il,z-D))):(m.sortIndex=O,T(cl,m),dl||I||(dl=!0,Dl||(Dl=!0,Zl()))),m},d.unstable_shouldYield=yl,d.unstable_wrapCallback=function(m){var A=$;return function(){var z=$;$=A;try{return m.apply(this,arguments)}finally{$=z}}}})(yc)),yc}var M4;function q3(){return M4||(M4=1,dc.exports=Z3()),dc.exports}var E4;function C3(){if(E4)return Tu;E4=1;var d=q3(),T=o3(),R=v3();function v(l){var t="https://react.dev/errors/"+l;if(1<arguments.length){t+="?args[]="+encodeURIComponent(arguments[1]);for(var a=2;a<arguments.length;a++)t+="&args[]="+encodeURIComponent(arguments[a])}return"Minified React error #"+l+"; visit "+t+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function G(l){return!(!l||l.nodeType!==1&&l.nodeType!==9&&l.nodeType!==11)}function nl(l){var t=l,a=l;if(l.alternate)for(;t.return;)t=t.return;else{l=t;do t=l,(t.flags&4098)!==0&&(a=t.return),l=t.return;while(l)}return t.tag===3?a:null}function Sl(l){if(l.tag===13){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function _l(l){if(l.tag===31){var t=l.memoizedState;if(t===null&&(l=l.alternate,l!==null&&(t=l.memoizedState)),t!==null)return t.dehydrated}return null}function cl(l){if(nl(l)!==l)throw Error(v(188))}function il(l){var t=l.alternate;if(!t){if(t=nl(l),t===null)throw Error(v(188));return t!==l?null:l}for(var a=l,e=t;;){var u=a.return;if(u===null)break;var n=u.alternate;if(n===null){if(e=u.return,e!==null){a=e;continue}break}if(u.child===n.child){for(n=u.child;n;){if(n===a)return cl(u),l;if(n===e)return cl(u),t;n=n.sibling}throw Error(v(188))}if(a.return!==e.return)a=u,e=n;else{for(var c=!1,i=u.child;i;){if(i===a){c=!0,a=u,e=n;break}if(i===e){c=!0,e=u,a=n;break}i=i.sibling}if(!c){for(i=n.child;i;){if(i===a){c=!0,a=n,e=u;break}if(i===e){c=!0,e=n,a=u;break}i=i.sibling}if(!c)throw Error(v(189))}}if(a.alternate!==e)throw Error(v(190))}if(a.tag!==3)throw Error(v(188));return a.stateNode.current===a?l:t}function mt(l){var t=l.tag;if(t===5||t===26||t===27||t===6)return l;for(l=l.child;l!==null;){if(t=mt(l),t!==null)return t;l=l.sibling}return null}var U=Object.assign,$=Symbol.for("react.element"),I=Symbol.for("react.transitional.element"),dl=Symbol.for("react.portal"),Nl=Symbol.for("react.fragment"),kl=Symbol.for("react.strict_mode"),It=Symbol.for("react.profiler"),Ta=Symbol.for("react.consumer"),Rl=Symbol.for("react.context"),Ot=Symbol.for("react.forward_ref"),Il=Symbol.for("react.suspense"),Dl=Symbol.for("react.suspense_list"),ht=Symbol.for("react.memo"),Ql=Symbol.for("react.lazy"),zt=Symbol.for("react.activity"),yl=Symbol.for("react.memo_cache_sentinel"),K=Symbol.iterator;function Zl(l){return l===null||typeof l!="object"?null:(l=K&&l[K]||l["@@iterator"],typeof l=="function"?l:null)}var pa=Symbol.for("react.client.reference");function q(l){if(l==null)return null;if(typeof l=="function")return l.$$typeof===pa?null:l.displayName||l.name||null;if(typeof l=="string")return l;switch(l){case Nl:return"Fragment";case It:return"Profiler";case kl:return"StrictMode";case Il:return"Suspense";case Dl:return"SuspenseList";case zt:return"Activity"}if(typeof l=="object")switch(l.$$typeof){case dl:return"Portal";case Rl:return l.displayName||"Context";case Ta:return(l._context.displayName||"Context")+".Consumer";case Ot:var t=l.render;return l=l.displayName,l||(l=t.displayName||t.name||"",l=l!==""?"ForwardRef("+l+")":"ForwardRef"),l;case ht:return t=l.displayName||null,t!==null?t:q(l.type)||"Memo";case Ql:t=l._payload,l=l._init;try{return q(l(t))}catch{}}return null}var p=Array.isArray,m=T.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,A=R.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,z={pending:!1,data:null,method:null,action:null},D=[],O=-1;function L(l){return{current:l}}function B(l){0>O||(l.current=D[O],D[O]=null,O--)}function C(l,t){O++,D[O]=l.current,l.current=t}var J=L(null),ol=L(null),jl=L(null),dt=L(null);function Ht(l,t){switch(C(jl,t),C(ol,l),C(J,null),t.nodeType){case 9:case 11:l=(l=t.documentElement)&&(l=l.namespaceURI)?G2(l):0;break;default:if(l=t.tagName,t=t.namespaceURI)t=G2(t),l=X2(t,l);else switch(l){case"svg":l=1;break;case"math":l=2;break;default:l=0}}B(J),C(J,l)}function Gl(){B(J),B(ol),B(jl)}function Ol(l){l.memoizedState!==null&&C(dt,l);var t=J.current,a=X2(t,l.type);t!==a&&(C(ol,l),C(J,a))}function Pl(l){ol.current===l&&(B(J),B(ol)),dt.current===l&&(B(dt),bu._currentValue=z)}var qt,Da;function Oa(l){if(qt===void 0)try{throw Error()}catch(a){var t=a.stack.trim().match(/\n( *(at )?)/);qt=t&&t[1]||"",Da=-1<a.stack.indexOf(`
|
|
2
2
|
at`)?" (<anonymous>)":-1<a.stack.indexOf("@")?"@unknown:0:0":""}return`
|
|
3
3
|
`+qt+l+Da}var Jn=!1;function wn(l,t){if(!l||Jn)return"";Jn=!0;var a=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var e={DetermineComponentFrameRoot:function(){try{if(t){var E=function(){throw Error()};if(Object.defineProperty(E.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(E,[])}catch(S){var g=S}Reflect.construct(l,[],E)}else{try{E.call()}catch(S){g=S}l.call(E.prototype)}}else{try{throw Error()}catch(S){g=S}(E=l())&&typeof E.catch=="function"&&E.catch(function(){})}}catch(S){if(S&&g&&typeof S.stack=="string")return[S.stack,g.stack]}return[null,null]}};e.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var u=Object.getOwnPropertyDescriptor(e.DetermineComponentFrameRoot,"name");u&&u.configurable&&Object.defineProperty(e.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var n=e.DetermineComponentFrameRoot(),c=n[0],i=n[1];if(c&&i){var f=c.split(`
|
|
4
4
|
`),o=i.split(`
|
package/server/client/dist/assets/{useReminderMutate-Cexk1ydX.js → useReminderMutate-jubqwSD9.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{r as u,j as r}from"./floating-ui.react-dom-BWTnA00G.js";import{d as C,M as F}from"./useNoteMutate-
|
|
1
|
+
import{r as u,j as r}from"./floating-ui.react-dom-BWTnA00G.js";import{d as C,M as F}from"./useNoteMutate-BQj6jHhV.js";import{g as p,M as f,L as h,I as w,d as N,a as S,b as v,T as $,B as R,u as q,q as b,e as A}from"./time-Kh6oav5R.js";const I=(e={})=>({limit:e.limit??10,offset:e.offset??0}),y=`
|
|
2
2
|
totalCount
|
|
3
3
|
reminders {
|
|
4
4
|
id
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
<link rel="icon" type="image/png" href="/icon.png" />
|
|
9
9
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
10
10
|
<title>Ocean Brain</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/app-
|
|
11
|
+
<script type="module" crossorigin src="/assets/app-OGsLwSqJ.js"></script>
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/floating-ui.react-dom-BWTnA00G.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/url-Bp8sXMb4.js">
|
|
14
|
-
<link rel="modulepreload" crossorigin href="/assets/time-
|
|
15
|
-
<link rel="modulepreload" crossorigin href="/assets/useNoteMutate-
|
|
14
|
+
<link rel="modulepreload" crossorigin href="/assets/time-Kh6oav5R.js">
|
|
15
|
+
<link rel="modulepreload" crossorigin href="/assets/useNoteMutate-BQj6jHhV.js">
|
|
16
16
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-BXl3LOEh.js">
|
|
17
|
-
<link rel="modulepreload" crossorigin href="/assets/Graph-
|
|
18
|
-
<link rel="stylesheet" crossorigin href="/assets/app-
|
|
17
|
+
<link rel="modulepreload" crossorigin href="/assets/Graph-BaqGi3M7.js">
|
|
18
|
+
<link rel="stylesheet" crossorigin href="/assets/app-yYMsgUrz.css">
|
|
19
19
|
</head>
|
|
20
20
|
|
|
21
21
|
<body>
|
package/server/dist/app.js
CHANGED
|
@@ -4,6 +4,7 @@ import fastifyFormbody from "@fastify/formbody";
|
|
|
4
4
|
import fastifyRateLimit from "@fastify/rate-limit";
|
|
5
5
|
import fastifySession from "@fastify/session";
|
|
6
6
|
import Fastify from "fastify";
|
|
7
|
+
import { createDatabaseAppGatewayOptions } from "./features/app-gateway/database.js";
|
|
7
8
|
import { createIntegrationService } from "./features/integration/service.js";
|
|
8
9
|
import { createMcpAdminService } from "./features/mcp-admin/service.js";
|
|
9
10
|
import { purgeExpiredNoteSnapshots } from "./features/note/services/snapshot.js";
|
|
@@ -11,7 +12,13 @@ import { purgeExpiredTrashedNotes } from "./features/note/services/trash.js";
|
|
|
11
12
|
import { closeDefaultSemanticSearchManager } from "./features/search/search-manager.js";
|
|
12
13
|
import { createAppError, createErrorHandler } from "./modules/error-handler.js";
|
|
13
14
|
import { AUTH_SESSION_IDLE_TIMEOUT_MS, createSessionStore } from "./modules/session-store.js";
|
|
14
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
createApiRouter,
|
|
17
|
+
createAppGatewayRouter,
|
|
18
|
+
createAuthPagesRouter,
|
|
19
|
+
createClientRouter,
|
|
20
|
+
createGraphqlRouter
|
|
21
|
+
} from "./routes/index.js";
|
|
15
22
|
const MAX_REQUEST_BODY_BYTES = 50 * 1024 * 1024;
|
|
16
23
|
const isHttpLoggerEnabled = (loggerOption) => loggerOption ?? process.env.OCEAN_BRAIN_HTTP_LOG?.toLowerCase() !== "false";
|
|
17
24
|
const createLoggerOptions = () => ({
|
|
@@ -99,6 +106,7 @@ const createAppWithMcpAuth = (authConfig, mcpAdminService, options = {}) => {
|
|
|
99
106
|
});
|
|
100
107
|
registerRequestInfrastructure(app, authConfig);
|
|
101
108
|
app.setErrorHandler(createErrorHandler(authConfig));
|
|
109
|
+
app.register(createAppGatewayRouter(authConfig, options.appGateway ?? createDatabaseAppGatewayOptions()));
|
|
102
110
|
app.register(createApiRouter(authConfig, mcpAdminService), { prefix: "/api" });
|
|
103
111
|
app.register(createAuthPagesRouter(authConfig));
|
|
104
112
|
app.register(createGraphqlRouter(authConfig, mcpAdminService));
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
const APP_GATEWAY_ACCESS_PROTOCOL_PREFIX = "ocean-brain.access.";
|
|
3
|
+
const APP_GATEWAY_ACCESS_COOKIE_NAME = "ocean-brain.app-access";
|
|
4
|
+
const APP_GATEWAY_ACCESS_HEADER = "x-ocean-brain-app-access";
|
|
5
|
+
const APP_GATEWAY_PROTOCOL = "ocean-brain.app";
|
|
6
|
+
const APP_GATEWAY_ACCESS_TTL_MS = 5 * 60 * 1e3;
|
|
7
|
+
const MAX_ACTIVE_ACCESS_TOKENS = 1024;
|
|
8
|
+
const hashToken = (token) => createHash("sha256").update(token, "utf8").digest("hex");
|
|
9
|
+
const extractAppGatewayAccessToken = (protocolHeader) => {
|
|
10
|
+
const protocols = (Array.isArray(protocolHeader) ? protocolHeader.join(",") : protocolHeader ?? "").split(",").map((protocol) => protocol.trim());
|
|
11
|
+
const accessProtocol = protocols.find((protocol) => protocol.startsWith(APP_GATEWAY_ACCESS_PROTOCOL_PREFIX));
|
|
12
|
+
const token = accessProtocol?.slice(APP_GATEWAY_ACCESS_PROTOCOL_PREFIX.length);
|
|
13
|
+
return token || void 0;
|
|
14
|
+
};
|
|
15
|
+
const extractAppGatewayAccessCookie = (cookieHeader) => {
|
|
16
|
+
const cookie = cookieHeader?.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${APP_GATEWAY_ACCESS_COOKIE_NAME}=`));
|
|
17
|
+
return cookie?.slice(APP_GATEWAY_ACCESS_COOKIE_NAME.length + 1) || void 0;
|
|
18
|
+
};
|
|
19
|
+
const selectAppGatewayProtocol = (protocols) => {
|
|
20
|
+
for (const protocol of protocols) {
|
|
21
|
+
if (!protocol.startsWith(APP_GATEWAY_ACCESS_PROTOCOL_PREFIX)) return protocol;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
25
|
+
const createAppGatewayAccessService = () => {
|
|
26
|
+
const grants = /* @__PURE__ */ new Map();
|
|
27
|
+
const prune = (now) => {
|
|
28
|
+
for (const [tokenHash, grant] of grants) {
|
|
29
|
+
if (grant.expiresAt <= now) grants.delete(tokenHash);
|
|
30
|
+
}
|
|
31
|
+
while (grants.size >= MAX_ACTIVE_ACCESS_TOKENS) {
|
|
32
|
+
const oldestTokenHash = grants.keys().next().value;
|
|
33
|
+
if (!oldestTokenHash) break;
|
|
34
|
+
grants.delete(oldestTokenHash);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const resolve = (connectionId, token) => {
|
|
38
|
+
if (!token) return void 0;
|
|
39
|
+
const now = Date.now();
|
|
40
|
+
const tokenHash = hashToken(token);
|
|
41
|
+
const grant = grants.get(tokenHash);
|
|
42
|
+
if (!grant || grant.expiresAt <= now) {
|
|
43
|
+
grants.delete(tokenHash);
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
return grant.connectionId === connectionId ? grant : void 0;
|
|
47
|
+
};
|
|
48
|
+
return {
|
|
49
|
+
issue(connectionId, publicProtocol = "http") {
|
|
50
|
+
const now = Date.now();
|
|
51
|
+
prune(now);
|
|
52
|
+
const token = randomBytes(32).toString("base64url");
|
|
53
|
+
const expiresAt = now + APP_GATEWAY_ACCESS_TTL_MS;
|
|
54
|
+
grants.set(hashToken(token), { connectionId, expiresAt, publicProtocol });
|
|
55
|
+
return { token, expiresAt: new Date(expiresAt).toISOString() };
|
|
56
|
+
},
|
|
57
|
+
resolve,
|
|
58
|
+
verify(connectionId, token) {
|
|
59
|
+
return Boolean(resolve(connectionId, token));
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
export {
|
|
64
|
+
APP_GATEWAY_ACCESS_COOKIE_NAME,
|
|
65
|
+
APP_GATEWAY_ACCESS_HEADER,
|
|
66
|
+
APP_GATEWAY_ACCESS_PROTOCOL_PREFIX,
|
|
67
|
+
APP_GATEWAY_ACCESS_TTL_MS,
|
|
68
|
+
APP_GATEWAY_PROTOCOL,
|
|
69
|
+
createAppGatewayAccessService,
|
|
70
|
+
extractAppGatewayAccessCookie,
|
|
71
|
+
extractAppGatewayAccessToken,
|
|
72
|
+
selectAppGatewayProtocol
|
|
73
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import models from "../../models.js";
|
|
2
|
+
import { parseManifest } from "../integration/manifest.js";
|
|
3
|
+
const findConnection = (connectionId) => models.integrationConnection.findUnique({ where: { id: connectionId } });
|
|
4
|
+
const createDatabaseAppGatewayOptions = (lookup = findConnection) => ({
|
|
5
|
+
resolveConnection: async (connectionId) => {
|
|
6
|
+
const connection = await lookup(connectionId);
|
|
7
|
+
if (!connection) return null;
|
|
8
|
+
const manifest = parseManifest(JSON.parse(connection.manifest));
|
|
9
|
+
if (manifest.launch?.mode !== "proxied") return null;
|
|
10
|
+
return {
|
|
11
|
+
id: connection.id,
|
|
12
|
+
integrationId: connection.integrationId,
|
|
13
|
+
enabled: connection.enabled,
|
|
14
|
+
proxyUrl: connection.proxyUrl
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
export {
|
|
19
|
+
createDatabaseAppGatewayOptions
|
|
20
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { createAppError } from "../../modules/error-handler.js";
|
|
2
|
+
const APP_GATEWAY_PUBLIC_PREFIX = "/apps";
|
|
3
|
+
const APP_IDENTIFIER_PATTERN = /^[a-z0-9](?:[a-z0-9._-]{0,126}[a-z0-9])?$/;
|
|
4
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
5
|
+
const BLOCKED_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
6
|
+
"authorization",
|
|
7
|
+
"connection",
|
|
8
|
+
"cookie",
|
|
9
|
+
"forwarded",
|
|
10
|
+
"host",
|
|
11
|
+
"keep-alive",
|
|
12
|
+
"proxy-authenticate",
|
|
13
|
+
"proxy-authorization",
|
|
14
|
+
"te",
|
|
15
|
+
"trailer",
|
|
16
|
+
"transfer-encoding",
|
|
17
|
+
"upgrade",
|
|
18
|
+
"x-csrf-token",
|
|
19
|
+
"x-forwarded-for",
|
|
20
|
+
"x-forwarded-host",
|
|
21
|
+
"x-forwarded-port",
|
|
22
|
+
"x-forwarded-prefix",
|
|
23
|
+
"x-forwarded-proto",
|
|
24
|
+
"x-real-ip",
|
|
25
|
+
"x-xsrf-token"
|
|
26
|
+
]);
|
|
27
|
+
const WEBSOCKET_REQUEST_HEADERS = ["accept-language", "origin", "user-agent"];
|
|
28
|
+
const isAppIdentifier = (value) => APP_IDENTIFIER_PATTERN.test(value);
|
|
29
|
+
const resolveAppGatewayOptions = (options) => {
|
|
30
|
+
if (typeof options.resolveConnection !== "function") {
|
|
31
|
+
throw new Error("The app gateway requires a connection resolver.");
|
|
32
|
+
}
|
|
33
|
+
const requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
34
|
+
if (!Number.isSafeInteger(requestTimeoutMs) || requestTimeoutMs < 1e3 || requestTimeoutMs > 12e4) {
|
|
35
|
+
throw new Error("The app gateway request timeout must be between 1000 and 120000 milliseconds.");
|
|
36
|
+
}
|
|
37
|
+
return { ...options, requestTimeoutMs };
|
|
38
|
+
};
|
|
39
|
+
const normalizeProxyUrl = (value) => {
|
|
40
|
+
if (!value) return null;
|
|
41
|
+
let url;
|
|
42
|
+
try {
|
|
43
|
+
url = new URL(value);
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return url.origin;
|
|
51
|
+
};
|
|
52
|
+
const resolveAppGatewayConnection = async (options, connectionId) => {
|
|
53
|
+
if (!isAppIdentifier(connectionId)) {
|
|
54
|
+
throw createAppError(400, "INVALID_APP_CONNECTION_ID", "The app connection id is invalid.");
|
|
55
|
+
}
|
|
56
|
+
const connection = await options.resolveConnection(connectionId);
|
|
57
|
+
if (!connection) {
|
|
58
|
+
throw createAppError(404, "APP_CONNECTION_NOT_FOUND", "The proxied app connection was not found.");
|
|
59
|
+
}
|
|
60
|
+
if (connection.id !== connectionId || !isAppIdentifier(connection.integrationId)) {
|
|
61
|
+
throw createAppError(502, "INVALID_APP_PROXY_STATE", "The proxied app connection is invalid.");
|
|
62
|
+
}
|
|
63
|
+
if (!connection.enabled) {
|
|
64
|
+
throw createAppError(503, "APP_CONNECTION_DISABLED", "The proxied app connection is disabled.");
|
|
65
|
+
}
|
|
66
|
+
const proxyUrl = normalizeProxyUrl(connection.proxyUrl);
|
|
67
|
+
if (!proxyUrl) {
|
|
68
|
+
throw createAppError(503, "APP_PROXY_NOT_CONFIGURED", "The proxied app URL is not configured.");
|
|
69
|
+
}
|
|
70
|
+
return { ...connection, proxyUrl };
|
|
71
|
+
};
|
|
72
|
+
const validateAppGatewayRequestPath = (requestUrl) => {
|
|
73
|
+
let path = requestUrl.split("?", 1)[0];
|
|
74
|
+
for (let index = 0; index < 3; index += 1) {
|
|
75
|
+
const normalized = path.replaceAll("\\", "/");
|
|
76
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(normalized) || normalized.startsWith("//")) {
|
|
77
|
+
throw createAppError(400, "INVALID_APP_PROXY_PATH", "The app request path is invalid.");
|
|
78
|
+
}
|
|
79
|
+
let decoded;
|
|
80
|
+
try {
|
|
81
|
+
decoded = decodeURIComponent(normalized);
|
|
82
|
+
} catch {
|
|
83
|
+
throw createAppError(400, "INVALID_APP_PROXY_PATH", "The app request path is invalid.");
|
|
84
|
+
}
|
|
85
|
+
if (decoded === ".." || decoded.includes("/..") || decoded.includes("../")) {
|
|
86
|
+
throw createAppError(400, "INVALID_APP_PROXY_PATH", "The app request path is invalid.");
|
|
87
|
+
}
|
|
88
|
+
if (decoded === path) break;
|
|
89
|
+
path = decoded;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const withoutBlockedHeaders = (headers) => {
|
|
93
|
+
const forwarded = {};
|
|
94
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
95
|
+
const normalizedName = name.toLowerCase();
|
|
96
|
+
if (BLOCKED_REQUEST_HEADERS.has(normalizedName) || normalizedName.startsWith("x-forwarded-") || normalizedName.startsWith("x-ocean-brain-")) {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
forwarded[normalizedName] = value;
|
|
100
|
+
}
|
|
101
|
+
return forwarded;
|
|
102
|
+
};
|
|
103
|
+
const addGatewayHeaders = (request, headers) => {
|
|
104
|
+
const connection = request.appGatewayConnection;
|
|
105
|
+
if (!connection) {
|
|
106
|
+
throw createAppError(502, "APP_GATEWAY_CONTEXT_MISSING", "The app gateway request context is missing.");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
...headers,
|
|
110
|
+
"x-forwarded-host": request.headers.host,
|
|
111
|
+
"x-forwarded-prefix": `${APP_GATEWAY_PUBLIC_PREFIX}/${connection.id}`,
|
|
112
|
+
"x-forwarded-proto": request.appGatewayPublicProtocol ?? request.protocol,
|
|
113
|
+
"x-ocean-brain-connection-id": connection.id,
|
|
114
|
+
"x-ocean-brain-integration-id": connection.integrationId
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
const createProxyRequestHeaders = (request, headers) => addGatewayHeaders(request, withoutBlockedHeaders(headers));
|
|
118
|
+
const createProxyWebSocketHeaders = (request) => {
|
|
119
|
+
const headers = {};
|
|
120
|
+
for (const name of WEBSOCKET_REQUEST_HEADERS) {
|
|
121
|
+
const value = request.headers[name];
|
|
122
|
+
if (value !== void 0) headers[name] = value;
|
|
123
|
+
}
|
|
124
|
+
return addGatewayHeaders(request, headers);
|
|
125
|
+
};
|
|
126
|
+
const getAppGatewayUpstream = (request) => {
|
|
127
|
+
const connection = request.appGatewayConnection;
|
|
128
|
+
if (!connection) {
|
|
129
|
+
throw createAppError(502, "APP_GATEWAY_CONTEXT_MISSING", "The app gateway request context is missing.");
|
|
130
|
+
}
|
|
131
|
+
return connection.proxyUrl;
|
|
132
|
+
};
|
|
133
|
+
const getHeader = (headers, name) => {
|
|
134
|
+
const value = headers[name];
|
|
135
|
+
return Array.isArray(value) ? value[0] : value;
|
|
136
|
+
};
|
|
137
|
+
const isWithinPath = (path, prefix) => path === prefix || path.startsWith(`${prefix}/`);
|
|
138
|
+
const rewriteProxyLocation = (location, request) => {
|
|
139
|
+
const connection = request.appGatewayConnection;
|
|
140
|
+
if (!connection) return void 0;
|
|
141
|
+
const publicPrefix = `${APP_GATEWAY_PUBLIC_PREFIX}/${connection.id}`;
|
|
142
|
+
const requestUrl = new URL(request.url, "http://ocean-brain.invalid");
|
|
143
|
+
const requestSuffix = isWithinPath(requestUrl.pathname, publicPrefix) ? requestUrl.pathname.slice(publicPrefix.length) || "/" : "/";
|
|
144
|
+
const upstreamRequestUrl = new URL(`${requestSuffix}${requestUrl.search}`, `${connection.proxyUrl}/`);
|
|
145
|
+
let destination;
|
|
146
|
+
try {
|
|
147
|
+
destination = new URL(location, upstreamRequestUrl);
|
|
148
|
+
} catch {
|
|
149
|
+
return void 0;
|
|
150
|
+
}
|
|
151
|
+
if (destination.origin !== connection.proxyUrl) return void 0;
|
|
152
|
+
if (isWithinPath(destination.pathname, publicPrefix)) {
|
|
153
|
+
return `${destination.pathname}${destination.search}${destination.hash}`;
|
|
154
|
+
}
|
|
155
|
+
return `${publicPrefix}${destination.pathname}${destination.search}${destination.hash}`;
|
|
156
|
+
};
|
|
157
|
+
const appendVary = (current, value) => {
|
|
158
|
+
const values = new Set(
|
|
159
|
+
(Array.isArray(current) ? current : current?.split(",") ?? []).map((item) => item.trim()).filter(Boolean)
|
|
160
|
+
);
|
|
161
|
+
values.add(value);
|
|
162
|
+
return Array.from(values).join(", ");
|
|
163
|
+
};
|
|
164
|
+
const APP_SANDBOX_POLICY = "sandbox allow-downloads allow-forms allow-modals allow-scripts; frame-ancestors 'self'; object-src 'none'";
|
|
165
|
+
const createGatewayResponseHeaders = (source, request) => {
|
|
166
|
+
const headers = { ...source };
|
|
167
|
+
delete headers["set-cookie"];
|
|
168
|
+
delete headers["set-cookie2"];
|
|
169
|
+
delete headers["x-frame-options"];
|
|
170
|
+
delete headers["content-security-policy-report-only"];
|
|
171
|
+
delete headers["clear-site-data"];
|
|
172
|
+
delete headers.nel;
|
|
173
|
+
delete headers["report-to"];
|
|
174
|
+
delete headers.refresh;
|
|
175
|
+
delete headers["access-control-allow-origin"];
|
|
176
|
+
delete headers["access-control-allow-credentials"];
|
|
177
|
+
delete headers["proxy-authenticate"];
|
|
178
|
+
delete headers["www-authenticate"];
|
|
179
|
+
const location = getHeader(headers, "location");
|
|
180
|
+
if (location) {
|
|
181
|
+
const rewrittenLocation = rewriteProxyLocation(location, request);
|
|
182
|
+
if (rewrittenLocation) headers.location = rewrittenLocation;
|
|
183
|
+
else delete headers.location;
|
|
184
|
+
}
|
|
185
|
+
const upstreamPolicy = headers["content-security-policy"];
|
|
186
|
+
headers["content-security-policy"] = upstreamPolicy ? [...Array.isArray(upstreamPolicy) ? upstreamPolicy : [upstreamPolicy], APP_SANDBOX_POLICY] : APP_SANDBOX_POLICY;
|
|
187
|
+
headers["permissions-policy"] = "camera=(), geolocation=(), microphone=(), payment=(), usb=()";
|
|
188
|
+
headers["referrer-policy"] = "no-referrer";
|
|
189
|
+
headers["x-content-type-options"] = "nosniff";
|
|
190
|
+
headers["cache-control"] ??= "private, no-store";
|
|
191
|
+
if (request.appGatewayCorsAllowed) {
|
|
192
|
+
headers["access-control-allow-origin"] = "null";
|
|
193
|
+
headers["access-control-allow-credentials"] = "true";
|
|
194
|
+
headers.vary = appendVary(headers.vary, "Origin");
|
|
195
|
+
}
|
|
196
|
+
return headers;
|
|
197
|
+
};
|
|
198
|
+
export {
|
|
199
|
+
APP_GATEWAY_PUBLIC_PREFIX,
|
|
200
|
+
createGatewayResponseHeaders,
|
|
201
|
+
createProxyRequestHeaders,
|
|
202
|
+
createProxyWebSocketHeaders,
|
|
203
|
+
getAppGatewayUpstream,
|
|
204
|
+
isAppIdentifier,
|
|
205
|
+
resolveAppGatewayConnection,
|
|
206
|
+
resolveAppGatewayOptions,
|
|
207
|
+
validateAppGatewayRequestPath
|
|
208
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
const INTEGRATION_ACCESS_CHANGE_CHANNEL = "integration-access-change";
|
|
3
|
+
const integrationAccessEmitter = new EventEmitter();
|
|
4
|
+
integrationAccessEmitter.setMaxListeners(0);
|
|
5
|
+
const emitIntegrationAccessChanged = (connectionId) => {
|
|
6
|
+
integrationAccessEmitter.emit(INTEGRATION_ACCESS_CHANGE_CHANNEL, connectionId);
|
|
7
|
+
};
|
|
8
|
+
const subscribeIntegrationAccessChanges = (listener) => {
|
|
9
|
+
integrationAccessEmitter.on(INTEGRATION_ACCESS_CHANGE_CHANNEL, listener);
|
|
10
|
+
return () => {
|
|
11
|
+
integrationAccessEmitter.off(INTEGRATION_ACCESS_CHANGE_CHANNEL, listener);
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
export {
|
|
15
|
+
emitIntegrationAccessChanged,
|
|
16
|
+
subscribeIntegrationAccessChanges
|
|
17
|
+
};
|
|
@@ -48,8 +48,13 @@ const parseManifest = (value) => {
|
|
|
48
48
|
return invalid("An integration requesting write access must also request notes:read.");
|
|
49
49
|
}
|
|
50
50
|
if (value.launch !== void 0) {
|
|
51
|
-
if (!isRecord(value.launch) || !["external", "iframe"].includes(String(value.launch.mode))) {
|
|
52
|
-
return invalid("launch requires a url
|
|
51
|
+
if (!isRecord(value.launch) || !["external", "iframe", "proxied"].includes(String(value.launch.mode))) {
|
|
52
|
+
return invalid("launch requires proxied mode or a url with external or iframe mode.");
|
|
53
|
+
}
|
|
54
|
+
if (value.launch.mode === "proxied") {
|
|
55
|
+
if (value.launch.url !== void 0) return invalid("A proxied launch cannot provide a url.");
|
|
56
|
+
manifest.launch = { mode: "proxied" };
|
|
57
|
+
return manifest;
|
|
53
58
|
}
|
|
54
59
|
let url;
|
|
55
60
|
try {
|
|
@@ -65,6 +70,29 @@ const parseManifest = (value) => {
|
|
|
65
70
|
}
|
|
66
71
|
return manifest;
|
|
67
72
|
};
|
|
73
|
+
const parseIntegrationProxyUrl = (value) => {
|
|
74
|
+
if (typeof value !== "string" || !value.trim() || value.length > 2e3) {
|
|
75
|
+
throw createAppError(
|
|
76
|
+
400,
|
|
77
|
+
"INVALID_INTEGRATION_PROXY_URL",
|
|
78
|
+
"The private app URL must be a nonempty string of at most 2000 characters."
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
let url;
|
|
82
|
+
try {
|
|
83
|
+
url = new URL(value.trim());
|
|
84
|
+
} catch {
|
|
85
|
+
throw createAppError(400, "INVALID_INTEGRATION_PROXY_URL", "The private app URL must be an absolute URL.");
|
|
86
|
+
}
|
|
87
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
|
|
88
|
+
throw createAppError(
|
|
89
|
+
400,
|
|
90
|
+
"INVALID_INTEGRATION_PROXY_URL",
|
|
91
|
+
"The private app URL must be an HTTP(S) origin without credentials, a path, query, or fragment."
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
return url.origin;
|
|
95
|
+
};
|
|
68
96
|
const validateGrants = (value, manifest) => {
|
|
69
97
|
const permissions = parsePermissions(value);
|
|
70
98
|
if (permissions.some((permission) => !manifest.permissions.includes(permission))) {
|
|
@@ -81,6 +109,7 @@ export {
|
|
|
81
109
|
MCP_INTEGRATION_MANIFEST,
|
|
82
110
|
NATIVE_INTEGRATIONS,
|
|
83
111
|
isRecord,
|
|
112
|
+
parseIntegrationProxyUrl,
|
|
84
113
|
parseManifest,
|
|
85
114
|
parsePermissions,
|
|
86
115
|
validateGrants
|