ocean-brain 0.7.4 → 0.8.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 +3 -3
- package/dist/mcp.js +180 -736
- package/package.json +4 -1
- package/server/client/dist/assets/ArrowRight.es-C4zlc54Z.js +1 -0
- package/server/client/dist/assets/{Calendar-Bz7jheg6.js → Calendar-COrN6Hjv.js} +2 -2
- package/server/client/dist/assets/Callout-BK0K9E74.js +1 -0
- package/server/client/dist/assets/Copy.es-zm8f_XXX.js +1 -0
- package/server/client/dist/assets/Graph-BH1fzhue.js +1 -0
- package/server/client/dist/assets/{Image-DxWhlIDG.js → Image-KXuHOG4b.js} +1 -1
- package/server/client/dist/assets/Image.es-BKahRbIm.js +1 -0
- package/server/client/dist/assets/Note-C143cKKw.js +12 -0
- package/server/client/dist/assets/Plus.es-BqeC8T8h.js +1 -0
- package/server/client/dist/assets/Reminders-BYG5rz1M.js +1 -0
- package/server/client/dist/assets/Search-DBWsIMDP.js +1 -0
- package/server/client/dist/assets/{SurfaceCard-CfL1jD-O.js → SurfaceCard-CNSQfYfM.js} +1 -1
- package/server/client/dist/assets/Tag-5aCfSO8Y.js +1 -0
- package/server/client/dist/assets/TagNotes-BPZjN-6l.js +1 -0
- package/server/client/dist/assets/Trash.es-CRQkTnXZ.js +1 -0
- package/server/client/dist/assets/ViewNotes-DGLNF96x.js +1 -0
- package/server/client/dist/assets/ViewSectionDialog-CzskgmMF.js +102 -0
- package/server/client/dist/assets/Views-DR5jT23G.js +1 -0
- package/server/client/dist/assets/{graph-vendor-CUxe67Lr.js → graph-vendor-UTvF2NS_.js} +2 -2
- package/server/client/dist/assets/image.api-8dlMwzMd.js +17 -0
- package/server/client/dist/assets/{index-C5BGzDgt.css → index-BEUqDuAZ.css} +1 -1
- package/server/client/dist/assets/{index-DiMt3Dz4.js → index-DGIAh3ZY.js} +1 -1
- package/server/client/dist/assets/index-VHp17GQX.js +342 -0
- package/server/client/dist/assets/manage-image-RqtWKas2.js +1 -0
- package/server/client/dist/assets/{manage-image-detail-Bqw8NURO.js → manage-image-detail-BTSIbLlQ.js} +1 -1
- package/server/client/dist/assets/mcp-EDurbggR.js +4 -0
- package/server/client/dist/assets/mcp-admin-adapter-DmGUo5G8.js +1 -0
- package/server/client/dist/assets/{note-core-Z6kqulGB.js → note-core-BklwyZHk.js} +1 -1
- package/server/client/dist/assets/{note-vendor-i1M2FSI2.js → note-vendor-DkYKUmLo.js} +1 -1
- package/server/client/dist/assets/{placeholder-Ro00byT7.js → placeholder-DeKgSBnC.js} +4 -4
- package/server/client/dist/assets/{properties-Tid6-ghT.js → properties-BjrZvOYn.js} +1 -1
- package/server/client/dist/assets/tag.api-Dl8DQd0m.js +21 -0
- package/server/client/dist/assets/trash-C5ntddLu.js +1 -0
- package/server/client/dist/assets/{useReminderMutate-BWymIujb.js → useReminderMutate-CoXXqJY2.js} +1 -1
- package/server/client/dist/index.html +5 -5
- package/server/dist/app.js +2 -2
- package/server/dist/app.js.map +1 -1
- package/server/dist/features/auth/http/api.js +5 -1
- package/server/dist/features/auth/http/api.js.map +1 -1
- package/server/dist/features/auth/http/login-page.js +1 -1
- package/server/dist/features/auth/http/pages.js +8 -1
- package/server/dist/features/auth/http/pages.js.map +1 -1
- package/server/dist/features/auth/service.js +16 -7
- package/server/dist/features/auth/service.js.map +1 -1
- package/server/dist/features/image/http/upload.js +34 -23
- package/server/dist/features/image/http/upload.js.map +1 -1
- package/server/dist/features/image/services/validation.js +111 -0
- package/server/dist/features/image/services/validation.js.map +1 -0
- package/server/dist/features/mcp-admin/http/handlers.js +11 -3
- package/server/dist/features/mcp-admin/http/handlers.js.map +1 -1
- package/server/dist/features/note/http/mcp.js +8 -43
- package/server/dist/features/note/http/mcp.js.map +1 -1
- package/server/dist/features/note/services/cleanup.js +2 -2
- package/server/dist/features/note/services/cleanup.js.map +1 -1
- package/server/dist/features/note/services/markdown-intent-write.js +11 -43
- package/server/dist/features/note/services/markdown-intent-write.js.map +1 -1
- package/server/dist/features/note/services/markdown-patch.js +4 -123
- package/server/dist/features/note/services/markdown-patch.js.map +1 -1
- package/server/dist/features/note/services/write.js +13 -13
- package/server/dist/features/note/services/write.js.map +1 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js +16 -1
- package/server/dist/features/tag/graphql/tag.query.resolver.js.map +1 -1
- package/server/dist/features/tag/graphql/tag.type-defs.js +2 -0
- package/server/dist/features/tag/graphql/tag.type-defs.js.map +1 -1
- package/server/dist/modules/app-version.js +105 -0
- package/server/dist/modules/app-version.js.map +1 -0
- package/server/dist/modules/auth-guard.js +21 -2
- package/server/dist/modules/auth-guard.js.map +1 -1
- package/server/dist/modules/auth-redirect.js +10 -0
- package/server/dist/modules/auth-redirect.js.map +1 -0
- package/server/dist/modules/mcp-auth.js +62 -1
- package/server/dist/modules/mcp-auth.js.map +1 -1
- package/server/dist/modules/rate-limit.js +15 -1
- package/server/dist/modules/rate-limit.js.map +1 -1
- package/server/dist/modules/server-events-handler.js +17 -0
- package/server/dist/modules/server-events-handler.js.map +1 -1
- package/server/dist/modules/session-store.js +149 -0
- package/server/dist/modules/session-store.js.map +1 -0
- package/server/dist/paths.js +1 -0
- package/server/dist/paths.js.map +1 -1
- package/server/dist/routes/api.js +39 -20
- package/server/dist/routes/api.js.map +1 -1
- package/server/dist/routes/auth-pages.js +13 -2
- package/server/dist/routes/auth-pages.js.map +1 -1
- package/server/dist/routes/client.js +63 -3
- package/server/dist/routes/client.js.map +1 -1
- package/server/dist/routes/graphql.js +23 -19
- package/server/dist/routes/graphql.js.map +1 -1
- package/server/client/dist/assets/Callout-DlFLkTCG.js +0 -1
- package/server/client/dist/assets/Graph-BdxtJvog.js +0 -1
- package/server/client/dist/assets/Image.es-BFodqKS5.js +0 -1
- package/server/client/dist/assets/Note-BgMxsWZT.js +0 -22
- package/server/client/dist/assets/Plus.es-Yq7J1Ur4.js +0 -1
- package/server/client/dist/assets/Reminders-CUsR56ff.js +0 -1
- package/server/client/dist/assets/Search-DncrGgOZ.js +0 -1
- package/server/client/dist/assets/Tag-CKDLs2Y6.js +0 -1
- package/server/client/dist/assets/TagNotes-ByX_Qias.js +0 -1
- package/server/client/dist/assets/Trash.es-BPrF6TPV.js +0 -1
- package/server/client/dist/assets/ViewNotes-r1kxKZ8O.js +0 -1
- package/server/client/dist/assets/ViewSectionTableRenderer-DQ4UeCnm.js +0 -102
- package/server/client/dist/assets/Views-FzFBtNT2.js +0 -1
- package/server/client/dist/assets/image.api-Bcg3TFCG.js +0 -17
- package/server/client/dist/assets/index-BFfQKAUY.js +0 -361
- package/server/client/dist/assets/manage-image-DzDlncCM.js +0 -1
- package/server/client/dist/assets/mcp-BqasqTWC.js +0 -1
- package/server/client/dist/assets/trash-bHK02-Up.js +0 -1
- package/server/dist/features/image/services/remote-fetch.js +0 -176
- package/server/dist/features/image/services/remote-fetch.js.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import session from "express-session";
|
|
2
|
+
const AUTH_SESSION_IDLE_TIMEOUT_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
3
|
+
const ANONYMOUS_SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
4
|
+
const AUTH_SESSION_PRUNE_INTERVAL_MS = 60 * 60 * 1e3;
|
|
5
|
+
const DEFAULT_MAX_SESSIONS = 1e3;
|
|
6
|
+
const serializeSession = (data) => JSON.stringify(data);
|
|
7
|
+
const deserializeSession = (data) => {
|
|
8
|
+
const parsed = JSON.parse(data);
|
|
9
|
+
const expires = parsed.cookie?.expires;
|
|
10
|
+
if (typeof expires === "string") {
|
|
11
|
+
parsed.cookie.expires = new Date(expires);
|
|
12
|
+
}
|
|
13
|
+
return parsed;
|
|
14
|
+
};
|
|
15
|
+
const isAuthenticatedSession = (data) => Boolean(data.authenticated);
|
|
16
|
+
const getSessionExpiry = (data, now = Date.now()) => {
|
|
17
|
+
const expires = data.cookie?.expires;
|
|
18
|
+
const ttlMs = isAuthenticatedSession(data) ? AUTH_SESSION_IDLE_TIMEOUT_MS : ANONYMOUS_SESSION_IDLE_TIMEOUT_MS;
|
|
19
|
+
const fallbackExpiresAt = now + ttlMs;
|
|
20
|
+
if (expires) {
|
|
21
|
+
const expiresAt = expires instanceof Date ? expires : new Date(expires);
|
|
22
|
+
if (!Number.isNaN(expiresAt.getTime())) {
|
|
23
|
+
return Math.min(expiresAt.getTime(), fallbackExpiresAt);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return fallbackExpiresAt;
|
|
27
|
+
};
|
|
28
|
+
class TtlMemorySessionStore extends session.Store {
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
super();
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.pruneTimer = setInterval(() => {
|
|
33
|
+
this.pruneExpiredSessions();
|
|
34
|
+
}, options.pruneIntervalMs ?? AUTH_SESSION_PRUNE_INTERVAL_MS);
|
|
35
|
+
this.pruneTimer.unref?.();
|
|
36
|
+
}
|
|
37
|
+
options;
|
|
38
|
+
sessions = /* @__PURE__ */ new Map();
|
|
39
|
+
pruneTimer;
|
|
40
|
+
get(sid, callback) {
|
|
41
|
+
try {
|
|
42
|
+
const entry = this.sessions.get(sid);
|
|
43
|
+
if (!entry) {
|
|
44
|
+
callback(null, null);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (entry.expiresAt <= Date.now()) {
|
|
48
|
+
this.sessions.delete(sid);
|
|
49
|
+
callback(null, null);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
callback(null, deserializeSession(entry.data));
|
|
53
|
+
} catch (error) {
|
|
54
|
+
callback(error);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
set(sid, data, callback) {
|
|
58
|
+
try {
|
|
59
|
+
const now = Date.now();
|
|
60
|
+
this.sessions.set(sid, {
|
|
61
|
+
data: serializeSession(data),
|
|
62
|
+
expiresAt: getSessionExpiry(data, now),
|
|
63
|
+
authenticated: isAuthenticatedSession(data),
|
|
64
|
+
updatedAt: now
|
|
65
|
+
});
|
|
66
|
+
this.pruneExpiredSessions();
|
|
67
|
+
this.evictOldestSessions();
|
|
68
|
+
callback?.();
|
|
69
|
+
} catch (error) {
|
|
70
|
+
callback?.(error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
destroy(sid, callback) {
|
|
74
|
+
this.sessions.delete(sid);
|
|
75
|
+
callback?.();
|
|
76
|
+
}
|
|
77
|
+
touch(sid, data, callback) {
|
|
78
|
+
const entry = this.sessions.get(sid);
|
|
79
|
+
if (entry) {
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
this.sessions.set(sid, {
|
|
82
|
+
data: serializeSession(data),
|
|
83
|
+
expiresAt: getSessionExpiry(data, now),
|
|
84
|
+
authenticated: isAuthenticatedSession(data),
|
|
85
|
+
updatedAt: now
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
callback?.();
|
|
89
|
+
}
|
|
90
|
+
all(callback) {
|
|
91
|
+
try {
|
|
92
|
+
this.pruneExpiredSessions();
|
|
93
|
+
const sessions = {};
|
|
94
|
+
for (const [sid, entry] of this.sessions) {
|
|
95
|
+
sessions[sid] = deserializeSession(entry.data);
|
|
96
|
+
}
|
|
97
|
+
callback(null, sessions);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
callback(error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
length(callback) {
|
|
103
|
+
this.pruneExpiredSessions();
|
|
104
|
+
callback(null, this.sessions.size);
|
|
105
|
+
}
|
|
106
|
+
clear(callback) {
|
|
107
|
+
this.sessions.clear();
|
|
108
|
+
callback?.();
|
|
109
|
+
}
|
|
110
|
+
pruneExpiredSessions(now = Date.now()) {
|
|
111
|
+
for (const [sid, entry] of this.sessions) {
|
|
112
|
+
if (entry.expiresAt <= now) {
|
|
113
|
+
this.sessions.delete(sid);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
stopPruning() {
|
|
118
|
+
clearInterval(this.pruneTimer);
|
|
119
|
+
}
|
|
120
|
+
evictOldestSessions() {
|
|
121
|
+
const maxSessions = this.options.maxSessions ?? DEFAULT_MAX_SESSIONS;
|
|
122
|
+
while (this.sessions.size > maxSessions) {
|
|
123
|
+
const oldestSid = this.findOldestSessionId((entry) => !entry.authenticated) ?? this.findOldestSessionId();
|
|
124
|
+
if (!oldestSid) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
this.sessions.delete(oldestSid);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
findOldestSessionId(filter = () => true) {
|
|
131
|
+
let oldestSid;
|
|
132
|
+
let oldestUpdatedAt = Number.POSITIVE_INFINITY;
|
|
133
|
+
for (const [sid, entry] of this.sessions) {
|
|
134
|
+
if (filter(entry) && entry.updatedAt < oldestUpdatedAt) {
|
|
135
|
+
oldestSid = sid;
|
|
136
|
+
oldestUpdatedAt = entry.updatedAt;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return oldestSid;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const createSessionStore = () => new TtlMemorySessionStore();
|
|
143
|
+
export {
|
|
144
|
+
ANONYMOUS_SESSION_IDLE_TIMEOUT_MS,
|
|
145
|
+
AUTH_SESSION_IDLE_TIMEOUT_MS,
|
|
146
|
+
TtlMemorySessionStore,
|
|
147
|
+
createSessionStore
|
|
148
|
+
};
|
|
149
|
+
//# sourceMappingURL=session-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/modules/session-store.ts"],"sourcesContent":["import session, { type SessionData } from 'express-session';\n\nexport const AUTH_SESSION_IDLE_TIMEOUT_MS = 7 * 24 * 60 * 60 * 1000;\nexport const ANONYMOUS_SESSION_IDLE_TIMEOUT_MS = 30 * 60 * 1000;\nconst AUTH_SESSION_PRUNE_INTERVAL_MS = 60 * 60 * 1000;\nconst DEFAULT_MAX_SESSIONS = 1000;\n\ntype StoredSession = {\n data: string;\n expiresAt: number;\n authenticated: boolean;\n updatedAt: number;\n};\n\ntype AuthSessionData = SessionData & {\n authenticated?: boolean;\n};\n\nconst serializeSession = (data: SessionData) => JSON.stringify(data);\n\nconst deserializeSession = (data: string): SessionData => {\n const parsed = JSON.parse(data) as SessionData;\n const expires = parsed.cookie?.expires;\n\n if (typeof expires === 'string') {\n parsed.cookie.expires = new Date(expires);\n }\n\n return parsed;\n};\n\nconst isAuthenticatedSession = (data: SessionData) => Boolean((data as AuthSessionData).authenticated);\n\nconst getSessionExpiry = (data: SessionData, now = Date.now()) => {\n const expires = data.cookie?.expires;\n const ttlMs = isAuthenticatedSession(data) ? AUTH_SESSION_IDLE_TIMEOUT_MS : ANONYMOUS_SESSION_IDLE_TIMEOUT_MS;\n const fallbackExpiresAt = now + ttlMs;\n\n if (expires) {\n const expiresAt = expires instanceof Date ? expires : new Date(expires);\n\n if (!Number.isNaN(expiresAt.getTime())) {\n return Math.min(expiresAt.getTime(), fallbackExpiresAt);\n }\n }\n\n return fallbackExpiresAt;\n};\n\nexport class TtlMemorySessionStore extends session.Store {\n private readonly sessions = new Map<string, StoredSession>();\n private readonly pruneTimer: NodeJS.Timeout;\n\n constructor(private readonly options: { maxSessions?: number; pruneIntervalMs?: number } = {}) {\n super();\n\n this.pruneTimer = setInterval(() => {\n this.pruneExpiredSessions();\n }, options.pruneIntervalMs ?? AUTH_SESSION_PRUNE_INTERVAL_MS);\n this.pruneTimer.unref?.();\n }\n\n get(sid: string, callback: (error: unknown, session?: SessionData | null) => void) {\n try {\n const entry = this.sessions.get(sid);\n\n if (!entry) {\n callback(null, null);\n return;\n }\n\n if (entry.expiresAt <= Date.now()) {\n this.sessions.delete(sid);\n callback(null, null);\n return;\n }\n\n callback(null, deserializeSession(entry.data));\n } catch (error) {\n callback(error);\n }\n }\n\n set(sid: string, data: SessionData, callback?: (error?: unknown) => void) {\n try {\n const now = Date.now();\n\n this.sessions.set(sid, {\n data: serializeSession(data),\n expiresAt: getSessionExpiry(data, now),\n authenticated: isAuthenticatedSession(data),\n updatedAt: now,\n });\n this.pruneExpiredSessions();\n this.evictOldestSessions();\n callback?.();\n } catch (error) {\n callback?.(error);\n }\n }\n\n destroy(sid: string, callback?: (error?: unknown) => void) {\n this.sessions.delete(sid);\n callback?.();\n }\n\n touch(sid: string, data: SessionData, callback?: () => void) {\n const entry = this.sessions.get(sid);\n\n if (entry) {\n const now = Date.now();\n\n this.sessions.set(sid, {\n data: serializeSession(data),\n expiresAt: getSessionExpiry(data, now),\n authenticated: isAuthenticatedSession(data),\n updatedAt: now,\n });\n }\n\n callback?.();\n }\n\n all(callback: (error: unknown, sessions?: SessionData[] | { [sid: string]: SessionData } | null) => void) {\n try {\n this.pruneExpiredSessions();\n\n const sessions: Record<string, SessionData> = {};\n for (const [sid, entry] of this.sessions) {\n sessions[sid] = deserializeSession(entry.data);\n }\n\n callback(null, sessions);\n } catch (error) {\n callback(error);\n }\n }\n\n length(callback: (error: unknown, length?: number) => void) {\n this.pruneExpiredSessions();\n callback(null, this.sessions.size);\n }\n\n clear(callback?: (error?: unknown) => void) {\n this.sessions.clear();\n callback?.();\n }\n\n pruneExpiredSessions(now = Date.now()) {\n for (const [sid, entry] of this.sessions) {\n if (entry.expiresAt <= now) {\n this.sessions.delete(sid);\n }\n }\n }\n\n stopPruning() {\n clearInterval(this.pruneTimer);\n }\n\n private evictOldestSessions() {\n const maxSessions = this.options.maxSessions ?? DEFAULT_MAX_SESSIONS;\n\n while (this.sessions.size > maxSessions) {\n const oldestSid = this.findOldestSessionId((entry) => !entry.authenticated) ?? this.findOldestSessionId();\n\n if (!oldestSid) {\n return;\n }\n\n this.sessions.delete(oldestSid);\n }\n }\n\n private findOldestSessionId(filter: (entry: StoredSession) => boolean = () => true) {\n let oldestSid: string | undefined;\n let oldestUpdatedAt = Number.POSITIVE_INFINITY;\n\n for (const [sid, entry] of this.sessions) {\n if (filter(entry) && entry.updatedAt < oldestUpdatedAt) {\n oldestSid = sid;\n oldestUpdatedAt = entry.updatedAt;\n }\n }\n\n return oldestSid;\n }\n}\n\nexport const createSessionStore = (): session.Store => new TtlMemorySessionStore();\n"],"mappings":"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","names":[]}
|
package/server/dist/paths.js
CHANGED
|
@@ -4,6 +4,7 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
4
4
|
const PACKAGE_ROOT = process.env.OCEAN_BRAIN_PACKAGE_ROOT || path.resolve(__dirname, "..");
|
|
5
5
|
const IMAGE_DIR = process.env.OCEAN_BRAIN_IMAGE_DIR || path.resolve(process.env.OCEAN_BRAIN_DATA_DIR || ".", "assets/images");
|
|
6
6
|
const paths = {
|
|
7
|
+
packageRoot: path.resolve(PACKAGE_ROOT),
|
|
7
8
|
clientDist: path.resolve(PACKAGE_ROOT, "client/dist"),
|
|
8
9
|
clientIndex: path.resolve(PACKAGE_ROOT, "client/dist/index.html"),
|
|
9
10
|
imageDir: path.resolve(IMAGE_DIR)
|
package/server/dist/paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/paths.ts"],"sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 패키지 루트 (dist/paths.js 기준 상위)\nconst PACKAGE_ROOT = process.env.OCEAN_BRAIN_PACKAGE_ROOT || path.resolve(__dirname, '..');\n\n// 이미지 디렉토리 (별도 지정 가능, Docker에서 분리 볼륨용)\nconst IMAGE_DIR =\n process.env.OCEAN_BRAIN_IMAGE_DIR || path.resolve(process.env.OCEAN_BRAIN_DATA_DIR || '.', 'assets/images');\n\nexport const paths = {\n clientDist: path.resolve(PACKAGE_ROOT, 'client/dist'),\n clientIndex: path.resolve(PACKAGE_ROOT, 'client/dist/index.html'),\n imageDir: path.resolve(IMAGE_DIR),\n};\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,MAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG7D,MAAM,eAAe,QAAQ,IAAI,4BAA4B,KAAK,QAAQ,WAAW,IAAI;AAGzF,MAAM,YACF,QAAQ,IAAI,yBAAyB,KAAK,QAAQ,QAAQ,IAAI,wBAAwB,KAAK,eAAe;AAEvG,MAAM,QAAQ;AAAA,EACjB,YAAY,KAAK,QAAQ,cAAc,aAAa;AAAA,EACpD,aAAa,KAAK,QAAQ,cAAc,wBAAwB;AAAA,EAChE,UAAU,KAAK,QAAQ,SAAS;AACpC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/paths.ts"],"sourcesContent":["import path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// 패키지 루트 (dist/paths.js 기준 상위)\nconst PACKAGE_ROOT = process.env.OCEAN_BRAIN_PACKAGE_ROOT || path.resolve(__dirname, '..');\n\n// 이미지 디렉토리 (별도 지정 가능, Docker에서 분리 볼륨용)\nconst IMAGE_DIR =\n process.env.OCEAN_BRAIN_IMAGE_DIR || path.resolve(process.env.OCEAN_BRAIN_DATA_DIR || '.', 'assets/images');\n\nexport const paths = {\n packageRoot: path.resolve(PACKAGE_ROOT),\n clientDist: path.resolve(PACKAGE_ROOT, 'client/dist'),\n clientIndex: path.resolve(PACKAGE_ROOT, 'client/dist/index.html'),\n imageDir: path.resolve(IMAGE_DIR),\n};\n"],"mappings":"AAAA,OAAO,UAAU;AACjB,SAAS,qBAAqB;AAE9B,MAAM,YAAY,KAAK,QAAQ,cAAc,YAAY,GAAG,CAAC;AAG7D,MAAM,eAAe,QAAQ,IAAI,4BAA4B,KAAK,QAAQ,WAAW,IAAI;AAGzF,MAAM,YACF,QAAQ,IAAI,yBAAyB,KAAK,QAAQ,QAAQ,IAAI,wBAAwB,KAAK,eAAe;AAEvG,MAAM,QAAQ;AAAA,EACjB,aAAa,KAAK,QAAQ,YAAY;AAAA,EACtC,YAAY,KAAK,QAAQ,cAAc,aAAa;AAAA,EACpD,aAAa,KAAK,QAAQ,cAAc,wBAAwB;AAAA,EAChE,UAAU,KAAK,QAAQ,SAAS;AACpC;","names":[]}
|
|
@@ -1,34 +1,53 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
2
|
import { createLoginHandler, createLogoutHandler, createSessionStatusHandler } from "../features/auth/http/api.js";
|
|
3
|
-
import {
|
|
3
|
+
import { createUploadImageHandler } from "../features/image/http/upload.js";
|
|
4
4
|
import {
|
|
5
5
|
createMcpAdminRevokeTokenHandler,
|
|
6
6
|
createMcpAdminRotateTokenHandler,
|
|
7
7
|
createMcpAdminSetEnabledHandler,
|
|
8
8
|
createMcpAdminStatusHandler
|
|
9
9
|
} from "../features/mcp-admin/http/handlers.js";
|
|
10
|
-
import { requireSessionForWrite } from "../modules/auth-guard.js";
|
|
11
|
-
import { createAuthAttemptRateLimit } from "../modules/rate-limit.js";
|
|
10
|
+
import { createCsrfProtection, requireSessionForWrite } from "../modules/auth-guard.js";
|
|
11
|
+
import { createAuthAttemptRateLimit, createSessionAccessRateLimit } from "../modules/rate-limit.js";
|
|
12
12
|
import { createServerEventsHandler } from "../modules/server-events-handler.js";
|
|
13
13
|
import useAsync from "../modules/use-async.js";
|
|
14
14
|
import { createMcpRouter } from "./mcp.js";
|
|
15
|
-
const createApiRouter = (authConfig, mcpAdminService) =>
|
|
16
|
-
|
|
17
|
-
requireSessionForWrite(authConfig)
|
|
18
|
-
|
|
19
|
-
).post(
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
).post(
|
|
15
|
+
const createApiRouter = (authConfig, mcpAdminService) => {
|
|
16
|
+
const csrfProtection = createCsrfProtection(authConfig);
|
|
17
|
+
const requireSession = requireSessionForWrite(authConfig);
|
|
18
|
+
const sessionAccessRateLimit = createSessionAccessRateLimit();
|
|
19
|
+
return Router().use("/mcp", createMcpRouter(authConfig, mcpAdminService)).get("/auth/session", csrfProtection, useAsync(createSessionStatusHandler(authConfig))).post("/auth/login", csrfProtection, createAuthAttemptRateLimit(), useAsync(createLoginHandler(authConfig))).post(
|
|
20
|
+
"/auth/logout",
|
|
21
|
+
sessionAccessRateLimit,
|
|
22
|
+
requireSession,
|
|
23
|
+
csrfProtection,
|
|
24
|
+
useAsync(createLogoutHandler(authConfig))
|
|
25
|
+
).get(
|
|
26
|
+
"/mcp-admin/status",
|
|
27
|
+
sessionAccessRateLimit,
|
|
28
|
+
requireSession,
|
|
29
|
+
csrfProtection,
|
|
30
|
+
useAsync(createMcpAdminStatusHandler(mcpAdminService))
|
|
31
|
+
).post(
|
|
32
|
+
"/mcp-admin/enabled",
|
|
33
|
+
sessionAccessRateLimit,
|
|
34
|
+
requireSession,
|
|
35
|
+
csrfProtection,
|
|
36
|
+
useAsync(createMcpAdminSetEnabledHandler(mcpAdminService))
|
|
37
|
+
).post(
|
|
38
|
+
"/mcp-admin/token/rotate",
|
|
39
|
+
sessionAccessRateLimit,
|
|
40
|
+
requireSession,
|
|
41
|
+
csrfProtection,
|
|
42
|
+
useAsync(createMcpAdminRotateTokenHandler(mcpAdminService))
|
|
43
|
+
).post(
|
|
44
|
+
"/mcp-admin/token/revoke",
|
|
45
|
+
sessionAccessRateLimit,
|
|
46
|
+
requireSession,
|
|
47
|
+
csrfProtection,
|
|
48
|
+
useAsync(createMcpAdminRevokeTokenHandler(mcpAdminService))
|
|
49
|
+
).post("/image", sessionAccessRateLimit, requireSession, csrfProtection, useAsync(createUploadImageHandler())).get("/events", sessionAccessRateLimit, requireSession, csrfProtection, createServerEventsHandler());
|
|
50
|
+
};
|
|
32
51
|
export {
|
|
33
52
|
createApiRouter
|
|
34
53
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/api.ts"],"sourcesContent":["import { Router } from 'express';\nimport { createLoginHandler, createLogoutHandler, createSessionStatusHandler } from '../features/auth/http/api.js';\nimport {
|
|
1
|
+
{"version":3,"sources":["../../src/routes/api.ts"],"sourcesContent":["import { Router } from 'express';\nimport { createLoginHandler, createLogoutHandler, createSessionStatusHandler } from '../features/auth/http/api.js';\nimport { createUploadImageHandler } from '../features/image/http/upload.js';\nimport {\n createMcpAdminRevokeTokenHandler,\n createMcpAdminRotateTokenHandler,\n createMcpAdminSetEnabledHandler,\n createMcpAdminStatusHandler,\n} from '../features/mcp-admin/http/handlers.js';\nimport type { McpAdminService } from '../features/mcp-admin/service.js';\nimport { createCsrfProtection, requireSessionForWrite } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { createAuthAttemptRateLimit, createSessionAccessRateLimit } from '../modules/rate-limit.js';\nimport { createServerEventsHandler } from '../modules/server-events-handler.js';\nimport useAsync from '../modules/use-async.js';\nimport { createMcpRouter } from './mcp.js';\n\ntype McpAdminApiService = Pick<\n McpAdminService,\n 'getStatus' | 'setEnabled' | 'rotateToken' | 'revokeActiveToken' | 'validatePresentedToken'\n>;\n\nexport const createApiRouter = (authConfig: AuthConfig, mcpAdminService: McpAdminApiService) => {\n const csrfProtection = createCsrfProtection(authConfig);\n const requireSession = requireSessionForWrite(authConfig);\n const sessionAccessRateLimit = createSessionAccessRateLimit();\n\n return Router()\n .use('/mcp', createMcpRouter(authConfig, mcpAdminService))\n .get('/auth/session', csrfProtection, useAsync(createSessionStatusHandler(authConfig)))\n .post('/auth/login', csrfProtection, createAuthAttemptRateLimit(), useAsync(createLoginHandler(authConfig)))\n .post(\n '/auth/logout',\n sessionAccessRateLimit,\n requireSession,\n csrfProtection,\n useAsync(createLogoutHandler(authConfig)),\n )\n .get(\n '/mcp-admin/status',\n sessionAccessRateLimit,\n requireSession,\n csrfProtection,\n useAsync(createMcpAdminStatusHandler(mcpAdminService)),\n )\n .post(\n '/mcp-admin/enabled',\n sessionAccessRateLimit,\n requireSession,\n csrfProtection,\n useAsync(createMcpAdminSetEnabledHandler(mcpAdminService)),\n )\n .post(\n '/mcp-admin/token/rotate',\n sessionAccessRateLimit,\n requireSession,\n csrfProtection,\n useAsync(createMcpAdminRotateTokenHandler(mcpAdminService)),\n )\n .post(\n '/mcp-admin/token/revoke',\n sessionAccessRateLimit,\n requireSession,\n csrfProtection,\n useAsync(createMcpAdminRevokeTokenHandler(mcpAdminService)),\n )\n .post('/image', sessionAccessRateLimit, requireSession, csrfProtection, useAsync(createUploadImageHandler()))\n .get('/events', sessionAccessRateLimit, requireSession, csrfProtection, createServerEventsHandler());\n};\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,oBAAoB,qBAAqB,kCAAkC;AACpF,SAAS,gCAAgC;AACzC;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACG;AAEP,SAAS,sBAAsB,8BAA8B;AAE7D,SAAS,4BAA4B,oCAAoC;AACzE,SAAS,iCAAiC;AAC1C,OAAO,cAAc;AACrB,SAAS,uBAAuB;AAOzB,MAAM,kBAAkB,CAAC,YAAwB,oBAAwC;AAC5F,QAAM,iBAAiB,qBAAqB,UAAU;AACtD,QAAM,iBAAiB,uBAAuB,UAAU;AACxD,QAAM,yBAAyB,6BAA6B;AAE5D,SAAO,OAAO,EACT,IAAI,QAAQ,gBAAgB,YAAY,eAAe,CAAC,EACxD,IAAI,iBAAiB,gBAAgB,SAAS,2BAA2B,UAAU,CAAC,CAAC,EACrF,KAAK,eAAe,gBAAgB,2BAA2B,GAAG,SAAS,mBAAmB,UAAU,CAAC,CAAC,EAC1G;AAAA,IACG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,oBAAoB,UAAU,CAAC;AAAA,EAC5C,EACC;AAAA,IACG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,4BAA4B,eAAe,CAAC;AAAA,EACzD,EACC;AAAA,IACG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,gCAAgC,eAAe,CAAC;AAAA,EAC7D,EACC;AAAA,IACG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,iCAAiC,eAAe,CAAC;AAAA,EAC9D,EACC;AAAA,IACG;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,iCAAiC,eAAe,CAAC;AAAA,EAC9D,EACC,KAAK,UAAU,wBAAwB,gBAAgB,gBAAgB,SAAS,yBAAyB,CAAC,CAAC,EAC3G,IAAI,WAAW,wBAAwB,gBAAgB,gBAAgB,0BAA0B,CAAC;AAC3G;","names":[]}
|
|
@@ -4,8 +4,19 @@ import {
|
|
|
4
4
|
createLoginPageSubmitHandler,
|
|
5
5
|
createLogoutPageHandler
|
|
6
6
|
} from "../features/auth/http/pages.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import { createCsrfProtection, createLoginCsrfFailureHandler, requireSessionForWrite } from "../modules/auth-guard.js";
|
|
8
|
+
import { createAuthAttemptRateLimit, createSessionAccessRateLimit } from "../modules/rate-limit.js";
|
|
9
|
+
const createAuthPagesRouter = (authConfig) => {
|
|
10
|
+
const csrfProtection = createCsrfProtection(authConfig);
|
|
11
|
+
const sessionAccessRateLimit = createSessionAccessRateLimit();
|
|
12
|
+
return Router().get("/login", csrfProtection, createLoginPageHandler(authConfig)).post("/login", csrfProtection, createAuthAttemptRateLimit(), createLoginPageSubmitHandler(authConfig)).post(
|
|
13
|
+
"/logout",
|
|
14
|
+
sessionAccessRateLimit,
|
|
15
|
+
requireSessionForWrite(authConfig),
|
|
16
|
+
csrfProtection,
|
|
17
|
+
createLogoutPageHandler(authConfig)
|
|
18
|
+
).use(createLoginCsrfFailureHandler(authConfig));
|
|
19
|
+
};
|
|
9
20
|
export {
|
|
10
21
|
createAuthPagesRouter
|
|
11
22
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/auth-pages.ts"],"sourcesContent":["import { Router } from 'express';\nimport {\n createLoginPageHandler,\n createLoginPageSubmitHandler,\n createLogoutPageHandler,\n} from '../features/auth/http/pages.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { createAuthAttemptRateLimit } from '../modules/rate-limit.js';\n\nexport const createAuthPagesRouter = (authConfig: AuthConfig)
|
|
1
|
+
{"version":3,"sources":["../../src/routes/auth-pages.ts"],"sourcesContent":["import { Router } from 'express';\nimport {\n createLoginPageHandler,\n createLoginPageSubmitHandler,\n createLogoutPageHandler,\n} from '../features/auth/http/pages.js';\nimport { createCsrfProtection, createLoginCsrfFailureHandler, requireSessionForWrite } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { createAuthAttemptRateLimit, createSessionAccessRateLimit } from '../modules/rate-limit.js';\n\nexport const createAuthPagesRouter = (authConfig: AuthConfig) => {\n const csrfProtection = createCsrfProtection(authConfig);\n const sessionAccessRateLimit = createSessionAccessRateLimit();\n\n return Router()\n .get('/login', csrfProtection, createLoginPageHandler(authConfig))\n .post('/login', csrfProtection, createAuthAttemptRateLimit(), createLoginPageSubmitHandler(authConfig))\n .post(\n '/logout',\n sessionAccessRateLimit,\n requireSessionForWrite(authConfig),\n csrfProtection,\n createLogoutPageHandler(authConfig),\n )\n .use(createLoginCsrfFailureHandler(authConfig));\n};\n"],"mappings":"AAAA,SAAS,cAAc;AACvB;AAAA,EACI;AAAA,EACA;AAAA,EACA;AAAA,OACG;AACP,SAAS,sBAAsB,+BAA+B,8BAA8B;AAE5F,SAAS,4BAA4B,oCAAoC;AAElE,MAAM,wBAAwB,CAAC,eAA2B;AAC7D,QAAM,iBAAiB,qBAAqB,UAAU;AACtD,QAAM,yBAAyB,6BAA6B;AAE5D,SAAO,OAAO,EACT,IAAI,UAAU,gBAAgB,uBAAuB,UAAU,CAAC,EAChE,KAAK,UAAU,gBAAgB,2BAA2B,GAAG,6BAA6B,UAAU,CAAC,EACrG;AAAA,IACG;AAAA,IACA;AAAA,IACA,uBAAuB,UAAU;AAAA,IACjC;AAAA,IACA,wBAAwB,UAAU;AAAA,EACtC,EACC,IAAI,8BAA8B,UAAU,CAAC;AACtD;","names":[]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import express, { Router } from "express";
|
|
2
|
+
import { rateLimit } from "express-rate-limit";
|
|
2
3
|
import path from "path";
|
|
3
|
-
import { isAuthenticatedRequest } from "../modules/auth-guard.js";
|
|
4
|
+
import { createCsrfProtection, isAuthenticatedRequest } from "../modules/auth-guard.js";
|
|
4
5
|
import { paths } from "../paths.js";
|
|
6
|
+
const IMAGE_ASSET_RATE_LIMIT_MESSAGE = "Too many image asset requests. Please try again later.";
|
|
5
7
|
const shouldBlockClientRoute = (authConfig, requestPath, authenticated) => {
|
|
6
8
|
if (authConfig.mode !== "password" || authenticated) {
|
|
7
9
|
return false;
|
|
@@ -11,14 +13,72 @@ const shouldBlockClientRoute = (authConfig, requestPath, authenticated) => {
|
|
|
11
13
|
}
|
|
12
14
|
return path.extname(requestPath) === "";
|
|
13
15
|
};
|
|
14
|
-
const
|
|
16
|
+
const createImageAssetAuthRateLimit = (authConfig) => rateLimit({
|
|
17
|
+
windowMs: 15 * 60 * 1e3,
|
|
18
|
+
limit: 10,
|
|
19
|
+
standardHeaders: true,
|
|
20
|
+
legacyHeaders: false,
|
|
21
|
+
skip: (req) => authConfig.mode !== "password" || isAuthenticatedRequest(req),
|
|
22
|
+
handler: (_req, res) => {
|
|
23
|
+
res.setHeader("Cache-Control", "no-store");
|
|
24
|
+
res.status(429).json({
|
|
25
|
+
code: "IMAGE_ASSET_RATE_LIMITED",
|
|
26
|
+
message: IMAGE_ASSET_RATE_LIMIT_MESSAGE
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
const createProtectedImageAssetsMiddleware = (authConfig) => {
|
|
31
|
+
return (req, res, next) => {
|
|
32
|
+
if (authConfig.mode !== "password" || isAuthenticatedRequest(req)) {
|
|
33
|
+
next();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
res.setHeader("Cache-Control", "no-store");
|
|
37
|
+
if (req.headers.accept?.includes("text/html")) {
|
|
38
|
+
const redirectPath = encodeURIComponent(req.originalUrl || req.url || "/");
|
|
39
|
+
res.redirect(303, `/login?next=${redirectPath}`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
res.status(401).end();
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
const createClientRouteCsrfTokenMiddleware = (authConfig) => {
|
|
46
|
+
const csrfProtection = createCsrfProtection(authConfig);
|
|
47
|
+
return (req, res, next) => {
|
|
48
|
+
if (path.extname(req.path) !== "") {
|
|
49
|
+
next();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
csrfProtection(req, res, next);
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const createClientRouter = (authConfig) => Router().use(
|
|
56
|
+
"/assets/images",
|
|
57
|
+
createImageAssetAuthRateLimit(authConfig),
|
|
58
|
+
createProtectedImageAssetsMiddleware(authConfig),
|
|
59
|
+
express.static(paths.imageDir, {
|
|
60
|
+
setHeaders: (res) => {
|
|
61
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
62
|
+
if (authConfig.mode === "password") {
|
|
63
|
+
res.setHeader("Cache-Control", "no-store");
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}),
|
|
67
|
+
(_req, res) => {
|
|
68
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
69
|
+
if (authConfig.mode === "password") {
|
|
70
|
+
res.setHeader("Cache-Control", "no-store");
|
|
71
|
+
}
|
|
72
|
+
res.status(404).end();
|
|
73
|
+
}
|
|
74
|
+
).use((req, res, next) => {
|
|
15
75
|
if (shouldBlockClientRoute(authConfig, req.path, isAuthenticatedRequest(req))) {
|
|
16
76
|
const redirectPath = encodeURIComponent(req.originalUrl || "/");
|
|
17
77
|
res.redirect(303, `/login?next=${redirectPath}`);
|
|
18
78
|
return;
|
|
19
79
|
}
|
|
20
80
|
next();
|
|
21
|
-
}).use(express.static(paths.clientDist, { extensions: ["html"] })).get(/.*/, (_req, res) => {
|
|
81
|
+
}).use(createClientRouteCsrfTokenMiddleware(authConfig)).use(express.static(paths.clientDist, { extensions: ["html"] })).get(/.*/, (_req, res) => {
|
|
22
82
|
res.sendFile(paths.clientIndex);
|
|
23
83
|
});
|
|
24
84
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/client.ts"],"sourcesContent":["import express, { Router } from 'express';\nimport path from 'path';\nimport { isAuthenticatedRequest } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { paths } from '../paths.js';\n\nconst shouldBlockClientRoute = (authConfig: AuthConfig, requestPath: string, authenticated: boolean) => {\n if (authConfig.mode !== 'password' || authenticated) {\n return false;\n }\n\n if (\n requestPath.startsWith('/api') ||\n requestPath.startsWith('/graphql') ||\n requestPath === '/login' ||\n requestPath === '/logout'\n ) {\n return false;\n }\n\n return path.extname(requestPath) === '';\n};\n\nexport const createClientRouter = (authConfig: AuthConfig) =>\n Router()\n .use('/assets/images'
|
|
1
|
+
{"version":3,"sources":["../../src/routes/client.ts"],"sourcesContent":["import express, { type RequestHandler, Router } from 'express';\nimport { rateLimit } from 'express-rate-limit';\nimport path from 'path';\nimport { createCsrfProtection, isAuthenticatedRequest } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { paths } from '../paths.js';\n\nconst IMAGE_ASSET_RATE_LIMIT_MESSAGE = 'Too many image asset requests. Please try again later.';\n\nconst shouldBlockClientRoute = (authConfig: AuthConfig, requestPath: string, authenticated: boolean) => {\n if (authConfig.mode !== 'password' || authenticated) {\n return false;\n }\n\n if (\n requestPath.startsWith('/api') ||\n requestPath.startsWith('/graphql') ||\n requestPath === '/login' ||\n requestPath === '/logout'\n ) {\n return false;\n }\n\n return path.extname(requestPath) === '';\n};\n\nconst createImageAssetAuthRateLimit = (authConfig: AuthConfig) =>\n rateLimit({\n windowMs: 15 * 60 * 1000,\n limit: 10,\n standardHeaders: true,\n legacyHeaders: false,\n skip: (req) => authConfig.mode !== 'password' || isAuthenticatedRequest(req),\n handler: (_req, res) => {\n res.setHeader('Cache-Control', 'no-store');\n res.status(429).json({\n code: 'IMAGE_ASSET_RATE_LIMITED',\n message: IMAGE_ASSET_RATE_LIMIT_MESSAGE,\n });\n },\n });\n\nconst createProtectedImageAssetsMiddleware = (authConfig: AuthConfig): RequestHandler => {\n return (req, res, next) => {\n if (authConfig.mode !== 'password' || isAuthenticatedRequest(req)) {\n next();\n return;\n }\n\n res.setHeader('Cache-Control', 'no-store');\n\n if (req.headers.accept?.includes('text/html')) {\n const redirectPath = encodeURIComponent(req.originalUrl || req.url || '/');\n res.redirect(303, `/login?next=${redirectPath}`);\n return;\n }\n\n res.status(401).end();\n };\n};\n\nconst createClientRouteCsrfTokenMiddleware = (authConfig: AuthConfig) => {\n const csrfProtection = createCsrfProtection(authConfig);\n\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n if (path.extname(req.path) !== '') {\n next();\n return;\n }\n\n csrfProtection(req, res, next);\n };\n};\n\nexport const createClientRouter = (authConfig: AuthConfig) =>\n Router()\n .use(\n '/assets/images',\n createImageAssetAuthRateLimit(authConfig),\n createProtectedImageAssetsMiddleware(authConfig),\n express.static(paths.imageDir, {\n setHeaders: (res) => {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n\n if (authConfig.mode === 'password') {\n res.setHeader('Cache-Control', 'no-store');\n }\n },\n }),\n (_req, res) => {\n res.setHeader('X-Content-Type-Options', 'nosniff');\n\n if (authConfig.mode === 'password') {\n res.setHeader('Cache-Control', 'no-store');\n }\n\n res.status(404).end();\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, `/login?next=${redirectPath}`);\n return;\n }\n\n next();\n })\n .use(createClientRouteCsrfTokenMiddleware(authConfig))\n .use(express.static(paths.clientDist, { extensions: ['html'] }))\n .get(/.*/, (_req, res) => {\n res.sendFile(paths.clientIndex);\n });\n"],"mappings":"AAAA,OAAO,WAAgC,cAAc;AACrD,SAAS,iBAAiB;AAC1B,OAAO,UAAU;AACjB,SAAS,sBAAsB,8BAA8B;AAE7D,SAAS,aAAa;AAEtB,MAAM,iCAAiC;AAEvC,MAAM,yBAAyB,CAAC,YAAwB,aAAqB,kBAA2B;AACpG,MAAI,WAAW,SAAS,cAAc,eAAe;AACjD,WAAO;AAAA,EACX;AAEA,MACI,YAAY,WAAW,MAAM,KAC7B,YAAY,WAAW,UAAU,KACjC,gBAAgB,YAChB,gBAAgB,WAClB;AACE,WAAO;AAAA,EACX;AAEA,SAAO,KAAK,QAAQ,WAAW,MAAM;AACzC;AAEA,MAAM,gCAAgC,CAAC,eACnC,UAAU;AAAA,EACN,UAAU,KAAK,KAAK;AAAA,EACpB,OAAO;AAAA,EACP,iBAAiB;AAAA,EACjB,eAAe;AAAA,EACf,MAAM,CAAC,QAAQ,WAAW,SAAS,cAAc,uBAAuB,GAAG;AAAA,EAC3E,SAAS,CAAC,MAAM,QAAQ;AACpB,QAAI,UAAU,iBAAiB,UAAU;AACzC,QAAI,OAAO,GAAG,EAAE,KAAK;AAAA,MACjB,MAAM;AAAA,MACN,SAAS;AAAA,IACb,CAAC;AAAA,EACL;AACJ,CAAC;AAEL,MAAM,uCAAuC,CAAC,eAA2C;AACrF,SAAO,CAAC,KAAK,KAAK,SAAS;AACvB,QAAI,WAAW,SAAS,cAAc,uBAAuB,GAAG,GAAG;AAC/D,WAAK;AACL;AAAA,IACJ;AAEA,QAAI,UAAU,iBAAiB,UAAU;AAEzC,QAAI,IAAI,QAAQ,QAAQ,SAAS,WAAW,GAAG;AAC3C,YAAM,eAAe,mBAAmB,IAAI,eAAe,IAAI,OAAO,GAAG;AACzE,UAAI,SAAS,KAAK,eAAe,YAAY,EAAE;AAC/C;AAAA,IACJ;AAEA,QAAI,OAAO,GAAG,EAAE,IAAI;AAAA,EACxB;AACJ;AAEA,MAAM,uCAAuC,CAAC,eAA2B;AACrE,QAAM,iBAAiB,qBAAqB,UAAU;AAEtD,SAAO,CAAC,KAAsB,KAAuB,SAA+B;AAChF,QAAI,KAAK,QAAQ,IAAI,IAAI,MAAM,IAAI;AAC/B,WAAK;AACL;AAAA,IACJ;AAEA,mBAAe,KAAK,KAAK,IAAI;AAAA,EACjC;AACJ;AAEO,MAAM,qBAAqB,CAAC,eAC/B,OAAO,EACF;AAAA,EACG;AAAA,EACA,8BAA8B,UAAU;AAAA,EACxC,qCAAqC,UAAU;AAAA,EAC/C,QAAQ,OAAO,MAAM,UAAU;AAAA,IAC3B,YAAY,CAAC,QAAQ;AACjB,UAAI,UAAU,0BAA0B,SAAS;AAEjD,UAAI,WAAW,SAAS,YAAY;AAChC,YAAI,UAAU,iBAAiB,UAAU;AAAA,MAC7C;AAAA,IACJ;AAAA,EACJ,CAAC;AAAA,EACD,CAAC,MAAM,QAAQ;AACX,QAAI,UAAU,0BAA0B,SAAS;AAEjD,QAAI,WAAW,SAAS,YAAY;AAChC,UAAI,UAAU,iBAAiB,UAAU;AAAA,IAC7C;AAEA,QAAI,OAAO,GAAG,EAAE,IAAI;AAAA,EACxB;AACJ,EACC,IAAI,CAAC,KAAK,KAAK,SAAS;AACrB,MAAI,uBAAuB,YAAY,IAAI,MAAM,uBAAuB,GAAG,CAAC,GAAG;AAC3E,UAAM,eAAe,mBAAmB,IAAI,eAAe,GAAG;AAC9D,QAAI,SAAS,KAAK,eAAe,YAAY,EAAE;AAC/C;AAAA,EACJ;AAEA,OAAK;AACT,CAAC,EACA,IAAI,qCAAqC,UAAU,CAAC,EACpD,IAAI,QAAQ,OAAO,MAAM,YAAY,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,EAC9D,IAAI,MAAM,CAAC,MAAM,QAAQ;AACtB,MAAI,SAAS,MAAM,WAAW;AAClC,CAAC;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from "express";
|
|
2
2
|
import { createHandler } from "graphql-http/lib/use/express";
|
|
3
|
-
import { isAuthenticatedRequest, requireSessionForGraphql } from "../modules/auth-guard.js";
|
|
3
|
+
import { createCsrfProtection, isAuthenticatedRequest, requireSessionForGraphql } from "../modules/auth-guard.js";
|
|
4
4
|
import { createMcpAuthMiddleware, createReadOnlyMcpValidationRule } from "../modules/mcp-auth.js";
|
|
5
5
|
import schema from "../schema/index.js";
|
|
6
6
|
const createGraphqlContext = (authConfig) => (req) => ({
|
|
@@ -9,24 +9,28 @@ const createGraphqlContext = (authConfig) => (req) => ({
|
|
|
9
9
|
req: req.raw,
|
|
10
10
|
res: req.context.res
|
|
11
11
|
});
|
|
12
|
-
const createGraphqlRouter = (authConfig, mcpAdminService) =>
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
12
|
+
const createGraphqlRouter = (authConfig, mcpAdminService) => {
|
|
13
|
+
const csrfProtection = createCsrfProtection(authConfig);
|
|
14
|
+
return Router().use(
|
|
15
|
+
"/mcp",
|
|
16
|
+
createMcpAuthMiddleware(authConfig, mcpAdminService),
|
|
17
|
+
createHandler({
|
|
18
|
+
schema,
|
|
19
|
+
context: createGraphqlContext(authConfig),
|
|
20
|
+
validationRules: (_req, _args, specifiedRules) => {
|
|
21
|
+
return [...specifiedRules, createReadOnlyMcpValidationRule()];
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
).use(
|
|
25
|
+
"/",
|
|
26
|
+
requireSessionForGraphql(authConfig),
|
|
27
|
+
csrfProtection,
|
|
28
|
+
createHandler({
|
|
29
|
+
schema,
|
|
30
|
+
context: createGraphqlContext(authConfig)
|
|
31
|
+
})
|
|
32
|
+
);
|
|
33
|
+
};
|
|
30
34
|
export {
|
|
31
35
|
createGraphqlRouter
|
|
32
36
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/routes/graphql.ts"],"sourcesContent":["import { type Request, type Response, Router } from 'express';\nimport { createHandler } from 'graphql-http/lib/use/express';\nimport type { McpAdminService } from '../features/mcp-admin/service.js';\nimport { isAuthenticatedRequest, requireSessionForGraphql } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { createMcpAuthMiddleware, createReadOnlyMcpValidationRule } from '../modules/mcp-auth.js';\nimport schema from '../schema/index.js';\n\ntype McpGraphqlService = Pick<McpAdminService, 'getStatus' | 'validatePresentedToken'>;\ntype GraphqlRequestContext = { raw: Request; context: { res: Response } };\n\nconst createGraphqlContext = (authConfig: AuthConfig) => (req: GraphqlRequestContext) => ({\n authMode: authConfig.mode,\n isAuthenticated: isAuthenticatedRequest(req.raw),\n req: req.raw,\n res: req.context.res,\n});\n\nexport const createGraphqlRouter = (authConfig: AuthConfig, mcpAdminService: McpGraphqlService)
|
|
1
|
+
{"version":3,"sources":["../../src/routes/graphql.ts"],"sourcesContent":["import { type Request, type Response, Router } from 'express';\nimport { createHandler } from 'graphql-http/lib/use/express';\nimport type { McpAdminService } from '../features/mcp-admin/service.js';\nimport { createCsrfProtection, isAuthenticatedRequest, requireSessionForGraphql } from '../modules/auth-guard.js';\nimport type { AuthConfig } from '../modules/auth-mode.js';\nimport { createMcpAuthMiddleware, createReadOnlyMcpValidationRule } from '../modules/mcp-auth.js';\nimport schema from '../schema/index.js';\n\ntype McpGraphqlService = Pick<McpAdminService, 'getStatus' | 'validatePresentedToken'>;\ntype GraphqlRequestContext = { raw: Request; context: { res: Response } };\n\nconst createGraphqlContext = (authConfig: AuthConfig) => (req: GraphqlRequestContext) => ({\n authMode: authConfig.mode,\n isAuthenticated: isAuthenticatedRequest(req.raw),\n req: req.raw,\n res: req.context.res,\n});\n\nexport const createGraphqlRouter = (authConfig: AuthConfig, mcpAdminService: McpGraphqlService) => {\n const csrfProtection = createCsrfProtection(authConfig);\n\n return Router()\n .use(\n '/mcp',\n createMcpAuthMiddleware(authConfig, mcpAdminService),\n createHandler({\n schema,\n context: createGraphqlContext(authConfig),\n validationRules: (_req, _args, specifiedRules) => {\n return [...specifiedRules, createReadOnlyMcpValidationRule()];\n },\n }),\n )\n .use(\n '/',\n requireSessionForGraphql(authConfig),\n csrfProtection,\n createHandler({\n schema,\n context: createGraphqlContext(authConfig),\n }),\n );\n};\n"],"mappings":"AAAA,SAAsC,cAAc;AACpD,SAAS,qBAAqB;AAE9B,SAAS,sBAAsB,wBAAwB,gCAAgC;AAEvF,SAAS,yBAAyB,uCAAuC;AACzE,OAAO,YAAY;AAKnB,MAAM,uBAAuB,CAAC,eAA2B,CAAC,SAAgC;AAAA,EACtF,UAAU,WAAW;AAAA,EACrB,iBAAiB,uBAAuB,IAAI,GAAG;AAAA,EAC/C,KAAK,IAAI;AAAA,EACT,KAAK,IAAI,QAAQ;AACrB;AAEO,MAAM,sBAAsB,CAAC,YAAwB,oBAAuC;AAC/F,QAAM,iBAAiB,qBAAqB,UAAU;AAEtD,SAAO,OAAO,EACT;AAAA,IACG;AAAA,IACA,wBAAwB,YAAY,eAAe;AAAA,IACnD,cAAc;AAAA,MACV;AAAA,MACA,SAAS,qBAAqB,UAAU;AAAA,MACxC,iBAAiB,CAAC,MAAM,OAAO,mBAAmB;AAC9C,eAAO,CAAC,GAAG,gBAAgB,gCAAgC,CAAC;AAAA,MAChE;AAAA,IACJ,CAAC;AAAA,EACL,EACC;AAAA,IACG;AAAA,IACA,yBAAyB,UAAU;AAAA,IACnC;AAAA,IACA,cAAc;AAAA,MACV;AAAA,MACA,SAAS,qBAAqB,UAAU;AAAA,IAC5C,CAAC;AAAA,EACL;AACR;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as n}from"./note-vendor-i1M2FSI2.js";import{p as m,T as d}from"./index-BFfQKAUY.js";import{a as e}from"./graph-vendor-CUxe67Lr.js";const p=new Map([["bold",e.createElement(e.Fragment,null,e.createElement("path",{d:"M108,84a16,16,0,1,1,16,16A16,16,0,0,1,108,84Zm128,44A108,108,0,1,1,128,20,108.12,108.12,0,0,1,236,128Zm-24,0a84,84,0,1,0-84,84A84.09,84.09,0,0,0,212,128Zm-72,36.68V132a20,20,0,0,0-20-20,12,12,0,0,0-4,23.32V168a20,20,0,0,0,20,20,12,12,0,0,0,4-23.32Z"}))],["duotone",e.createElement(e.Fragment,null,e.createElement("path",{d:"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z",opacity:"0.2"}),e.createElement("path",{d:"M144,176a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176Zm88-48A104,104,0,1,1,128,24,104.11,104.11,0,0,1,232,128Zm-16,0a88,88,0,1,0-88,88A88.1,88.1,0,0,0,216,128ZM124,96a12,12,0,1,0-12-12A12,12,0,0,0,124,96Z"}))],["fill",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-4,48a12,12,0,1,1-12,12A12,12,0,0,1,124,72Zm12,112a16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40a8,8,0,0,1,0,16Z"}))],["light",e.createElement(e.Fragment,null,e.createElement("path",{d:"M142,176a6,6,0,0,1-6,6,14,14,0,0,1-14-14V128a2,2,0,0,0-2-2,6,6,0,0,1,0-12,14,14,0,0,1,14,14v40a2,2,0,0,0,2,2A6,6,0,0,1,142,176ZM124,94a10,10,0,1,0-10-10A10,10,0,0,0,124,94Zm106,34A102,102,0,1,1,128,26,102.12,102.12,0,0,1,230,128Zm-12,0a90,90,0,1,0-90,90A90.1,90.1,0,0,0,218,128Z"}))],["regular",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm16-40a8,8,0,0,1-8,8,16,16,0,0,1-16-16V128a8,8,0,0,1,0-16,16,16,0,0,1,16,16v40A8,8,0,0,1,144,176ZM112,84a12,12,0,1,1,12,12A12,12,0,0,1,112,84Z"}))],["thin",e.createElement(e.Fragment,null,e.createElement("path",{d:"M140,176a4,4,0,0,1-4,4,12,12,0,0,1-12-12V128a4,4,0,0,0-4-4,4,4,0,0,1,0-8,12,12,0,0,1,12,12v40a4,4,0,0,0,4,4A4,4,0,0,1,140,176ZM124,92a8,8,0,1,0-8-8A8,8,0,0,0,124,92Zm104,36A100,100,0,1,1,128,28,100.11,100.11,0,0,1,228,128Zm-8,0a92,92,0,1,0-92,92A92.1,92.1,0,0,0,220,128Z"}))]]),g=new Map([["bold",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,20A108,108,0,1,0,236,128,108.12,108.12,0,0,0,128,20Zm0,192a84,84,0,1,1,84-84A84.09,84.09,0,0,1,128,212Zm-12-80V80a12,12,0,0,1,24,0v52a12,12,0,0,1-24,0Zm28,40a16,16,0,1,1-16-16A16,16,0,0,1,144,172Z"}))],["duotone",e.createElement(e.Fragment,null,e.createElement("path",{d:"M224,128a96,96,0,1,1-96-96A96,96,0,0,1,224,128Z",opacity:"0.2"}),e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"}))],["fill",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm-8,56a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm8,104a12,12,0,1,1,12-12A12,12,0,0,1,128,184Z"}))],["light",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,26A102,102,0,1,0,230,128,102.12,102.12,0,0,0,128,26Zm0,192a90,90,0,1,1,90-90A90.1,90.1,0,0,1,128,218Zm-6-82V80a6,6,0,0,1,12,0v56a6,6,0,0,1-12,0Zm16,36a10,10,0,1,1-10-10A10,10,0,0,1,138,172Z"}))],["regular",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,24A104,104,0,1,0,232,128,104.11,104.11,0,0,0,128,24Zm0,192a88,88,0,1,1,88-88A88.1,88.1,0,0,1,128,216Zm-8-80V80a8,8,0,0,1,16,0v56a8,8,0,0,1-16,0Zm20,36a12,12,0,1,1-12-12A12,12,0,0,1,140,172Z"}))],["thin",e.createElement(e.Fragment,null,e.createElement("path",{d:"M128,28A100,100,0,1,0,228,128,100.11,100.11,0,0,0,128,28Zm0,192a92,92,0,1,1,92-92A92.1,92.1,0,0,1,128,220Zm-4-84V80a4,4,0,0,1,8,0v56a4,4,0,0,1-8,0Zm12,36a8,8,0,1,1-8-8A8,8,0,0,1,136,172Z"}))]]),l=e.forwardRef((a,t)=>e.createElement(m,{ref:t,...a,weights:p}));l.displayName="InfoIcon";const E=l,c=e.forwardRef((a,t)=>e.createElement(m,{ref:t,...a,weights:g}));c.displayName="WarningCircleIcon";const i=c,f=({children:a,className:t="",tone:r="info"})=>{const o=r==="danger"?i:E,Z=r==="danger"?"border-border-error/70 bg-accent-soft-danger/60":"border-border-subtle bg-hover-subtle/50",s=r==="danger"?"text-fg-error":"text-fg-tertiary",A=r==="danger"?"error":"secondary";return n.jsxs("div",{className:`flex items-center gap-2.5 rounded-[12px] border px-4 py-3 ${Z} ${t}`,children:[n.jsx(o,{className:`h-4 w-4 shrink-0 ${s}`}),n.jsx(d,{as:"div",variant:"meta",weight:"medium",tone:A,children:a})]})};export{f as C,i as m};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{j as t}from"./note-vendor-i1M2FSI2.js";import{a0 as _,P as C,a3 as U,i as G,a4 as J,a5 as Y,a6 as X,a7 as ee,q as ne,N as te,J as re,T as N,a8 as oe}from"./index-BFfQKAUY.js";import{a as i,F as se}from"./graph-vendor-CUxe67Lr.js";import"./note-core-Z6kqulGB.js";const ae=s=>{let a=0;for(let r=0;r<s.length;r++)a+=s.charCodeAt(r);return a},ie={background:"#f4f6f8",nodeHub:"#2c2f36",nodeSelected:"#111318",nodeConnected:"#636b76",nodeDefault:["#d6dbe1","#c8ced6","#b7bec7","#a7afb9"],nodeDimmed:["rgba(214,219,225,0.28)","rgba(200,206,214,0.24)","rgba(183,190,199,0.24)","rgba(167,175,185,0.2)"],nodeHubDimmed:"rgba(44,47,54,0.16)",nodeStroke:"#eef1f4",nodeSelectedStroke:"#a6b0bc",labelBackground:"rgba(246,248,250,0.92)",labelText:"#222831",labelFontFamily:"Pretendard Variable, Pretendard, system-ui, sans-serif",linkIdle:"rgba(99, 107, 117, 0.34)",linkConnected:"#68717c",linkDimmed:"rgba(127, 136, 146, 0.12)",legendHub:"#2c2f36"},le={background:"#121316",nodeHub:"#d6dce3",nodeSelected:"#eef1f5",nodeConnected:"#9099a4",nodeDefault:["#343a43","#2d333c","#262c35","#20262f"],nodeDimmed:["rgba(52,58,67,0.28)","rgba(45,51,60,0.24)","rgba(38,44,53,0.22)","rgba(32,38,47,0.2)"],nodeHubDimmed:"rgba(214,220,227,0.16)",nodeStroke:"#171c23",nodeSelectedStroke:"#7f8a97",labelBackground:"rgba(16,18,22,0.9)",labelText:"#eef2f6",labelFontFamily:"Pretendard Variable, Pretendard, system-ui, sans-serif",linkIdle:"rgba(118, 127, 138, 0.42)",linkConnected:"#a2abb6",linkDimmed:"rgba(118, 127, 138, 0.1)",legendHub:"#d6dce3"};function D(s){return s==="dark"?le:ie}function de(s,a){const r=D(s),{connections:g,colorIndex:y,selectedNodeId:d,nodeId:b,isConnected:c}=a,h=d===b;return d!==null&&!h&&!c?g>3?r.nodeHubDimmed:r.nodeDimmed[y%r.nodeDimmed.length]:h?r.nodeSelected:c?r.nodeConnected:g>3?r.nodeHub:r.nodeDefault[y%r.nodeDefault.length]}function ce(s,a){const r=D(s);return a.selectedNodeId!==null&&!a.isConnected?r.linkDimmed:a.isConnected?r.linkConnected:r.linkIdle}function ue(s,a){const r=D(s);return`${a.emphasize?"700":"400"} ${a.fontSize}px ${r.labelFontFamily}`}function z(s){return s<=1?3.5:s<=3?4.5:s<=6?5.5:Math.min(7,5.5+Math.sqrt(s)*.5)}const fe=t.jsx(C,{title:"Knowledge Graph",description:t.jsx(G,{width:184,height:16,className:"rounded-full"}),children:t.jsx("div",{className:"flex h-[600px] items-center justify-center",children:t.jsx(G,{width:"100%",height:"100%"})})});function he(){const s=J(),a=i.useRef(null),r=i.useRef(null),[g,y]=i.useState({width:800,height:600}),[d,b]=i.useState(null),{theme:c}=Y(e=>e),h=D(c),v=i.useRef(h);v.current=h;const{data:w}=X({queryKey:ne.notes.graph(),queryFn:async()=>{const e=await ee();if(e.type==="error")throw e;return e.noteGraph}}),l=i.useMemo(()=>{if(w.nodes.length===0)return null;const e=w.nodes.filter(o=>o.connections>0);if(e.length===0)return null;const n=new Set(e.map(o=>o.id));return{nodes:e,links:w.links.filter(o=>n.has(o.source)&&n.has(o.target))}},[w]);i.useEffect(()=>{if(!l)return;const e=()=>{if(!a.current)return;const n=a.current.getBoundingClientRect();y({width:n.width,height:Math.max(600,window.innerHeight-150)})};return e(),window.addEventListener("resize",e),()=>window.removeEventListener("resize",e)},[l]),i.useEffect(()=>{if(!l||!r.current)return;const e=window.setTimeout(()=>{r.current?.zoomToFit(400,50)},500);return()=>window.clearTimeout(e)},[l]);const R=i.useRef(d);R.current=d;const B=i.useCallback(e=>{if(R.current===e.id){s({to:te,params:{id:e.id}});return}b(e.id)},[s]),L=i.useCallback(()=>{b(null)},[]),K=i.useCallback(e=>{a.current&&(a.current.style.cursor=e?"pointer":"default")},[]),I=i.useRef(!1),q=i.useCallback(()=>{I.current||(I.current=!0,r.current?.enableZoomInteraction(!1))},[]),A=i.useCallback(()=>{I.current=!1,r.current?.enableZoomInteraction(!0)},[]),H=i.useRef(new Map),O=i.useMemo(()=>{const e=new Map;if(l)for(const n of l.links)e.has(n.source)||e.set(n.source,new Set),e.has(n.target)||e.set(n.target,new Set),e.get(n.source)?.add(n.target),e.get(n.target)?.add(n.source);return e},[l]);H.current=O;const Z=i.useCallback((e,n,o)=>{const u=v.current,f=d,p=H.current,m=z(e.connections),x=e.x||0,S=e.y||0,k=f===e.id,T=f?p.get(f)?.has(e.id)??!1:!1,V=f!==null&&!k&&!T,W=ae(e.id);if(n.beginPath(),n.arc(x,S,m,0,Math.PI*2),n.fillStyle=de(c,{connections:e.connections,colorIndex:W,selectedNodeId:f,nodeId:e.id,isConnected:T}),n.fill(),V)return;n.strokeStyle=u.nodeStroke,n.lineWidth=(k?2:1)/o,n.stroke(),k&&(n.beginPath(),n.arc(x,S,m+2/o,0,Math.PI*2),n.strokeStyle=u.nodeSelectedStroke,n.lineWidth=1.5/o,n.stroke());const Q=T&&e.connections>=4;if(k||Q||o>2.5){const P=e.title||"Untitled",E=Math.max(10/o,2.5);n.font=ue(c,{fontSize:E,emphasize:k}),n.textAlign="center",n.textBaseline="top";const M=n.measureText(P).width,j=2/o,F=S+m+3/o;n.fillStyle=u.labelBackground,n.fillRect(x-M/2-j,F,M+j*2,E+j*2),n.fillStyle=u.labelText,n.fillText(P,x,F+j)}},[d,c]),$=i.useCallback((e,n,o)=>{const u=d,f=e.source,p=e.target,m=u?f.id===u||p.id===u:!1;n.beginPath(),n.moveTo(f.x||0,f.y||0),n.lineTo(p.x||0,p.y||0),n.strokeStyle=ce(c,{selectedNodeId:u,isConnected:m}),n.lineWidth=m?2/o:.5/o,n.stroke()},[d,c]);return l?t.jsx(C,{title:"Knowledge Graph",description:`${l.nodes.length} linked notes, ${l.links.length} connections`,children:t.jsxs("div",{ref:a,className:"surface-base graph-canvas relative overflow-hidden",style:{"--graph-bg":h.background},children:[d&&(()=>{const e=l.nodes.find(n=>n.id===d);return e?t.jsxs("div",{className:"surface-floating absolute top-3 left-3 z-10 flex items-center gap-2 px-3 py-2",children:[t.jsx(N,{as:"span",variant:"meta",weight:"semibold",truncate:!0,className:"max-w-48",children:e.title}),t.jsxs(N,{as:"span",variant:"label",tone:"tertiary",children:[e.connections," links"]}),t.jsx("button",{type:"button",onClick:()=>b(null),className:"focus-ring-soft ml-1 flex h-6 w-6 cursor-pointer items-center justify-center rounded-[8px] text-fg-tertiary transition-colors hover:bg-hover-subtle hover:text-fg-default","aria-label":"Deselect node",children:t.jsx(oe,{className:"h-3.5 w-3.5"})})]}):null})(),t.jsxs("div",{className:"surface-floating absolute top-3 right-3 z-10 flex flex-col gap-1.5 px-3 py-2.5",children:[t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("span",{className:"legend-dot h-2.5 w-2.5 shrink-0 rounded-full",style:{"--legend-color":h.legendHub}}),t.jsx(N,{as:"span",variant:"label",weight:"medium",tone:"secondary",children:"Hub notes (4+ connections)"})]}),t.jsxs("div",{className:"flex items-center gap-2",children:[t.jsx("span",{className:"legend-dot h-2.5 w-2.5 shrink-0 rounded-full",style:{"--legend-color":h.nodeConnected}}),t.jsx(N,{as:"span",variant:"label",weight:"medium",tone:"secondary",children:"Connected notes"})]})]}),t.jsx(se,{ref:r,graphData:l,width:g.width,height:g.height,nodeId:"id",nodeLabel:"",nodeCanvasObject:Z,nodePointerAreaPaint:(e,n,o)=>{o.beginPath(),o.arc(e.x||0,e.y||0,Math.max(z(e.connections)+4,10),0,2*Math.PI),o.fillStyle=n,o.fill()},linkCanvasObject:$,linkCanvasObjectMode:()=>"replace",linkDirectionalParticles:0,onNodeClick:B,onNodeHover:K,onBackgroundClick:L,onNodeDrag:q,onNodeDragEnd:A,warmupTicks:30,cooldownTicks:80,d3AlphaDecay:.05,d3VelocityDecay:.3,enableZoomInteraction:!0,enablePanInteraction:!0,minZoom:.3,maxZoom:5})]})}):t.jsx(C,{title:"Knowledge Graph",description:"0 linked notes, 0 connections",children:t.jsx(re,{title:"No constellations yet",description:"Link your notes together and watch your own starry sky unfold"})})}function ke(){return t.jsx(_,{fallback:fe,errorTitle:"Failed to load graph",errorDescription:"Retry loading your linked note constellation",renderError:({error:s,retry:a})=>t.jsx(C,{title:"Knowledge Graph",children:t.jsx(U,{title:"Failed to load graph",description:"Retry loading your linked note constellation",error:s,onRetry:a})}),children:t.jsx(he,{})})}export{ke as default};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a}from"./graph-vendor-CUxe67Lr.js";import{p as m}from"./index-BFfQKAUY.js";const n=new Map([["bold",a.createElement(a.Fragment,null,a.createElement("path",{d:"M144,96a16,16,0,1,1,16,16A16,16,0,0,1,144,96Zm92-40V200a20,20,0,0,1-20,20H40a20,20,0,0,1-20-20V56A20,20,0,0,1,40,36H216A20,20,0,0,1,236,56ZM44,60v79.72l33.86-33.86a20,20,0,0,1,28.28,0L147.31,147l17.18-17.17a20,20,0,0,1,28.28,0L212,149.09V60Zm0,136H162.34L92,125.66l-48,48Zm168,0V183l-33.37-33.37L164.28,164l32,32Z"}))],["duotone",a.createElement(a.Fragment,null,a.createElement("path",{d:"M224,56V178.06l-39.72-39.72a8,8,0,0,0-11.31,0L147.31,164,97.66,114.34a8,8,0,0,0-11.32,0L32,168.69V56a8,8,0,0,1,8-8H216A8,8,0,0,1,224,56Z",opacity:"0.2"}),a.createElement("path",{d:"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V158.75l-26.07-26.06a16,16,0,0,0-22.63,0l-20,20-44-44a16,16,0,0,0-22.62,0L40,149.37V56ZM40,172l52-52,80,80H40Zm176,28H194.63l-36-36,20-20L216,181.38V200ZM144,100a12,12,0,1,1,12,12A12,12,0,0,1,144,100Z"}))],["fill",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40ZM156,88a12,12,0,1,1-12,12A12,12,0,0,1,156,88Zm60,112H40V160.69l46.34-46.35a8,8,0,0,1,11.32,0h0L165,181.66a8,8,0,0,0,11.32-11.32l-17.66-17.65L173,138.34a8,8,0,0,1,11.31,0L216,170.07V200Z"}))],["light",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216,42H40A14,14,0,0,0,26,56V200a14,14,0,0,0,14,14H216a14,14,0,0,0,14-14V56A14,14,0,0,0,216,42ZM40,54H216a2,2,0,0,1,2,2V163.57L188.53,134.1a14,14,0,0,0-19.8,0l-21.42,21.42L101.9,110.1a14,14,0,0,0-19.8,0L38,154.2V56A2,2,0,0,1,40,54ZM38,200V171.17l52.58-52.58a2,2,0,0,1,2.84,0L176.83,202H40A2,2,0,0,1,38,200Zm178,2H193.8l-38-38,21.41-21.42a2,2,0,0,1,2.83,0l38,38V200A2,2,0,0,1,216,202ZM146,100a10,10,0,1,1,10,10A10,10,0,0,1,146,100Z"}))],["regular",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216,40H40A16,16,0,0,0,24,56V200a16,16,0,0,0,16,16H216a16,16,0,0,0,16-16V56A16,16,0,0,0,216,40Zm0,16V158.75l-26.07-26.06a16,16,0,0,0-22.63,0l-20,20-44-44a16,16,0,0,0-22.62,0L40,149.37V56ZM40,172l52-52,80,80H40Zm176,28H194.63l-36-36,20-20L216,181.38V200ZM144,100a12,12,0,1,1,12,12A12,12,0,0,1,144,100Z"}))],["thin",a.createElement(a.Fragment,null,a.createElement("path",{d:"M216,44H40A12,12,0,0,0,28,56V200a12,12,0,0,0,12,12H216a12,12,0,0,0,12-12V56A12,12,0,0,0,216,44ZM40,52H216a4,4,0,0,1,4,4V168.4l-32.89-32.89a12,12,0,0,0-17,0l-22.83,22.83-46.82-46.83a12,12,0,0,0-17,0L36,159V56A4,4,0,0,1,40,52ZM36,200V170.34l53.17-53.17a4,4,0,0,1,5.66,0L181.66,204H40A4,4,0,0,1,36,200Zm180,4H193l-40-40,22.83-22.83a4,4,0,0,1,5.66,0L220,179.71V200A4,4,0,0,1,216,204ZM148,100a8,8,0,1,1,8,8A8,8,0,0,1,148,100Z"}))]]),e=a.forwardRef((l,t)=>a.createElement(m,{ref:t,...l,weights:n}));e.displayName="ImageIcon";const Z=e;export{Z as I};
|