ocean-brain 0.2.1 → 0.3.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/dist/index.js +73 -41
- package/dist/mcp.js +587 -26
- package/package.json +6 -1
- package/scripts/postinstall-prisma.mjs +41 -0
- package/server/client/dist/assets/Calendar-DRv2aEl8.js +28 -0
- package/server/client/dist/assets/Callout-B2kPcDrq.js +1 -0
- package/server/client/dist/assets/Graph-DotbTrZR.js +1 -0
- package/server/client/dist/assets/Image-B5R7gF2P.js +1 -0
- package/server/client/dist/assets/Image.es-Dld9r9MJ.js +1 -0
- package/server/client/dist/assets/ModalActionRow-CES9L_Gy.js +1 -0
- package/server/client/dist/assets/Note-SK4hDhEM.css +1 -0
- package/server/client/dist/assets/Note-cIbXJc1v.js +1 -0
- package/server/client/dist/assets/Reminders-4LunlZRb.js +1 -0
- package/server/client/dist/assets/Search-Cp17DGR6.js +1 -0
- package/server/client/dist/assets/SurfaceCard-yzVU4Qkx.js +1 -0
- package/server/client/dist/assets/Tag-CwmuMPmo.js +1 -0
- package/server/client/dist/assets/TagNotes-gGroWMtT.js +1 -0
- package/server/client/dist/assets/Trash.es-B3KWD8_C.js +1 -0
- package/server/client/dist/assets/color-wpu7wT_s.js +1 -0
- package/server/client/dist/assets/graph-vendor-CUxe67Lr.js +46 -0
- package/server/client/dist/assets/image.api-DJgOBt8S.js +17 -0
- package/server/client/dist/assets/index-40Y2UsVr.js +205 -0
- package/server/client/dist/assets/index-BnxpTwtL.css +1 -0
- package/server/client/dist/assets/index-DYUg2N-6.js +1 -0
- package/server/client/dist/assets/manage-image-ByY1mRZx.js +1 -0
- package/server/client/dist/assets/manage-image-detail-BVaDZHRu.js +1 -0
- package/server/client/dist/assets/mcp-Jj3061aI.js +1 -0
- package/server/client/dist/assets/{note-core-DTIu0anW.js → note-core-BCgMq5QA.js} +7 -7
- package/server/client/dist/assets/note-vendor-BofYbzmZ.js +54 -0
- package/server/client/dist/assets/placeholder-4P_GK7fV.js +34 -0
- package/server/client/dist/assets/trash-CPBwY07S.js +1 -0
- package/server/client/dist/assets/useReminderMutate-1YVndM2T.js +82 -0
- package/server/client/dist/index.html +5 -4
- package/server/dist/app.js +97 -9
- package/server/dist/app.js.map +1 -1
- package/server/dist/main.js +16 -2
- package/server/dist/main.js.map +1 -1
- package/server/dist/modules/auth-guard.js +73 -0
- package/server/dist/modules/auth-guard.js.map +1 -0
- package/server/dist/modules/auth-mode.js +51 -0
- package/server/dist/modules/auth-mode.js.map +1 -0
- package/server/dist/modules/auth.js +9 -0
- package/server/dist/modules/auth.js.map +1 -1
- package/server/dist/modules/blocknote.js +7 -1
- package/server/dist/modules/blocknote.js.map +1 -1
- package/server/dist/modules/error-handler.js +41 -0
- package/server/dist/modules/error-handler.js.map +1 -0
- package/server/dist/modules/image-upload.js +114 -0
- package/server/dist/modules/image-upload.js.map +1 -0
- package/server/dist/modules/mcp-admin.js +88 -0
- package/server/dist/modules/mcp-admin.js.map +1 -0
- package/server/dist/modules/mcp-auth.js +71 -0
- package/server/dist/modules/mcp-auth.js.map +1 -0
- package/server/dist/modules/mcp-token.js +28 -0
- package/server/dist/modules/mcp-token.js.map +1 -0
- package/server/dist/modules/note-authoring.js +140 -0
- package/server/dist/modules/note-authoring.js.map +1 -0
- package/server/dist/modules/note-cleanup.js +207 -0
- package/server/dist/modules/note-cleanup.js.map +1 -0
- package/server/dist/modules/note-snapshot.js +223 -0
- package/server/dist/modules/note-snapshot.js.map +1 -0
- package/server/dist/modules/note-trash.js +262 -0
- package/server/dist/modules/note-trash.js.map +1 -0
- package/server/dist/modules/prisma-runtime.js +46 -0
- package/server/dist/modules/prisma-runtime.js.map +1 -0
- package/server/dist/modules/recovery-retention.js +26 -0
- package/server/dist/modules/recovery-retention.js.map +1 -0
- package/server/dist/modules/remote-image.js +176 -0
- package/server/dist/modules/remote-image.js.map +1 -0
- package/server/dist/modules/tag-organization.js +66 -0
- package/server/dist/modules/tag-organization.js.map +1 -0
- package/server/dist/modules/use-async.js +2 -4
- package/server/dist/modules/use-async.js.map +1 -1
- package/server/dist/schema/note/index.js +150 -6
- package/server/dist/schema/note/index.js.map +1 -1
- package/server/dist/start.js +11 -0
- package/server/dist/start.js.map +1 -0
- package/server/dist/types/index.js.map +1 -1
- package/server/dist/urls.js +3 -2
- package/server/dist/urls.js.map +1 -1
- package/server/dist/views/auth.js +305 -0
- package/server/dist/views/auth.js.map +1 -0
- package/server/dist/views/image.js +24 -81
- package/server/dist/views/image.js.map +1 -1
- package/server/dist/views/index.js +4 -0
- package/server/dist/views/index.js.map +1 -1
- package/server/dist/views/mcp-admin.js +42 -0
- package/server/dist/views/mcp-admin.js.map +1 -0
- package/server/dist/views/note.js +124 -0
- package/server/dist/views/note.js.map +1 -0
- package/server/dist/views/tag.js +23 -0
- package/server/dist/views/tag.js.map +1 -0
- package/server/prisma/migrations/20260331183000_0010_note_snapshot/migration.sql +13 -0
- package/server/prisma/migrations/20260331195000_0011_note_trash_entities/migration.sql +35 -0
- package/server/prisma/migrations/20260331213000_0012_image_hash_unique/migration.sql +49 -0
- package/server/prisma/migrations/20260404000000_0013_mcp_admin_control/migration.sql +9 -0
- package/server/prisma/schema.prisma +66 -1
- package/server/client/dist/assets/index-BJgBFWwL.css +0 -1
- package/server/client/dist/assets/index-DvNRv9YC.js +0 -391
- package/server/client/dist/assets/note-vendor-CUVHa9LU.js +0 -54
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import{j as e}from"./note-vendor-BofYbzmZ.js";import{p as O,g as y,z as $,A as H,q as x,al as I,u as q,H as P,P as Z,B as f,Y as k,am as F,an as v,ao as Q,M as p,V as g,W as j,F as T,f as K,e as B,a8 as U}from"./index-40Y2UsVr.js";import{a as t}from"./graph-vendor-CUxe67Lr.js";import{C as z}from"./Callout-B2kPcDrq.js";import{n as X,M as Y}from"./ModalActionRow-CES9L_Gy.js";import{S as C}from"./SurfaceCard-yzVU4Qkx.js";import"./note-core-BCgMq5QA.js";const G=new Map([["bold",t.createElement(t.Fragment,null,t.createElement("path",{d:"M216.49,168.49a12,12,0,0,1-17,0L128,97,56.49,168.49a12,12,0,0,1-17-17l80-80a12,12,0,0,1,17,0l80,80A12,12,0,0,1,216.49,168.49Z"}))],["duotone",t.createElement(t.Fragment,null,t.createElement("path",{d:"M208,160H48l80-80Z",opacity:"0.2"}),t.createElement("path",{d:"M213.66,154.34l-80-80a8,8,0,0,0-11.32,0l-80,80A8,8,0,0,0,48,168H208a8,8,0,0,0,5.66-13.66ZM67.31,152,128,91.31,188.69,152Z"}))],["fill",t.createElement(t.Fragment,null,t.createElement("path",{d:"M215.39,163.06A8,8,0,0,1,208,168H48a8,8,0,0,1-5.66-13.66l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,215.39,163.06Z"}))],["light",t.createElement(t.Fragment,null,t.createElement("path",{d:"M212.24,164.24a6,6,0,0,1-8.48,0L128,88.49,52.24,164.24a6,6,0,0,1-8.48-8.48l80-80a6,6,0,0,1,8.48,0l80,80A6,6,0,0,1,212.24,164.24Z"}))],["regular",t.createElement(t.Fragment,null,t.createElement("path",{d:"M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z"}))],["thin",t.createElement(t.Fragment,null,t.createElement("path",{d:"M210.83,162.83a4,4,0,0,1-5.66,0L128,85.66,50.83,162.83a4,4,0,0,1-5.66-5.66l80-80a4,4,0,0,1,5.66,0l80,80A4,4,0,0,1,210.83,162.83Z"}))]]),S=t.forwardRef((l,r)=>t.createElement(O,{ref:r,...l,weights:G}));S.displayName="CaretUpIcon";const V=S,b=["id","name","template","replacement","createdAt","updatedAt"],W=new Set(b),J=`query FetchPlaceholders(
|
|
2
|
+
$searchFilter: SearchFilterInput,
|
|
3
|
+
$pagination: PaginationInput
|
|
4
|
+
) {
|
|
5
|
+
allPlaceholders(
|
|
6
|
+
searchFilter: $searchFilter,
|
|
7
|
+
pagination: $pagination
|
|
8
|
+
) {
|
|
9
|
+
totalCount
|
|
10
|
+
placeholders {
|
|
11
|
+
__PLACEHOLDER_FIELDS__ }
|
|
12
|
+
}
|
|
13
|
+
}`,ee=l=>{const r=l&&l.length>0?l.filter(n=>W.has(n)):b;return Array.from(new Set(r)).map(n=>" "+n).join(`
|
|
14
|
+
`)+`
|
|
15
|
+
`},te=async({limit:l=25,offset:r=0,query:d="",fields:n}={})=>{const h=ee(n),c=J.replace("__PLACEHOLDER_FIELDS__",h);return y(c,{searchFilter:{query:d},pagination:{limit:l,offset:r}})},ae=l=>y(`mutation CreatePlaceholder(
|
|
16
|
+
$name: String!,
|
|
17
|
+
$template: String!,
|
|
18
|
+
$replacement: String!
|
|
19
|
+
) {
|
|
20
|
+
createPlaceholder(
|
|
21
|
+
name: $name,
|
|
22
|
+
template: $template,
|
|
23
|
+
replacement: $replacement
|
|
24
|
+
) {
|
|
25
|
+
id
|
|
26
|
+
name
|
|
27
|
+
template
|
|
28
|
+
replacement
|
|
29
|
+
createdAt
|
|
30
|
+
updatedAt
|
|
31
|
+
}
|
|
32
|
+
}`,l),le=l=>y(`mutation DeletePlaceholder($id: ID!) {
|
|
33
|
+
deletePlaceholder(id: $id)
|
|
34
|
+
}`,{id:l}),L="p-4",A=B(U),me=()=>{const l=$(),r=H(),d=A.useNavigate(),{page:n}=A.useSearch(),[h,c]=t.useState(!1),[u,N]=t.useState(!1),[s,m]=t.useState({name:"",template:"",replacement:""}),i=25,_=x.placeholders.list({limit:i,offset:(n-1)*i}),[w]=t.useState(I),{data:o,isLoading:M}=q({queryKey:_,queryFn:async()=>{const a=await te({offset:(n-1)*i,limit:i});if(a.type==="error")throw a;return a.allPlaceholders}}),E=P({mutationFn:ae,onSuccess:async()=>{l("Placeholder added successfully"),c(!1),m({name:"",template:"",replacement:""}),await r.invalidateQueries({queryKey:x.placeholders.listAll(),exact:!1})}}),R=P({mutationFn:le,onSuccess:async()=>{l("Placeholder deleted successfully"),await r.invalidateQueries({queryKey:x.placeholders.listAll(),exact:!1})}});return e.jsxs(Z,{title:"Placeholders",variant:"subtle",description:"Manage template variables for note cloning",children:[e.jsx(z,{className:"mb-4",children:e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsx("span",{children:"Placeholders will be replaced with new note data during cloning."}),e.jsx(f,{variant:"subtle",size:"icon-sm",onClick:()=>c(!0),children:e.jsx(X,{width:20,height:20})})]})}),e.jsxs("div",{className:"mt-3 flex flex-col gap-3",children:[e.jsxs("button",{type:"button",onClick:()=>N(!u),className:"focus-ring-soft surface-base flex items-center justify-between gap-2 rounded-[18px] px-4 py-3 text-left text-sm font-semibold text-fg-default transition-colors hover:bg-hover-subtle",children:[e.jsx("div",{children:"System Placeholders"}),e.jsx("div",{className:"flex h-6 w-6 items-center justify-center text-fg-tertiary",children:u?e.jsx(V,{}):e.jsx(k,{})})]}),u&&e.jsx("div",{className:"grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3",children:w.map(a=>e.jsxs(C,{className:L,children:[e.jsx("div",{className:"text-sm font-semibold text-fg-default",children:a.name}),e.jsxs("div",{className:"text-xs font-medium text-fg-tertiary",children:[F,a.template,v," = '",a.replacement,"'"]})]},a.name))}),!M&&o?.placeholders&&o.placeholders.length>0&&e.jsx("div",{className:"grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3",children:o.placeholders.map(a=>e.jsxs(C,{className:`${L} relative`,children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx("div",{className:"text-sm font-semibold text-fg-default",children:a.name}),e.jsx("button",{type:"button",className:"focus-ring-soft inline-flex h-8 w-8 items-center justify-center rounded-[10px] text-fg-tertiary transition-colors hover:bg-hover-subtle hover:text-fg-default",onClick:()=>R.mutate(a.id.toString()),children:e.jsx(Q,{})})]}),e.jsxs("div",{className:"text-xs font-medium text-fg-tertiary",children:[F,a.template,v," = '",a.replacement,"'"]})]},a.id))})]}),e.jsxs(p,{isOpen:h,onClose:()=>c(!1),children:[e.jsx(p.Header,{title:"Add Placeholder",onClose:()=>c(!1)}),e.jsx(p.Body,{children:e.jsxs("div",{className:"flex flex-col gap-3",children:[e.jsx(g,{htmlFor:"name",children:"Name:"}),e.jsx(j,{id:"name",type:"text",value:s.name,onChange:a=>m({...s,name:a.target.value}),placeholder:"Description of this placeholder"}),e.jsx(g,{htmlFor:"template",children:"Placeholder:"}),e.jsx(j,{id:"template",type:"text",value:s.template,onChange:a=>m({...s,template:a.target.value}),placeholder:"note_app"}),e.jsx(g,{htmlFor:"replacement",children:"Replacement:"}),e.jsx(j,{id:"replacement",type:"text",value:s.replacement,onChange:a=>m({...s,replacement:a.target.value}),placeholder:"Ocean Brain"})]})}),e.jsx(p.Footer,{children:e.jsxs(Y,{children:[e.jsx(f,{variant:"ghost",onClick:()=>c(!1),children:"Cancel"}),e.jsx(f,{variant:"primary",isLoading:E.isPending,onClick:()=>{if(!s.name||!s.template||!s.replacement){l("All fields are required");return}E.mutate({name:s.name,template:s.template,replacement:s.replacement})},children:"Add"})]})})]}),e.jsx(T,{fallback:null,children:o?.totalCount&&i<o.totalCount&&e.jsx(K,{page:n,last:Math.ceil(o.totalCount/i),onChange:a=>{d({search:D=>({...D,page:a})})}})})]})};export{me as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{j as e}from"./note-vendor-BofYbzmZ.js";import{P as h,Q as p,S as d,e as f,a1 as y,A as g,z as j,k as b,ac as v,q as i,H as N,ad as T,N as S,F as q,a2 as E,B as R,f as Q,E as w,d as C}from"./index-40Y2UsVr.js";import"./graph-vendor-CUxe67Lr.js";import{S as K}from"./SurfaceCard-yzVU4Qkx.js";import"./note-core-BCgMq5QA.js";const a=25,c=f(y),x=t=>C(t).format("YYYY-MM-DD HH:mm"),k=()=>{const{page:t}=c.useSearch(),u=c.useNavigate(),r=g(),m=j(),{data:l}=b({queryKey:i.notes.trash({limit:a,offset:(t-1)*a}),queryFn:async()=>{const s=await v({limit:a,offset:(t-1)*a});if(s.type==="error")throw s;return s.trashedNotes}}),o=N({mutationFn:T,onSuccess:async s=>{if(s.type==="error")throw s;await Promise.all([r.invalidateQueries({queryKey:i.notes.all(),exact:!1}),r.invalidateQueries({queryKey:i.tags.all(),exact:!1}),r.invalidateQueries({queryKey:i.reminders.all(),exact:!1}),r.invalidateQueries({queryKey:i.images.all(),exact:!1}),r.invalidateQueries({queryKey:["calendar"],exact:!1})]),m("The note has been restored."),u({to:S,params:{id:s.restoreTrashedNote.id}})},onError:()=>{m("Failed to restore the note.")}});return e.jsx(q,{fallback:e.jsx(w,{title:"Trash is empty",description:"Deleted notes stay here until you restore them or retention cleanup removes them."}),children:l.notes.length>0&&e.jsxs(e.Fragment,{children:[e.jsx("div",{className:"grid gap-4",children:l.notes.map(s=>e.jsx(K,{className:"p-4",children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"space-y-2",children:[e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("h2",{className:"text-base font-semibold text-fg-default",children:s.title}),s.pinned&&e.jsxs("span",{className:"inline-flex items-center gap-1 rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5 text-xs font-medium text-fg-secondary",children:[e.jsx(E,{className:"h-3.5 w-3.5"}),"Pinned"]})]}),e.jsxs("div",{className:"text-sm text-fg-secondary",children:["Deleted ",x(s.deletedAt)]}),e.jsxs("div",{className:"text-xs text-fg-tertiary",children:["Last edited ",x(s.updatedAt)]}),s.tagNames.length>0&&e.jsx("div",{className:"flex flex-wrap gap-2",children:s.tagNames.map(n=>e.jsx("span",{className:"rounded-full border border-border-subtle bg-hover-subtle px-2 py-0.5 text-xs font-medium text-fg-secondary",children:n},`${s.id}-${n}`))})]}),e.jsx(R,{variant:"subtle",isLoading:o.isPending&&o.variables===s.id,onClick:()=>o.mutate(s.id),children:"Restore"})]})},s.id))}),l.totalCount>a&&e.jsx(Q,{page:t,last:Math.ceil(l.totalCount/a),onChange:s=>{u({search:n=>({...n,page:s})})}})]})})},H=()=>{const{page:t}=c.useSearch();return e.jsx(h,{title:"Trash",variant:"subtle",description:"Restore notes removed from your active workspace before retention cleanup clears them",children:e.jsx(p,{fallback:e.jsxs("div",{className:"grid gap-4",children:[e.jsx(d,{height:"128px"}),e.jsx(d,{height:"128px"}),e.jsx(d,{height:"128px"})]}),errorTitle:"Failed to load trash",errorDescription:"Retry loading deleted notes.",resetKeys:[t],children:e.jsx(k,{})})})};export{H as default};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import{j as y}from"./note-vendor-BofYbzmZ.js";import{g as u,k as f,q as n,z as R,A as $}from"./index-40Y2UsVr.js";import{a as l}from"./graph-vendor-CUxe67Lr.js";const g=(e={})=>({limit:e.limit??10,offset:e.offset??0}),D=async(e,t)=>u(`
|
|
2
|
+
query FetchNoteReminders($noteId: ID!, $pagination: PaginationInput) {
|
|
3
|
+
noteReminders(noteId: $noteId, pagination: $pagination) {
|
|
4
|
+
totalCount
|
|
5
|
+
reminders {
|
|
6
|
+
id
|
|
7
|
+
noteId
|
|
8
|
+
reminderDate
|
|
9
|
+
priority
|
|
10
|
+
content
|
|
11
|
+
completed
|
|
12
|
+
createdAt
|
|
13
|
+
updatedAt
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
`,{noteId:e,pagination:g(t)}),A=async e=>u(`
|
|
18
|
+
query FetchUpcomingReminders($pagination: PaginationInput) {
|
|
19
|
+
upcomingReminders(pagination: $pagination) {
|
|
20
|
+
totalCount
|
|
21
|
+
reminders {
|
|
22
|
+
id
|
|
23
|
+
noteId
|
|
24
|
+
reminderDate
|
|
25
|
+
priority
|
|
26
|
+
content
|
|
27
|
+
completed
|
|
28
|
+
createdAt
|
|
29
|
+
updatedAt
|
|
30
|
+
note {
|
|
31
|
+
id
|
|
32
|
+
title
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
`,{pagination:g(e)}),q=async e=>{const{noteId:t,reminderDate:a,priority:o="medium",content:i}=e;return u(`
|
|
38
|
+
mutation CreateReminder($noteId: ID!, $reminderDate: String!, $priority: ReminderPriority, $content: String) {
|
|
39
|
+
createReminder(
|
|
40
|
+
noteId: $noteId,
|
|
41
|
+
reminderDate: $reminderDate,
|
|
42
|
+
priority: $priority,
|
|
43
|
+
content: $content
|
|
44
|
+
) {
|
|
45
|
+
id
|
|
46
|
+
noteId
|
|
47
|
+
reminderDate
|
|
48
|
+
completed
|
|
49
|
+
priority
|
|
50
|
+
createdAt
|
|
51
|
+
updatedAt
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`,{noteId:t,reminderDate:a.toISOString(),priority:o,content:i})},I=async({id:e,reminderDate:t,completed:a,priority:o,content:i})=>{const r={id:e};return t&&(r.reminderDate=t.toISOString()),a!==void 0&&(r.completed=a),o&&(r.priority=o),i!==void 0&&(r.content=i),u(`
|
|
55
|
+
mutation UpdateReminder(
|
|
56
|
+
$id: ID!,
|
|
57
|
+
$reminderDate: String,
|
|
58
|
+
$completed: Boolean,
|
|
59
|
+
$priority: ReminderPriority,
|
|
60
|
+
$content: String
|
|
61
|
+
) {
|
|
62
|
+
updateReminder(
|
|
63
|
+
id: $id,
|
|
64
|
+
reminderDate: $reminderDate,
|
|
65
|
+
completed: $completed,
|
|
66
|
+
priority: $priority,
|
|
67
|
+
content: $content
|
|
68
|
+
) {
|
|
69
|
+
id
|
|
70
|
+
noteId
|
|
71
|
+
reminderDate
|
|
72
|
+
completed
|
|
73
|
+
priority
|
|
74
|
+
createdAt
|
|
75
|
+
updatedAt
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
`,r)},x=async e=>u(`
|
|
79
|
+
mutation DeleteReminder($id: ID!) {
|
|
80
|
+
deleteReminder(id: $id)
|
|
81
|
+
}
|
|
82
|
+
`,{id:e});function P({noteId:e,searchParams:t,render:a}){const{data:o}=f({queryKey:e?n.reminders.note(e,t):n.reminders.upcoming(t),queryFn:async()=>{if(e){const r=await D(e,t);if(r.type==="error")throw r;return r.noteReminders}const i=await A(t);if(i.type==="error")throw i;return i.upcomingReminders}});return y.jsx(y.Fragment,{children:a(o)})}function w(){const e=R(),t=$(),a=l.useCallback(async(r,m,d="medium",s,c)=>{const p=await q({noteId:r,reminderDate:m,priority:d,content:c});if(p.type==="error"){e(p.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(r),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),o=l.useCallback(async(r,m,d,s)=>{const c=await I({id:r,...d});if(c.type==="error"){e(c.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),s&&s()},[t,e]),i=l.useCallback(async(r,m,d)=>{const s=await x(r);if(s.type==="error"){e(s.errors[0].message);return}await Promise.all([t.invalidateQueries({queryKey:n.reminders.noteAllPages(m),exact:!1}),t.invalidateQueries({queryKey:n.reminders.upcomingAllPages(),exact:!1}),t.invalidateQueries({queryKey:n.reminders.inDateRangeAll(),exact:!1})]),d&&d()},[t,e]);return{onCreate:a,onUpdate:o,onDelete:i}}export{P as R,w as u};
|
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
<link rel="icon" type="image/png" href="/icon.png" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Ocean Brain</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/
|
|
11
|
-
<link rel="modulepreload" crossorigin href="/assets/note-
|
|
12
|
-
<link rel="
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-40Y2UsVr.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-CUxe67Lr.js">
|
|
11
|
+
<link rel="modulepreload" crossorigin href="/assets/note-core-BCgMq5QA.js">
|
|
12
|
+
<link rel="modulepreload" crossorigin href="/assets/note-vendor-BofYbzmZ.js">
|
|
13
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BnxpTwtL.css">
|
|
13
14
|
</head>
|
|
14
15
|
|
|
15
16
|
<body>
|
package/server/dist/app.js
CHANGED
|
@@ -1,18 +1,106 @@
|
|
|
1
1
|
import express from "express";
|
|
2
|
-
import
|
|
2
|
+
import path from "path";
|
|
3
3
|
import { createHandler } from "graphql-http/lib/use/express";
|
|
4
4
|
import logger from "./modules/logger.js";
|
|
5
5
|
import { paths } from "./paths.js";
|
|
6
6
|
import schema from "./schema/index.js";
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
import { createApiRouter } from "./urls.js";
|
|
8
|
+
import { createSessionMiddleware, isAuthenticatedRequest, requireSessionForGraphql } from "./modules/auth-guard.js";
|
|
9
|
+
import {
|
|
10
|
+
createMcpAuthMiddleware,
|
|
11
|
+
createReadOnlyMcpValidationRule
|
|
12
|
+
} from "./modules/mcp-auth.js";
|
|
13
|
+
import { createMcpAdminService } from "./modules/mcp-admin.js";
|
|
14
|
+
import {
|
|
15
|
+
createLoginPageHandler,
|
|
16
|
+
createLoginPageSubmitHandler,
|
|
17
|
+
createLogoutPageHandler
|
|
18
|
+
} from "./views/auth.js";
|
|
19
|
+
import useAsync from "./modules/use-async.js";
|
|
20
|
+
import { createErrorHandler } from "./modules/error-handler.js";
|
|
21
|
+
import {
|
|
22
|
+
createMcpCreateNoteHandler,
|
|
23
|
+
createMcpDeleteNoteHandler,
|
|
24
|
+
createMcpUpdateNoteHandler
|
|
25
|
+
} from "./views/note.js";
|
|
26
|
+
import { createMcpCreateTagHandler } from "./views/tag.js";
|
|
27
|
+
import { purgeExpiredNoteSnapshots } from "./modules/note-snapshot.js";
|
|
28
|
+
import { purgeExpiredTrashedNotes } from "./modules/note-trash.js";
|
|
29
|
+
const shouldBlockClientRoute = (authConfig, requestPath, authenticated) => {
|
|
30
|
+
if (authConfig.mode !== "password" || authenticated) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (requestPath.startsWith("/api") || requestPath.startsWith("/graphql") || requestPath.startsWith("/auth")) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return path.extname(requestPath) === "";
|
|
37
|
+
};
|
|
38
|
+
const createApp = (authConfig) => {
|
|
39
|
+
const mcpAdminService = createMcpAdminService();
|
|
40
|
+
return createAppWithMcpAuth(authConfig, mcpAdminService);
|
|
41
|
+
};
|
|
42
|
+
const createAppWithMcpAuth = (authConfig, mcpAdminService) => {
|
|
43
|
+
const app = express();
|
|
44
|
+
app.locals.authConfig = authConfig;
|
|
45
|
+
void Promise.all([
|
|
46
|
+
purgeExpiredNoteSnapshots(),
|
|
47
|
+
purgeExpiredTrashedNotes()
|
|
48
|
+
]).catch((error) => {
|
|
49
|
+
const message = error instanceof Error ? error.message : "Unknown recovery cleanup error";
|
|
50
|
+
process.stderr.write(`[recovery] Startup cleanup failed: ${message}
|
|
51
|
+
`);
|
|
52
|
+
});
|
|
53
|
+
app.use(logger).use(createSessionMiddleware(authConfig)).use(express.urlencoded({ extended: false })).use(express.json({ limit: "50mb" })).use("/assets/images/", express.static(paths.imageDir)).post(
|
|
54
|
+
"/api/mcp/notes/create",
|
|
55
|
+
createMcpAuthMiddleware(authConfig, mcpAdminService),
|
|
56
|
+
useAsync(createMcpCreateNoteHandler())
|
|
57
|
+
).post(
|
|
58
|
+
"/api/mcp/notes/update",
|
|
59
|
+
createMcpAuthMiddleware(authConfig, mcpAdminService),
|
|
60
|
+
useAsync(createMcpUpdateNoteHandler())
|
|
61
|
+
).post(
|
|
62
|
+
"/api/mcp/tags/create",
|
|
63
|
+
createMcpAuthMiddleware(authConfig, mcpAdminService),
|
|
64
|
+
useAsync(createMcpCreateTagHandler())
|
|
65
|
+
).post(
|
|
66
|
+
"/api/mcp/notes/delete",
|
|
67
|
+
createMcpAuthMiddleware(authConfig, mcpAdminService),
|
|
68
|
+
useAsync(createMcpDeleteNoteHandler())
|
|
69
|
+
).use("/api", createApiRouter(authConfig, mcpAdminService)).get("/auth/login", createLoginPageHandler(authConfig)).post("/auth/login", createLoginPageSubmitHandler(authConfig)).post("/auth/logout", createLogoutPageHandler(authConfig)).use("/graphql/mcp", createMcpAuthMiddleware(authConfig, mcpAdminService), createHandler({
|
|
70
|
+
schema,
|
|
71
|
+
context: (req) => ({
|
|
72
|
+
authMode: authConfig.mode,
|
|
73
|
+
isAuthenticated: isAuthenticatedRequest(req.raw),
|
|
74
|
+
req: req.raw,
|
|
75
|
+
res: req.context.res
|
|
76
|
+
}),
|
|
77
|
+
validationRules: (_req, _args, specifiedRules) => {
|
|
78
|
+
return [...specifiedRules, createReadOnlyMcpValidationRule()];
|
|
79
|
+
}
|
|
80
|
+
})).use("/graphql", requireSessionForGraphql(authConfig), createHandler({
|
|
81
|
+
schema,
|
|
82
|
+
context: (req) => ({
|
|
83
|
+
authMode: authConfig.mode,
|
|
84
|
+
isAuthenticated: isAuthenticatedRequest(req.raw),
|
|
85
|
+
req: req.raw,
|
|
86
|
+
res: req.context.res
|
|
87
|
+
})
|
|
88
|
+
})).use((req, res, next) => {
|
|
89
|
+
if (shouldBlockClientRoute(authConfig, req.path, isAuthenticatedRequest(req))) {
|
|
90
|
+
const redirectPath = encodeURIComponent(req.originalUrl || "/");
|
|
91
|
+
res.redirect(303, `/auth/login?next=${redirectPath}`);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
next();
|
|
95
|
+
}).use(express.static(paths.clientDist, { extensions: ["html"] })).get(/.*/, (_req, res) => {
|
|
96
|
+
res.sendFile(paths.clientIndex);
|
|
97
|
+
}).use(createErrorHandler());
|
|
98
|
+
return app;
|
|
99
|
+
};
|
|
100
|
+
var app_default = createApp;
|
|
15
101
|
export {
|
|
102
|
+
createApp,
|
|
103
|
+
createAppWithMcpAuth,
|
|
16
104
|
app_default as default
|
|
17
105
|
};
|
|
18
106
|
//# sourceMappingURL=app.js.map
|
package/server/dist/app.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/app.ts"],"sourcesContent":["import express from 'express';\nimport
|
|
1
|
+
{"version":3,"sources":["../src/app.ts"],"sourcesContent":["import express from 'express';\nimport path from 'path';\nimport { createHandler } from 'graphql-http/lib/use/express';\n\nimport logger from './modules/logger.js';\nimport { paths } from './paths.js';\nimport schema from './schema/index.js';\nimport { createApiRouter } from './urls.js';\nimport { createSessionMiddleware, isAuthenticatedRequest, requireSessionForGraphql } from './modules/auth-guard.js';\nimport type { AuthConfig } from './modules/auth-mode.js';\nimport {\n createMcpAuthMiddleware,\n createReadOnlyMcpValidationRule\n} from './modules/mcp-auth.js';\nimport { createMcpAdminService, type McpAdminService } from './modules/mcp-admin.js';\nimport {\n createLoginPageHandler,\n createLoginPageSubmitHandler,\n createLogoutPageHandler\n} from './views/auth.js';\nimport useAsync from './modules/use-async.js';\nimport { createErrorHandler } from './modules/error-handler.js';\nimport {\n createMcpCreateNoteHandler,\n createMcpDeleteNoteHandler,\n createMcpUpdateNoteHandler\n} from './views/note.js';\nimport { createMcpCreateTagHandler } from './views/tag.js';\nimport { purgeExpiredNoteSnapshots } from './modules/note-snapshot.js';\nimport { purgeExpiredTrashedNotes } from './modules/note-trash.js';\n\nconst shouldBlockClientRoute = (authConfig: AuthConfig, requestPath: string, authenticated: boolean) => {\n if (authConfig.mode !== 'password' || authenticated) {\n return false;\n }\n\n if (requestPath.startsWith('/api') || requestPath.startsWith('/graphql') || requestPath.startsWith('/auth')) {\n return false;\n }\n\n return path.extname(requestPath) === '';\n};\n\nexport const createApp = (authConfig: AuthConfig) => {\n const mcpAdminService = createMcpAdminService();\n return createAppWithMcpAuth(authConfig, mcpAdminService);\n};\n\nexport const createAppWithMcpAuth = (authConfig: AuthConfig, mcpAdminService: McpAdminService) => {\n const app = express();\n app.locals.authConfig = authConfig;\n\n void Promise.all([\n purgeExpiredNoteSnapshots(),\n purgeExpiredTrashedNotes()\n ]).catch((error) => {\n const message = error instanceof Error ? error.message : 'Unknown recovery cleanup error';\n process.stderr.write(`[recovery] Startup cleanup failed: ${message}\\n`);\n });\n\n app.use(logger)\n .use(createSessionMiddleware(authConfig))\n .use(express.urlencoded({ extended: false }))\n .use(express.json({ limit: '50mb' }))\n .use('/assets/images/', express.static(paths.imageDir))\n .post(\n '/api/mcp/notes/create',\n createMcpAuthMiddleware(authConfig, mcpAdminService),\n useAsync(createMcpCreateNoteHandler())\n )\n .post(\n '/api/mcp/notes/update',\n createMcpAuthMiddleware(authConfig, mcpAdminService),\n useAsync(createMcpUpdateNoteHandler())\n )\n .post(\n '/api/mcp/tags/create',\n createMcpAuthMiddleware(authConfig, mcpAdminService),\n useAsync(createMcpCreateTagHandler())\n )\n .post(\n '/api/mcp/notes/delete',\n createMcpAuthMiddleware(authConfig, mcpAdminService),\n useAsync(createMcpDeleteNoteHandler())\n )\n .use('/api', createApiRouter(authConfig, mcpAdminService))\n .get('/auth/login', createLoginPageHandler(authConfig))\n .post('/auth/login', createLoginPageSubmitHandler(authConfig))\n .post('/auth/logout', createLogoutPageHandler(authConfig))\n .use('/graphql/mcp', createMcpAuthMiddleware(authConfig, mcpAdminService), createHandler({\n schema,\n context: (req) => ({\n authMode: authConfig.mode,\n isAuthenticated: isAuthenticatedRequest(req.raw),\n req: req.raw,\n res: req.context.res\n }),\n validationRules: (_req, _args, specifiedRules) => {\n return [...specifiedRules, createReadOnlyMcpValidationRule()];\n }\n }))\n .use('/graphql', requireSessionForGraphql(authConfig), createHandler({\n schema,\n context: (req) => ({\n authMode: authConfig.mode,\n isAuthenticated: isAuthenticatedRequest(req.raw),\n req: req.raw,\n res: req.context.res\n })\n }))\n .use((req, res, next) => {\n if (shouldBlockClientRoute(authConfig, req.path, isAuthenticatedRequest(req))) {\n const redirectPath = encodeURIComponent(req.originalUrl || '/');\n res.redirect(303, `/auth/login?next=${redirectPath}`);\n return;\n }\n\n next();\n })\n .use(express.static(paths.clientDist, { extensions: ['html'] }))\n .get(/.*/, (_req, res) => {\n res.sendFile(paths.clientIndex);\n })\n .use(createErrorHandler());\n\n return app;\n};\n\nexport default createApp;\n"],"mappings":"AAAA,OAAO,aAAa;AACpB,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,OAAO,YAAY;AACnB,SAAS,aAAa;AACtB,OAAO,YAAY;AACnB,SAAS,uBAAuB;AAChC,SAAS,yBAAyB,wBAAwB,gCAAgC;AAE1F;AAAA,EACI;AAAA,EACA;AAAA,OACG;AACP,SAAS,6BAAmD;AAC5D;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,OAAO,cAAc;AACrB,SAAS,0BAA0B;AACnC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,iCAAiC;AAC1C,SAAS,iCAAiC;AAC1C,SAAS,gCAAgC;AAEzC,MAAM,yBAAyB,CAAC,YAAwB,aAAqB,kBAA2B;AACpG,MAAI,WAAW,SAAS,cAAc,eAAe;AACjD,WAAO;AAAA,EACX;AAEA,MAAI,YAAY,WAAW,MAAM,KAAK,YAAY,WAAW,UAAU,KAAK,YAAY,WAAW,OAAO,GAAG;AACzG,WAAO;AAAA,EACX;AAEA,SAAO,KAAK,QAAQ,WAAW,MAAM;AACzC;AAEO,MAAM,YAAY,CAAC,eAA2B;AACjD,QAAM,kBAAkB,sBAAsB;AAC9C,SAAO,qBAAqB,YAAY,eAAe;AAC3D;AAEO,MAAM,uBAAuB,CAAC,YAAwB,oBAAqC;AAC9F,QAAM,MAAM,QAAQ;AACpB,MAAI,OAAO,aAAa;AAExB,OAAK,QAAQ,IAAI;AAAA,IACb,0BAA0B;AAAA,IAC1B,yBAAyB;AAAA,EAC7B,CAAC,EAAE,MAAM,CAAC,UAAU;AAChB,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,YAAQ,OAAO,MAAM,sCAAsC,OAAO;AAAA,CAAI;AAAA,EAC1E,CAAC;AAED,MAAI,IAAI,MAAM,EACT,IAAI,wBAAwB,UAAU,CAAC,EACvC,IAAI,QAAQ,WAAW,EAAE,UAAU,MAAM,CAAC,CAAC,EAC3C,IAAI,QAAQ,KAAK,EAAE,OAAO,OAAO,CAAC,CAAC,EACnC,IAAI,mBAAmB,QAAQ,OAAO,MAAM,QAAQ,CAAC,EACrD;AAAA,IACG;AAAA,IACA,wBAAwB,YAAY,eAAe;AAAA,IACnD,SAAS,2BAA2B,CAAC;AAAA,EACzC,EACC;AAAA,IACG;AAAA,IACA,wBAAwB,YAAY,eAAe;AAAA,IACnD,SAAS,2BAA2B,CAAC;AAAA,EACzC,EACC;AAAA,IACG;AAAA,IACA,wBAAwB,YAAY,eAAe;AAAA,IACnD,SAAS,0BAA0B,CAAC;AAAA,EACxC,EACC;AAAA,IACG;AAAA,IACA,wBAAwB,YAAY,eAAe;AAAA,IACnD,SAAS,2BAA2B,CAAC;AAAA,EACzC,EACC,IAAI,QAAQ,gBAAgB,YAAY,eAAe,CAAC,EACxD,IAAI,eAAe,uBAAuB,UAAU,CAAC,EACrD,KAAK,eAAe,6BAA6B,UAAU,CAAC,EAC5D,KAAK,gBAAgB,wBAAwB,UAAU,CAAC,EACxD,IAAI,gBAAgB,wBAAwB,YAAY,eAAe,GAAG,cAAc;AAAA,IACrF;AAAA,IACA,SAAS,CAAC,SAAS;AAAA,MACf,UAAU,WAAW;AAAA,MACrB,iBAAiB,uBAAuB,IAAI,GAAG;AAAA,MAC/C,KAAK,IAAI;AAAA,MACT,KAAK,IAAI,QAAQ;AAAA,IACrB;AAAA,IACA,iBAAiB,CAAC,MAAM,OAAO,mBAAmB;AAC9C,aAAO,CAAC,GAAG,gBAAgB,gCAAgC,CAAC;AAAA,IAChE;AAAA,EACJ,CAAC,CAAC,EACD,IAAI,YAAY,yBAAyB,UAAU,GAAG,cAAc;AAAA,IACjE;AAAA,IACA,SAAS,CAAC,SAAS;AAAA,MACf,UAAU,WAAW;AAAA,MACrB,iBAAiB,uBAAuB,IAAI,GAAG;AAAA,MAC/C,KAAK,IAAI;AAAA,MACT,KAAK,IAAI,QAAQ;AAAA,IACrB;AAAA,EACJ,CAAC,CAAC,EACD,IAAI,CAAC,KAAK,KAAK,SAAS;AACrB,QAAI,uBAAuB,YAAY,IAAI,MAAM,uBAAuB,GAAG,CAAC,GAAG;AAC3E,YAAM,eAAe,mBAAmB,IAAI,eAAe,GAAG;AAC9D,UAAI,SAAS,KAAK,oBAAoB,YAAY,EAAE;AACpD;AAAA,IACJ;AAEA,SAAK;AAAA,EACT,CAAC,EACA,IAAI,QAAQ,OAAO,MAAM,YAAY,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,EAC9D,IAAI,MAAM,CAAC,MAAM,QAAQ;AACtB,QAAI,SAAS,MAAM,WAAW;AAAA,EAClC,CAAC,EACA,IAAI,mBAAmB,CAAC;AAE7B,SAAO;AACX;AAEA,IAAO,cAAQ;","names":[]}
|
package/server/dist/main.js
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createApp } from "./app.js";
|
|
2
|
+
import { logAuthConfig, resolveAuthConfig } from "./modules/auth-mode.js";
|
|
2
3
|
const PORT = Number(process.env.PORT || 6683);
|
|
3
4
|
const HOST = process.env.HOST || "0.0.0.0";
|
|
4
|
-
|
|
5
|
+
try {
|
|
6
|
+
const authConfig = resolveAuthConfig(process.env);
|
|
7
|
+
logAuthConfig(authConfig);
|
|
8
|
+
const app = createApp(authConfig);
|
|
9
|
+
app.listen(PORT, HOST, () => {
|
|
10
|
+
process.stdout.write(`http server listen on ${HOST}:${PORT} (auth: ${authConfig.mode})
|
|
11
|
+
`);
|
|
12
|
+
});
|
|
13
|
+
} catch (error) {
|
|
14
|
+
const message = error instanceof Error ? error.message : "Unknown auth configuration error";
|
|
15
|
+
process.stderr.write(`[auth] Startup failed: ${message}
|
|
16
|
+
`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
5
19
|
//# sourceMappingURL=main.js.map
|
package/server/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/main.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../src/main.ts"],"sourcesContent":["import { createApp } from './app.js';\nimport { logAuthConfig, resolveAuthConfig } from './modules/auth-mode.js';\n\nconst PORT = Number(process.env.PORT || 6683);\nconst HOST = process.env.HOST || '0.0.0.0';\n\ntry {\n const authConfig = resolveAuthConfig(process.env);\n\n logAuthConfig(authConfig);\n\n const app = createApp(authConfig);\n\n app.listen(PORT, HOST, () => {\n process.stdout.write(`http server listen on ${HOST}:${PORT} (auth: ${authConfig.mode})\\n`);\n });\n} catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown auth configuration error';\n process.stderr.write(`[auth] Startup failed: ${message}\\n`);\n process.exit(1);\n}\n"],"mappings":"AAAA,SAAS,iBAAiB;AAC1B,SAAS,eAAe,yBAAyB;AAEjD,MAAM,OAAO,OAAO,QAAQ,IAAI,QAAQ,IAAI;AAC5C,MAAM,OAAO,QAAQ,IAAI,QAAQ;AAEjC,IAAI;AACA,QAAM,aAAa,kBAAkB,QAAQ,GAAG;AAEhD,gBAAc,UAAU;AAExB,QAAM,MAAM,UAAU,UAAU;AAEhC,MAAI,OAAO,MAAM,MAAM,MAAM;AACzB,YAAQ,OAAO,MAAM,yBAAyB,IAAI,IAAI,IAAI,WAAW,WAAW,IAAI;AAAA,CAAK;AAAA,EAC7F,CAAC;AACL,SAAS,OAAO;AACZ,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,UAAQ,OAAO,MAAM,0BAA0B,OAAO;AAAA,CAAI;AAC1D,UAAQ,KAAK,CAAC;AAClB;","names":[]}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import session from "express-session";
|
|
2
|
+
import { GraphQLError } from "graphql";
|
|
3
|
+
const JSON_HEADERS = { "Content-Type": "application/json" };
|
|
4
|
+
const buildUnauthorizedPayload = () => ({
|
|
5
|
+
code: "UNAUTHORIZED",
|
|
6
|
+
message: "Authentication required"
|
|
7
|
+
});
|
|
8
|
+
const buildUnauthorizedGraphqlPayload = () => ({
|
|
9
|
+
errors: [{
|
|
10
|
+
message: "Authentication required",
|
|
11
|
+
extensions: { code: "UNAUTHORIZED" }
|
|
12
|
+
}]
|
|
13
|
+
});
|
|
14
|
+
const isAuthenticatedRequest = (req) => Boolean(req.session?.authenticated);
|
|
15
|
+
const createSessionMiddleware = (authConfig) => {
|
|
16
|
+
if (authConfig.mode !== "password" || !authConfig.sessionSecret) {
|
|
17
|
+
return (_req, _res, next) => next();
|
|
18
|
+
}
|
|
19
|
+
return session({
|
|
20
|
+
secret: authConfig.sessionSecret,
|
|
21
|
+
name: "ocean-brain.sid",
|
|
22
|
+
resave: false,
|
|
23
|
+
saveUninitialized: false,
|
|
24
|
+
cookie: {
|
|
25
|
+
httpOnly: true,
|
|
26
|
+
sameSite: "lax"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const requireSessionForWrite = (authConfig) => {
|
|
31
|
+
return (req, res, next) => {
|
|
32
|
+
if (authConfig.mode === "disabled" || isAuthenticatedRequest(req)) {
|
|
33
|
+
next();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
res.status(401).set(JSON_HEADERS).json(buildUnauthorizedPayload()).end();
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
const requireSessionForGraphql = (authConfig) => {
|
|
40
|
+
return (req, res, next) => {
|
|
41
|
+
if (authConfig.mode === "disabled" || isAuthenticatedRequest(req)) {
|
|
42
|
+
next();
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
res.status(401).set(JSON_HEADERS).json(buildUnauthorizedGraphqlPayload()).end();
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const createMutationAuthValidationRule = () => {
|
|
49
|
+
return (context) => {
|
|
50
|
+
return {
|
|
51
|
+
OperationDefinition(node) {
|
|
52
|
+
if (node.operation !== "mutation") {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
context.reportError(new GraphQLError(
|
|
56
|
+
"Authentication required",
|
|
57
|
+
{
|
|
58
|
+
nodes: [node],
|
|
59
|
+
extensions: { code: "UNAUTHORIZED" }
|
|
60
|
+
}
|
|
61
|
+
));
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
export {
|
|
67
|
+
createMutationAuthValidationRule,
|
|
68
|
+
createSessionMiddleware,
|
|
69
|
+
isAuthenticatedRequest,
|
|
70
|
+
requireSessionForGraphql,
|
|
71
|
+
requireSessionForWrite
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=auth-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/auth-guard.ts"],"sourcesContent":["import session from 'express-session';\nimport type { NextFunction, Request, Response, RequestHandler } from 'express';\nimport type { ValidationRule } from 'graphql';\nimport { GraphQLError } from 'graphql';\n\nimport type { AuthConfig } from './auth-mode.js';\n\nconst JSON_HEADERS = { 'Content-Type': 'application/json' };\n\nconst buildUnauthorizedPayload = () => ({\n code: 'UNAUTHORIZED',\n message: 'Authentication required'\n});\n\nconst buildUnauthorizedGraphqlPayload = () => ({\n errors: [{\n message: 'Authentication required',\n extensions: { code: 'UNAUTHORIZED' }\n }]\n});\n\nexport const isAuthenticatedRequest = (req: Request) => Boolean(req.session?.authenticated);\n\nexport const createSessionMiddleware = (authConfig: AuthConfig): RequestHandler => {\n if (authConfig.mode !== 'password' || !authConfig.sessionSecret) {\n return (_req, _res, next) => next();\n }\n\n return session({\n secret: authConfig.sessionSecret,\n name: 'ocean-brain.sid',\n resave: false,\n saveUninitialized: false,\n cookie: {\n httpOnly: true,\n sameSite: 'lax'\n }\n });\n};\n\nexport const requireSessionForWrite = (authConfig: AuthConfig): RequestHandler => {\n return (req: Request, res: Response, next: NextFunction) => {\n if (authConfig.mode === 'disabled' || isAuthenticatedRequest(req)) {\n next();\n return;\n }\n\n res\n .status(401)\n .set(JSON_HEADERS)\n .json(buildUnauthorizedPayload())\n .end();\n };\n};\n\nexport const requireSessionForGraphql = (authConfig: AuthConfig): RequestHandler => {\n return (req: Request, res: Response, next: NextFunction) => {\n if (authConfig.mode === 'disabled' || isAuthenticatedRequest(req)) {\n next();\n return;\n }\n\n res\n .status(401)\n .set(JSON_HEADERS)\n .json(buildUnauthorizedGraphqlPayload())\n .end();\n };\n};\n\nexport const createMutationAuthValidationRule = (): ValidationRule => {\n return (context) => {\n return {\n OperationDefinition(node) {\n if (node.operation !== 'mutation') {\n return;\n }\n\n context.reportError(new GraphQLError(\n 'Authentication required',\n {\n nodes: [node],\n extensions: { code: 'UNAUTHORIZED' }\n }\n ));\n }\n };\n };\n};\n"],"mappings":"AAAA,OAAO,aAAa;AAGpB,SAAS,oBAAoB;AAI7B,MAAM,eAAe,EAAE,gBAAgB,mBAAmB;AAE1D,MAAM,2BAA2B,OAAO;AAAA,EACpC,MAAM;AAAA,EACN,SAAS;AACb;AAEA,MAAM,kCAAkC,OAAO;AAAA,EAC3C,QAAQ,CAAC;AAAA,IACL,SAAS;AAAA,IACT,YAAY,EAAE,MAAM,eAAe;AAAA,EACvC,CAAC;AACL;AAEO,MAAM,yBAAyB,CAAC,QAAiB,QAAQ,IAAI,SAAS,aAAa;AAEnF,MAAM,0BAA0B,CAAC,eAA2C;AAC/E,MAAI,WAAW,SAAS,cAAc,CAAC,WAAW,eAAe;AAC7D,WAAO,CAAC,MAAM,MAAM,SAAS,KAAK;AAAA,EACtC;AAEA,SAAO,QAAQ;AAAA,IACX,QAAQ,WAAW;AAAA,IACnB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,mBAAmB;AAAA,IACnB,QAAQ;AAAA,MACJ,UAAU;AAAA,MACV,UAAU;AAAA,IACd;AAAA,EACJ,CAAC;AACL;AAEO,MAAM,yBAAyB,CAAC,eAA2C;AAC9E,SAAO,CAAC,KAAc,KAAe,SAAuB;AACxD,QAAI,WAAW,SAAS,cAAc,uBAAuB,GAAG,GAAG;AAC/D,WAAK;AACL;AAAA,IACJ;AAEA,QACK,OAAO,GAAG,EACV,IAAI,YAAY,EAChB,KAAK,yBAAyB,CAAC,EAC/B,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,2BAA2B,CAAC,eAA2C;AAChF,SAAO,CAAC,KAAc,KAAe,SAAuB;AACxD,QAAI,WAAW,SAAS,cAAc,uBAAuB,GAAG,GAAG;AAC/D,WAAK;AACL;AAAA,IACJ;AAEA,QACK,OAAO,GAAG,EACV,IAAI,YAAY,EAChB,KAAK,gCAAgC,CAAC,EACtC,IAAI;AAAA,EACb;AACJ;AAEO,MAAM,mCAAmC,MAAsB;AAClE,SAAO,CAAC,YAAY;AAChB,WAAO;AAAA,MACH,oBAAoB,MAAM;AACtB,YAAI,KAAK,cAAc,YAAY;AAC/B;AAAA,QACJ;AAEA,gBAAQ,YAAY,IAAI;AAAA,UACpB;AAAA,UACA;AAAA,YACI,OAAO,CAAC,IAAI;AAAA,YACZ,YAAY,EAAE,MAAM,eAAe;AAAA,UACvC;AAAA,QACJ,CAAC;AAAA,MACL;AAAA,IACJ;AAAA,EACJ;AACJ;","names":[]}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const isTruthy = (value) => {
|
|
2
|
+
if (!value) {
|
|
3
|
+
return false;
|
|
4
|
+
}
|
|
5
|
+
return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
|
|
6
|
+
};
|
|
7
|
+
const ensurePasswordModeRequirements = (env) => {
|
|
8
|
+
if (!env.OCEAN_BRAIN_PASSWORD) {
|
|
9
|
+
throw new Error(
|
|
10
|
+
"Missing OCEAN_BRAIN_PASSWORD. Set OCEAN_BRAIN_PASSWORD to enable password auth mode."
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
if (!env.OCEAN_BRAIN_SESSION_SECRET) {
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Missing OCEAN_BRAIN_SESSION_SECRET. Set OCEAN_BRAIN_SESSION_SECRET when password auth mode is enabled."
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
const resolveAuthConfig = (env) => {
|
|
20
|
+
const allowInsecureNoAuth = isTruthy(env.OCEAN_BRAIN_ALLOW_INSECURE_NO_AUTH);
|
|
21
|
+
if (allowInsecureNoAuth) {
|
|
22
|
+
return {
|
|
23
|
+
mode: "disabled",
|
|
24
|
+
source: "auto"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (env.OCEAN_BRAIN_PASSWORD) {
|
|
28
|
+
ensurePasswordModeRequirements(env);
|
|
29
|
+
return {
|
|
30
|
+
mode: "password",
|
|
31
|
+
password: env.OCEAN_BRAIN_PASSWORD,
|
|
32
|
+
sessionSecret: env.OCEAN_BRAIN_SESSION_SECRET,
|
|
33
|
+
source: "auto"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
throw new Error(
|
|
37
|
+
"Unable to resolve auth mode. Set OCEAN_BRAIN_PASSWORD and OCEAN_BRAIN_SESSION_SECRET for password mode, or set OCEAN_BRAIN_ALLOW_INSECURE_NO_AUTH=true for disabled mode."
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
const logAuthConfig = (authConfig) => {
|
|
41
|
+
if (authConfig.mode === "disabled") {
|
|
42
|
+
process.stderr.write("[auth] Running in disabled mode. Authentication is not required for write access.\n");
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
process.stdout.write("[auth] Running in password mode. Write access requires an authenticated session.\n");
|
|
46
|
+
};
|
|
47
|
+
export {
|
|
48
|
+
logAuthConfig,
|
|
49
|
+
resolveAuthConfig
|
|
50
|
+
};
|
|
51
|
+
//# sourceMappingURL=auth-mode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/auth-mode.ts"],"sourcesContent":["export type AuthMode = 'password' | 'disabled';\n\nexport interface AuthConfig {\n mode: AuthMode;\n password?: string;\n sessionSecret?: string;\n source: 'auto' | 'override';\n}\n\nexport interface AuthModeEnvironment {\n [key: string]: string | undefined;\n OCEAN_BRAIN_ALLOW_INSECURE_NO_AUTH?: string;\n OCEAN_BRAIN_PASSWORD?: string;\n OCEAN_BRAIN_SESSION_SECRET?: string;\n}\n\nconst isTruthy = (value?: string) => {\n if (!value) {\n return false;\n }\n\n return ['1', 'true', 'yes', 'on'].includes(value.trim().toLowerCase());\n};\n\nconst ensurePasswordModeRequirements = (env: AuthModeEnvironment) => {\n if (!env.OCEAN_BRAIN_PASSWORD) {\n throw new Error(\n 'Missing OCEAN_BRAIN_PASSWORD. Set OCEAN_BRAIN_PASSWORD to enable password auth mode.'\n );\n }\n\n if (!env.OCEAN_BRAIN_SESSION_SECRET) {\n throw new Error(\n 'Missing OCEAN_BRAIN_SESSION_SECRET. Set OCEAN_BRAIN_SESSION_SECRET when password auth mode is enabled.'\n );\n }\n};\n\nexport const resolveAuthConfig = (env: AuthModeEnvironment): AuthConfig => {\n const allowInsecureNoAuth = isTruthy(env.OCEAN_BRAIN_ALLOW_INSECURE_NO_AUTH);\n\n if (allowInsecureNoAuth) {\n return {\n mode: 'disabled',\n source: 'auto'\n };\n }\n\n if (env.OCEAN_BRAIN_PASSWORD) {\n ensurePasswordModeRequirements(env);\n\n return {\n mode: 'password',\n password: env.OCEAN_BRAIN_PASSWORD,\n sessionSecret: env.OCEAN_BRAIN_SESSION_SECRET,\n source: 'auto'\n };\n }\n\n throw new Error(\n 'Unable to resolve auth mode. Set OCEAN_BRAIN_PASSWORD and OCEAN_BRAIN_SESSION_SECRET for password mode, or set OCEAN_BRAIN_ALLOW_INSECURE_NO_AUTH=true for disabled mode.'\n );\n};\n\nexport const logAuthConfig = (authConfig: AuthConfig) => {\n if (authConfig.mode === 'disabled') {\n process.stderr.write('[auth] Running in disabled mode. Authentication is not required for write access.\\n');\n return;\n }\n\n process.stdout.write('[auth] Running in password mode. Write access requires an authenticated session.\\n');\n};\n"],"mappings":"AAgBA,MAAM,WAAW,CAAC,UAAmB;AACjC,MAAI,CAAC,OAAO;AACR,WAAO;AAAA,EACX;AAEA,SAAO,CAAC,KAAK,QAAQ,OAAO,IAAI,EAAE,SAAS,MAAM,KAAK,EAAE,YAAY,CAAC;AACzE;AAEA,MAAM,iCAAiC,CAAC,QAA6B;AACjE,MAAI,CAAC,IAAI,sBAAsB;AAC3B,UAAM,IAAI;AAAA,MACN;AAAA,IACJ;AAAA,EACJ;AAEA,MAAI,CAAC,IAAI,4BAA4B;AACjC,UAAM,IAAI;AAAA,MACN;AAAA,IACJ;AAAA,EACJ;AACJ;AAEO,MAAM,oBAAoB,CAAC,QAAyC;AACvE,QAAM,sBAAsB,SAAS,IAAI,kCAAkC;AAE3E,MAAI,qBAAqB;AACrB,WAAO;AAAA,MACH,MAAM;AAAA,MACN,QAAQ;AAAA,IACZ;AAAA,EACJ;AAEA,MAAI,IAAI,sBAAsB;AAC1B,mCAA+B,GAAG;AAElC,WAAO;AAAA,MACH,MAAM;AAAA,MACN,UAAU,IAAI;AAAA,MACd,eAAe,IAAI;AAAA,MACnB,QAAQ;AAAA,IACZ;AAAA,EACJ;AAEA,QAAM,IAAI;AAAA,IACN;AAAA,EACJ;AACJ;AAEO,MAAM,gBAAgB,CAAC,eAA2B;AACrD,MAAI,WAAW,SAAS,YAAY;AAChC,YAAQ,OAAO,MAAM,qFAAqF;AAC1G;AAAA,EACJ;AAEA,UAAQ,OAAO,MAAM,oFAAoF;AAC7G;","names":[]}
|
|
@@ -9,8 +9,17 @@ const comparePassword = async (password, storedHash) => {
|
|
|
9
9
|
const newHash = crypto.pbkdf2Sync(password, salt, 1e3, 64, "sha512").toString("hex");
|
|
10
10
|
return hash === newHash;
|
|
11
11
|
};
|
|
12
|
+
const compareSharedSecret = (expected, received) => {
|
|
13
|
+
const expectedBuffer = Buffer.from(expected, "utf8");
|
|
14
|
+
const receivedBuffer = Buffer.from(received, "utf8");
|
|
15
|
+
if (expectedBuffer.length !== receivedBuffer.length) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
|
|
19
|
+
};
|
|
12
20
|
export {
|
|
13
21
|
comparePassword,
|
|
22
|
+
compareSharedSecret,
|
|
14
23
|
createPasswordHash
|
|
15
24
|
};
|
|
16
25
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/auth.ts"],"sourcesContent":["import crypto from 'crypto';\n\nexport const createPasswordHash = async (password: string) => {\n const salt = crypto.randomBytes(16).toString('hex');\n const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return `${salt}:${hash}`;\n};\n\nexport const comparePassword = async (password: string, storedHash: string) => {\n const [salt, hash] = storedHash.split(':');\n const newHash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return hash === newHash;\n};\n"],"mappings":"AAAA,OAAO,YAAY;AAEZ,MAAM,qBAAqB,OAAO,aAAqB;AAC1D,QAAM,OAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAClD,QAAM,OAAO,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACjF,SAAO,GAAG,IAAI,IAAI,IAAI;AAC1B;AAEO,MAAM,kBAAkB,OAAO,UAAkB,eAAuB;AAC3E,QAAM,CAAC,MAAM,IAAI,IAAI,WAAW,MAAM,GAAG;AACzC,QAAM,UAAU,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACpF,SAAO,SAAS;AACpB;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/modules/auth.ts"],"sourcesContent":["import crypto from 'crypto';\n\nexport const createPasswordHash = async (password: string) => {\n const salt = crypto.randomBytes(16).toString('hex');\n const hash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return `${salt}:${hash}`;\n};\n\nexport const comparePassword = async (password: string, storedHash: string) => {\n const [salt, hash] = storedHash.split(':');\n const newHash = crypto.pbkdf2Sync(password, salt, 1000, 64, 'sha512').toString('hex');\n return hash === newHash;\n};\n\nexport const compareSharedSecret = (expected: string, received: string) => {\n const expectedBuffer = Buffer.from(expected, 'utf8');\n const receivedBuffer = Buffer.from(received, 'utf8');\n\n if (expectedBuffer.length !== receivedBuffer.length) {\n return false;\n }\n\n return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);\n};\n"],"mappings":"AAAA,OAAO,YAAY;AAEZ,MAAM,qBAAqB,OAAO,aAAqB;AAC1D,QAAM,OAAO,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAClD,QAAM,OAAO,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACjF,SAAO,GAAG,IAAI,IAAI,IAAI;AAC1B;AAEO,MAAM,kBAAkB,OAAO,UAAkB,eAAuB;AAC3E,QAAM,CAAC,MAAM,IAAI,IAAI,WAAW,MAAM,GAAG;AACzC,QAAM,UAAU,OAAO,WAAW,UAAU,MAAM,KAAM,IAAI,QAAQ,EAAE,SAAS,KAAK;AACpF,SAAO,SAAS;AACpB;AAEO,MAAM,sBAAsB,CAAC,UAAkB,aAAqB;AACvE,QAAM,iBAAiB,OAAO,KAAK,UAAU,MAAM;AACnD,QAAM,iBAAiB,OAAO,KAAK,UAAU,MAAM;AAEnD,MAAI,eAAe,WAAW,eAAe,QAAQ;AACjD,WAAO;AAAA,EACX;AAEA,SAAO,OAAO,gBAAgB,gBAAgB,cAAc;AAChE;","names":[]}
|
|
@@ -40,7 +40,13 @@ async function blocksToMarkdown(contentJson) {
|
|
|
40
40
|
return "";
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
+
async function markdownToBlocksJson(markdown) {
|
|
44
|
+
const editor = getEditor();
|
|
45
|
+
const blocks = await editor.tryParseMarkdownToBlocks(markdown);
|
|
46
|
+
return JSON.stringify(blocks);
|
|
47
|
+
}
|
|
43
48
|
export {
|
|
44
|
-
blocksToMarkdown
|
|
49
|
+
blocksToMarkdown,
|
|
50
|
+
markdownToBlocksJson
|
|
45
51
|
};
|
|
46
52
|
//# sourceMappingURL=blocknote.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/modules/blocknote.ts"],"sourcesContent":["import { ServerBlockNoteEditor } from '@blocknote/server-util';\n\ninterface BlockNote {\n id?: string;\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNote[];\n children?: BlockNote[];\n text?: string;\n styles?: Record<string, unknown>;\n}\n\nfunction preprocessCustomInlineContent(blocks: BlockNote[]): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: block.content?.map((inline) => {\n if (inline.type === 'reference') {\n return {\n type: 'text',\n text: `[[${inline.props?.title || inline.props?.id || ''}]]`,\n styles: {}\n };\n }\n if (inline.type === 'tag') {\n const tag = (inline.props?.tag as string)?.replace(/^@/, '') || '';\n return {\n type: 'text',\n text: `#${tag}`,\n styles: {}\n };\n }\n return inline;\n }),\n children: block.children?.length\n ? preprocessCustomInlineContent(block.children)\n : []\n }));\n}\n\nlet editorInstance: ServerBlockNoteEditor | null = null;\n\nfunction getEditor(): ServerBlockNoteEditor {\n if (!editorInstance) {\n editorInstance = ServerBlockNoteEditor.create();\n }\n return editorInstance;\n}\n\nexport async function blocksToMarkdown(contentJson: string): Promise<string> {\n try {\n const blocks = JSON.parse(contentJson);\n const processed = preprocessCustomInlineContent(blocks);\n const editor = getEditor();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await editor.blocksToMarkdownLossy(processed as any);\n } catch {\n return '';\n }\n}\n"],"mappings":"AAAA,SAAS,6BAA6B;AAYtC,SAAS,8BAA8B,QAAkC;AACrE,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS,MAAM,SAAS,IAAI,CAAC,WAAW;AACpC,UAAI,OAAO,SAAS,aAAa;AAC7B,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,KAAK,OAAO,OAAO,SAAS,OAAO,OAAO,MAAM,EAAE;AAAA,UACxD,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,UAAI,OAAO,SAAS,OAAO;AACvB,cAAM,MAAO,OAAO,OAAO,KAAgB,QAAQ,MAAM,EAAE,KAAK;AAChE,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,IAAI,GAAG;AAAA,UACb,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,aAAO;AAAA,IACX,CAAC;AAAA,IACD,UAAU,MAAM,UAAU,SACpB,8BAA8B,MAAM,QAAQ,IAC5C,CAAC;AAAA,EACX,EAAE;AACN;AAEA,IAAI,iBAA+C;AAEnD,SAAS,YAAmC;AACxC,MAAI,CAAC,gBAAgB;AACjB,qBAAiB,sBAAsB,OAAO;AAAA,EAClD;AACA,SAAO;AACX;AAEA,eAAsB,iBAAiB,aAAsC;AACzE,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,WAAW;AACrC,UAAM,YAAY,8BAA8B,MAAM;AACtD,UAAM,SAAS,UAAU;AAEzB,WAAO,MAAM,OAAO,sBAAsB,SAAgB;AAAA,EAC9D,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/modules/blocknote.ts"],"sourcesContent":["import { ServerBlockNoteEditor } from '@blocknote/server-util';\n\ninterface BlockNote {\n id?: string;\n type: string;\n props?: Record<string, unknown>;\n content?: BlockNote[];\n children?: BlockNote[];\n text?: string;\n styles?: Record<string, unknown>;\n}\n\nfunction preprocessCustomInlineContent(blocks: BlockNote[]): BlockNote[] {\n return blocks.map((block) => ({\n ...block,\n content: block.content?.map((inline) => {\n if (inline.type === 'reference') {\n return {\n type: 'text',\n text: `[[${inline.props?.title || inline.props?.id || ''}]]`,\n styles: {}\n };\n }\n if (inline.type === 'tag') {\n const tag = (inline.props?.tag as string)?.replace(/^@/, '') || '';\n return {\n type: 'text',\n text: `#${tag}`,\n styles: {}\n };\n }\n return inline;\n }),\n children: block.children?.length\n ? preprocessCustomInlineContent(block.children)\n : []\n }));\n}\n\nlet editorInstance: ServerBlockNoteEditor | null = null;\n\nfunction getEditor(): ServerBlockNoteEditor {\n if (!editorInstance) {\n editorInstance = ServerBlockNoteEditor.create();\n }\n return editorInstance;\n}\n\nexport async function blocksToMarkdown(contentJson: string): Promise<string> {\n try {\n const blocks = JSON.parse(contentJson);\n const processed = preprocessCustomInlineContent(blocks);\n const editor = getEditor();\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return await editor.blocksToMarkdownLossy(processed as any);\n } catch {\n return '';\n }\n}\n\nexport async function markdownToBlocksJson(markdown: string): Promise<string> {\n const editor = getEditor();\n const blocks = await editor.tryParseMarkdownToBlocks(markdown);\n return JSON.stringify(blocks);\n}\n"],"mappings":"AAAA,SAAS,6BAA6B;AAYtC,SAAS,8BAA8B,QAAkC;AACrE,SAAO,OAAO,IAAI,CAAC,WAAW;AAAA,IAC1B,GAAG;AAAA,IACH,SAAS,MAAM,SAAS,IAAI,CAAC,WAAW;AACpC,UAAI,OAAO,SAAS,aAAa;AAC7B,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,KAAK,OAAO,OAAO,SAAS,OAAO,OAAO,MAAM,EAAE;AAAA,UACxD,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,UAAI,OAAO,SAAS,OAAO;AACvB,cAAM,MAAO,OAAO,OAAO,KAAgB,QAAQ,MAAM,EAAE,KAAK;AAChE,eAAO;AAAA,UACH,MAAM;AAAA,UACN,MAAM,IAAI,GAAG;AAAA,UACb,QAAQ,CAAC;AAAA,QACb;AAAA,MACJ;AACA,aAAO;AAAA,IACX,CAAC;AAAA,IACD,UAAU,MAAM,UAAU,SACpB,8BAA8B,MAAM,QAAQ,IAC5C,CAAC;AAAA,EACX,EAAE;AACN;AAEA,IAAI,iBAA+C;AAEnD,SAAS,YAAmC;AACxC,MAAI,CAAC,gBAAgB;AACjB,qBAAiB,sBAAsB,OAAO;AAAA,EAClD;AACA,SAAO;AACX;AAEA,eAAsB,iBAAiB,aAAsC;AACzE,MAAI;AACA,UAAM,SAAS,KAAK,MAAM,WAAW;AACrC,UAAM,YAAY,8BAA8B,MAAM;AACtD,UAAM,SAAS,UAAU;AAEzB,WAAO,MAAM,OAAO,sBAAsB,SAAgB;AAAA,EAC9D,QAAQ;AACJ,WAAO;AAAA,EACX;AACJ;AAEA,eAAsB,qBAAqB,UAAmC;AAC1E,QAAM,SAAS,UAAU;AACzB,QAAM,SAAS,MAAM,OAAO,yBAAyB,QAAQ;AAC7D,SAAO,KAAK,UAAU,MAAM;AAChC;","names":[]}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class AppError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
status;
|
|
4
|
+
constructor(status, code, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.name = "AppError";
|
|
7
|
+
this.status = status;
|
|
8
|
+
this.code = code;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
const createAppError = (status, code, message) => {
|
|
12
|
+
return new AppError(status, code, message);
|
|
13
|
+
};
|
|
14
|
+
const createErrorHandler = () => {
|
|
15
|
+
return (error, _req, res, next) => {
|
|
16
|
+
if (res.headersSent) {
|
|
17
|
+
next(error);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (error instanceof AppError) {
|
|
21
|
+
res.status(error.status).json({
|
|
22
|
+
code: error.code,
|
|
23
|
+
message: error.message
|
|
24
|
+
}).end();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const message = error instanceof Error ? error.stack || error.message : String(error);
|
|
28
|
+
process.stderr.write(`[error] ${message}
|
|
29
|
+
`);
|
|
30
|
+
res.status(500).json({
|
|
31
|
+
code: "INTERNAL_SERVER_ERROR",
|
|
32
|
+
message: "Internal Server Error"
|
|
33
|
+
}).end();
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
export {
|
|
37
|
+
AppError,
|
|
38
|
+
createAppError,
|
|
39
|
+
createErrorHandler
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/error-handler.ts"],"sourcesContent":["import type { ErrorRequestHandler } from 'express';\n\nexport class AppError extends Error {\n code: string;\n status: number;\n\n constructor(status: number, code: string, message: string) {\n super(message);\n this.name = 'AppError';\n this.status = status;\n this.code = code;\n }\n}\n\nexport const createAppError = (status: number, code: string, message: string) => {\n return new AppError(status, code, message);\n};\n\nexport const createErrorHandler = (): ErrorRequestHandler => {\n return (error, _req, res, next) => {\n if (res.headersSent) {\n next(error);\n return;\n }\n\n if (error instanceof AppError) {\n res.status(error.status).json({\n code: error.code,\n message: error.message\n }).end();\n return;\n }\n\n const message = error instanceof Error ? error.stack || error.message : String(error);\n process.stderr.write(`[error] ${message}\\n`);\n\n res.status(500).json({\n code: 'INTERNAL_SERVER_ERROR',\n message: 'Internal Server Error'\n }).end();\n };\n};\n"],"mappings":"AAEO,MAAM,iBAAiB,MAAM;AAAA,EAChC;AAAA,EACA;AAAA,EAEA,YAAY,QAAgB,MAAc,SAAiB;AACvD,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EAChB;AACJ;AAEO,MAAM,iBAAiB,CAAC,QAAgB,MAAc,YAAoB;AAC7E,SAAO,IAAI,SAAS,QAAQ,MAAM,OAAO;AAC7C;AAEO,MAAM,qBAAqB,MAA2B;AACzD,SAAO,CAAC,OAAO,MAAM,KAAK,SAAS;AAC/B,QAAI,IAAI,aAAa;AACjB,WAAK,KAAK;AACV;AAAA,IACJ;AAEA,QAAI,iBAAiB,UAAU;AAC3B,UAAI,OAAO,MAAM,MAAM,EAAE,KAAK;AAAA,QAC1B,MAAM,MAAM;AAAA,QACZ,SAAS,MAAM;AAAA,MACnB,CAAC,EAAE,IAAI;AACP;AAAA,IACJ;AAEA,UAAM,UAAU,iBAAiB,QAAQ,MAAM,SAAS,MAAM,UAAU,OAAO,KAAK;AACpF,YAAQ,OAAO,MAAM,WAAW,OAAO;AAAA,CAAI;AAE3C,QAAI,OAAO,GAAG,EAAE,KAAK;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,IACb,CAAC,EAAE,IAAI;AAAA,EACX;AACJ;","names":[]}
|