botnote 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +277 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +320 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/db/client.d.ts +10 -0
- package/dist/db/client.js +16 -0
- package/dist/db/client.js.map +1 -0
- package/dist/db/migrate.d.ts +1 -0
- package/dist/db/migrate.js +57 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/migrations/0001_init.sql +89 -0
- package/dist/db/migrations/0002_tasks_due_at.sql +16 -0
- package/dist/db/migrations/0003_task_priority_and_sequence.sql +45 -0
- package/dist/db/migrations/0004_pinned.sql +8 -0
- package/dist/db/migrations/0005_nullable_title_and_parent.sql +5 -0
- package/dist/db/migrations/0006_project_color_icon.sql +3 -0
- package/dist/db/migrations/0007_tokens.sql +14 -0
- package/dist/db/migrations/0008_simplify_kinds_drop_actors.sql +21 -0
- package/dist/db/migrations/0009_sessions.sql +14 -0
- package/dist/db/migrations/0010_completed_at.sql +17 -0
- package/dist/db/migrations/0011_completed_at_backfill_fix.sql +14 -0
- package/dist/db/migrations/0012_completed_at_backfill_due.sql +16 -0
- package/dist/db/migrations/0013_due_at_noon_utc.sql +13 -0
- package/dist/db/schema.d.ts +811 -0
- package/dist/db/schema.js +112 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/mcp/http-client.d.ts +154 -0
- package/dist/mcp/http-client.js +113 -0
- package/dist/mcp/http-client.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +492 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/rest/routes.d.ts +3 -0
- package/dist/rest/routes.js +320 -0
- package/dist/rest/routes.js.map +1 -0
- package/dist/rest/server.d.ts +12 -0
- package/dist/rest/server.js +282 -0
- package/dist/rest/server.js.map +1 -0
- package/dist/service/embedding.d.ts +25 -0
- package/dist/service/embedding.js +112 -0
- package/dist/service/embedding.js.map +1 -0
- package/dist/service/entities.d.ts +22 -0
- package/dist/service/entities.js +174 -0
- package/dist/service/entities.js.map +1 -0
- package/dist/service/index.d.ts +8 -0
- package/dist/service/index.js +8 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/opening_brief.d.ts +14 -0
- package/dist/service/opening_brief.js +104 -0
- package/dist/service/opening_brief.js.map +1 -0
- package/dist/service/projects.d.ts +8 -0
- package/dist/service/projects.js +36 -0
- package/dist/service/projects.js.map +1 -0
- package/dist/service/search.d.ts +15 -0
- package/dist/service/search.js +101 -0
- package/dist/service/search.js.map +1 -0
- package/dist/service/sessions.d.ts +25 -0
- package/dist/service/sessions.js +89 -0
- package/dist/service/sessions.js.map +1 -0
- package/dist/service/tasks.d.ts +23 -0
- package/dist/service/tasks.js +130 -0
- package/dist/service/tasks.js.map +1 -0
- package/dist/service/tokens.d.ts +18 -0
- package/dist/service/tokens.js +41 -0
- package/dist/service/tokens.js.map +1 -0
- package/dist/service/types.d.ts +294 -0
- package/dist/service/types.js +132 -0
- package/dist/service/types.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/package.json +73 -0
- package/web/dist/assets/index-Bddg7h_x.css +1 -0
- package/web/dist/assets/index-DYW6stVx.js +542 -0
- package/web/dist/favicon.svg +13 -0
- package/web/dist/index.html +17 -0
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { get, getByKey, link, listRelated, recent, remove, update, write } from "../service/entities.js";
|
|
3
|
+
import { formatOpeningBrief, openingBrief } from "../service/opening_brief.js";
|
|
4
|
+
import { createProject, getProject, getProjectByKey, listProjects, updateProject } from "../service/projects.js";
|
|
5
|
+
import { search } from "../service/search.js";
|
|
6
|
+
import { createSession, revokeSession, verifyMasterPassword } from "../service/sessions.js";
|
|
7
|
+
import { tasksRange } from "../service/tasks.js";
|
|
8
|
+
import { createToken, listTokens, revokeToken } from "../service/tokens.js";
|
|
9
|
+
import { CreateNoteInput, CreateProjectInput, CreateTaskInput, CreateTokenInput, LinkInput, OpeningBriefInput, RecentInput, SearchInput, TasksRangeInput, UpdateInput, UpdateProjectInput, Uuid } from "../service/types.js";
|
|
10
|
+
import { VERSION } from "../version.js";
|
|
11
|
+
const IdParams = z.object({ id: Uuid });
|
|
12
|
+
const KeyParams = z.object({ key: z.string() });
|
|
13
|
+
const KeySeqParams = z.object({
|
|
14
|
+
key: z.string().regex(/^[A-Z][A-Z0-9_]*$/),
|
|
15
|
+
seq: z.coerce.number().int().positive()
|
|
16
|
+
});
|
|
17
|
+
const OpeningBriefBody = OpeningBriefInput.omit({ projectId: true }).optional();
|
|
18
|
+
const LinkBody = LinkInput.omit({ fromId: true });
|
|
19
|
+
function publicToken(t) {
|
|
20
|
+
return {
|
|
21
|
+
id: t.id,
|
|
22
|
+
name: t.name,
|
|
23
|
+
prefix: t.prefix,
|
|
24
|
+
lastUsedAt: t.lastUsedAt,
|
|
25
|
+
createdAt: t.createdAt
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export async function registerRoutes(rawApp, ctx) {
|
|
29
|
+
const app = rawApp.withTypeProvider();
|
|
30
|
+
app.get("/health", async () => ({ ok: true, version: VERSION }));
|
|
31
|
+
// ----- projects -----
|
|
32
|
+
app.post("/v1/projects", { schema: { tags: ["projects"], summary: "Create a project", body: CreateProjectInput } }, async (req) => createProject(ctx.db, CreateProjectInput.parse(req.body)));
|
|
33
|
+
app.get("/v1/projects", { schema: { tags: ["projects"], summary: "List projects" } }, async () => listProjects(ctx.db));
|
|
34
|
+
app.get("/v1/projects/:id", { schema: { tags: ["projects"], summary: "Fetch a project by id (includes AGENTS.md)", params: IdParams } }, async (req, reply) => {
|
|
35
|
+
const { id } = IdParams.parse(req.params);
|
|
36
|
+
const p = await getProject(ctx.db, id);
|
|
37
|
+
if (!p)
|
|
38
|
+
return reply.code(404).send({ error: "not_found" });
|
|
39
|
+
return p;
|
|
40
|
+
});
|
|
41
|
+
app.patch("/v1/projects/:id", {
|
|
42
|
+
schema: {
|
|
43
|
+
tags: ["projects"],
|
|
44
|
+
summary: "Update project (name, color, icon, agents_md)",
|
|
45
|
+
params: IdParams,
|
|
46
|
+
body: UpdateProjectInput
|
|
47
|
+
}
|
|
48
|
+
}, async (req) => {
|
|
49
|
+
const { id } = IdParams.parse(req.params);
|
|
50
|
+
const body = UpdateProjectInput.parse(req.body);
|
|
51
|
+
return updateProject(ctx.db, id, body);
|
|
52
|
+
});
|
|
53
|
+
app.get("/v1/projects/by-key/:key", { schema: { tags: ["projects"], summary: "Fetch a project by key", params: KeyParams } }, async (req, reply) => {
|
|
54
|
+
const { key } = KeyParams.parse(req.params);
|
|
55
|
+
const p = await getProjectByKey(ctx.db, key);
|
|
56
|
+
if (!p)
|
|
57
|
+
return reply.code(404).send({ error: "not_found" });
|
|
58
|
+
return p;
|
|
59
|
+
});
|
|
60
|
+
app.post("/v1/projects/:id/opening-brief", {
|
|
61
|
+
schema: {
|
|
62
|
+
tags: ["brief"],
|
|
63
|
+
summary: "Opening brief for a project",
|
|
64
|
+
description: "Returns the agent context bundle: AGENTS.md, pinned notes, open tasks, pending decisions, recent activity. Also returns a markdown-formatted version.",
|
|
65
|
+
params: IdParams,
|
|
66
|
+
body: OpeningBriefBody
|
|
67
|
+
}
|
|
68
|
+
}, async (req) => {
|
|
69
|
+
const { id } = IdParams.parse(req.params);
|
|
70
|
+
const bodyOpts = OpeningBriefBody.parse(req.body);
|
|
71
|
+
const input = OpeningBriefInput.parse({ projectId: id, recentLimit: bodyOpts?.recentLimit });
|
|
72
|
+
const brief = await openingBrief(ctx.db, input);
|
|
73
|
+
return { ...brief, markdown: formatOpeningBrief(brief) };
|
|
74
|
+
});
|
|
75
|
+
// Workspace-or-project opening brief. The path-param variant above is kept
|
|
76
|
+
// for clients that already integrate against it; the MCP HTTP client and
|
|
77
|
+
// new plugin code uses this single entry point and passes projectId in the
|
|
78
|
+
// body (or omits it for a workspace-wide brief).
|
|
79
|
+
app.post("/v1/opening-brief", {
|
|
80
|
+
schema: {
|
|
81
|
+
tags: ["brief"],
|
|
82
|
+
summary: "Opening brief (project-scoped or workspace-wide)",
|
|
83
|
+
body: OpeningBriefInput.partial().optional()
|
|
84
|
+
}
|
|
85
|
+
}, async (req) => {
|
|
86
|
+
const input = OpeningBriefInput.parse(req.body ?? {});
|
|
87
|
+
const brief = await openingBrief(ctx.db, input);
|
|
88
|
+
return { ...brief, markdown: formatOpeningBrief(brief) };
|
|
89
|
+
});
|
|
90
|
+
// ----- entities (read) -----
|
|
91
|
+
app.get("/v1/entities/:id", { schema: { tags: ["entities"], summary: "Fetch an entity by id", params: IdParams } }, async (req, reply) => {
|
|
92
|
+
const { id } = IdParams.parse(req.params);
|
|
93
|
+
const e = await get(ctx.db, id);
|
|
94
|
+
if (!e)
|
|
95
|
+
return reply.code(404).send({ error: "not_found" });
|
|
96
|
+
return e;
|
|
97
|
+
});
|
|
98
|
+
app.get("/v1/projects/by-key/:key/entities/by-seq/:seq", {
|
|
99
|
+
schema: {
|
|
100
|
+
tags: ["entities"],
|
|
101
|
+
summary: "Fetch an entity by its human-readable identifier (e.g. BOT-12)",
|
|
102
|
+
params: KeySeqParams
|
|
103
|
+
}
|
|
104
|
+
}, async (req, reply) => {
|
|
105
|
+
const { key, seq } = KeySeqParams.parse(req.params);
|
|
106
|
+
const e = await getByKey(ctx.db, key, seq);
|
|
107
|
+
if (!e)
|
|
108
|
+
return reply.code(404).send({ error: "not_found" });
|
|
109
|
+
return e;
|
|
110
|
+
});
|
|
111
|
+
app.get("/v1/entities/:id/related", {
|
|
112
|
+
schema: {
|
|
113
|
+
tags: ["entities"],
|
|
114
|
+
summary: "List entities whose parent_id is this id (e.g. notes linked to a task)",
|
|
115
|
+
params: IdParams
|
|
116
|
+
}
|
|
117
|
+
}, async (req) => {
|
|
118
|
+
const { id } = IdParams.parse(req.params);
|
|
119
|
+
return listRelated(ctx.db, id);
|
|
120
|
+
});
|
|
121
|
+
app.post("/v1/recent", {
|
|
122
|
+
schema: {
|
|
123
|
+
tags: ["entities"],
|
|
124
|
+
summary: "Recent entities (filterable by project, kinds, since)",
|
|
125
|
+
body: RecentInput.optional()
|
|
126
|
+
}
|
|
127
|
+
}, async (req) => recent(ctx.db, RecentInput.parse(req.body ?? {})));
|
|
128
|
+
app.post("/v1/search", {
|
|
129
|
+
schema: {
|
|
130
|
+
tags: ["search"],
|
|
131
|
+
summary: "Hybrid retrieval (BM25 + cosine + time decay, RRF merged)",
|
|
132
|
+
description: "If OPENAI_API_KEY is set, the query is embedded and cosine similarity is merged in via RRF. Otherwise BM25-only.",
|
|
133
|
+
body: SearchInput
|
|
134
|
+
}
|
|
135
|
+
}, async (req) => {
|
|
136
|
+
const body = SearchInput.parse(req.body);
|
|
137
|
+
const queryEmbedding = await ctx.embedding.embedQuery(body.query);
|
|
138
|
+
const hits = await search(ctx.db, body, queryEmbedding ? { queryEmbedding } : {});
|
|
139
|
+
return { hits, embeddingUsed: queryEmbedding != null };
|
|
140
|
+
});
|
|
141
|
+
app.post("/v1/tasks/range", {
|
|
142
|
+
schema: {
|
|
143
|
+
tags: ["tasks"],
|
|
144
|
+
summary: "Tasks in a date range + overdue + backlog (no due_at)",
|
|
145
|
+
description: "Returns three buckets: scheduled (due_at in [from, to)), overdue (due_at before now, only when not includeDone), backlog (no due_at, only when includeBacklog).",
|
|
146
|
+
body: TasksRangeInput
|
|
147
|
+
}
|
|
148
|
+
}, async (req) => tasksRange(ctx.db, TasksRangeInput.parse(req.body)));
|
|
149
|
+
// ----- entities (write) -----
|
|
150
|
+
app.post("/v1/tasks", {
|
|
151
|
+
schema: {
|
|
152
|
+
tags: ["entities"],
|
|
153
|
+
summary: "Create a task",
|
|
154
|
+
body: CreateTaskInput
|
|
155
|
+
}
|
|
156
|
+
}, async (req) => {
|
|
157
|
+
const body = CreateTaskInput.parse(req.body);
|
|
158
|
+
const entity = await write(ctx.db, { ...body, kind: "task", pinned: false });
|
|
159
|
+
if (ctx.embedding.isEnabled()) {
|
|
160
|
+
ctx.embedding.enqueue(entity.id, `${entity.title ?? ""}\n${entity.body}`);
|
|
161
|
+
}
|
|
162
|
+
return entity;
|
|
163
|
+
});
|
|
164
|
+
app.post("/v1/notes", {
|
|
165
|
+
schema: {
|
|
166
|
+
tags: ["entities"],
|
|
167
|
+
summary: "Create a note (title optional; body's first line acts as the label)",
|
|
168
|
+
body: CreateNoteInput
|
|
169
|
+
}
|
|
170
|
+
}, async (req) => {
|
|
171
|
+
const body = CreateNoteInput.parse(req.body);
|
|
172
|
+
const entity = await write(ctx.db, {
|
|
173
|
+
...body,
|
|
174
|
+
kind: "note",
|
|
175
|
+
status: "open",
|
|
176
|
+
priority: "none",
|
|
177
|
+
dueAt: null
|
|
178
|
+
});
|
|
179
|
+
if (ctx.embedding.isEnabled()) {
|
|
180
|
+
ctx.embedding.enqueue(entity.id, `${entity.title ?? ""}\n${entity.body}`);
|
|
181
|
+
}
|
|
182
|
+
return entity;
|
|
183
|
+
});
|
|
184
|
+
app.patch("/v1/entities/:id", {
|
|
185
|
+
schema: {
|
|
186
|
+
tags: ["entities"],
|
|
187
|
+
summary: "Update an entity (task or note)",
|
|
188
|
+
params: IdParams,
|
|
189
|
+
body: UpdateInput
|
|
190
|
+
}
|
|
191
|
+
}, async (req) => {
|
|
192
|
+
const { id } = IdParams.parse(req.params);
|
|
193
|
+
const fields = UpdateInput.parse(req.body);
|
|
194
|
+
const updated = await update(ctx.db, id, fields);
|
|
195
|
+
if (ctx.embedding.isEnabled() &&
|
|
196
|
+
(fields.body !== undefined || fields.title !== undefined)) {
|
|
197
|
+
ctx.embedding.enqueue(updated.id, `${updated.title ?? ""}\n${updated.body}`);
|
|
198
|
+
}
|
|
199
|
+
return updated;
|
|
200
|
+
});
|
|
201
|
+
app.delete("/v1/entities/:id", {
|
|
202
|
+
schema: {
|
|
203
|
+
tags: ["entities"],
|
|
204
|
+
summary: "Delete an entity",
|
|
205
|
+
params: IdParams
|
|
206
|
+
}
|
|
207
|
+
}, async (req, reply) => {
|
|
208
|
+
const { id } = IdParams.parse(req.params);
|
|
209
|
+
const ok = await remove(ctx.db, id);
|
|
210
|
+
if (!ok)
|
|
211
|
+
return reply.code(404).send({ error: "not_found" });
|
|
212
|
+
return reply.code(204).send();
|
|
213
|
+
});
|
|
214
|
+
app.post("/v1/entities/:id/links", {
|
|
215
|
+
schema: {
|
|
216
|
+
tags: ["entities"],
|
|
217
|
+
summary: "Link this entity to another (blocks / references / parent_of)",
|
|
218
|
+
params: IdParams,
|
|
219
|
+
body: LinkBody
|
|
220
|
+
}
|
|
221
|
+
}, async (req) => {
|
|
222
|
+
const { id } = IdParams.parse(req.params);
|
|
223
|
+
const body = LinkBody.parse(req.body);
|
|
224
|
+
return link(ctx.db, { fromId: id, toId: body.toId, kind: body.kind });
|
|
225
|
+
});
|
|
226
|
+
// ----- auth (browser session cookie) -----
|
|
227
|
+
const LoginBody = z.object({
|
|
228
|
+
password: z.string().min(1).max(500)
|
|
229
|
+
});
|
|
230
|
+
app.post("/v1/auth/login", {
|
|
231
|
+
// Tight cap — this is the brute-force surface.
|
|
232
|
+
config: { rateLimit: { max: 10, timeWindow: "1 minute" } },
|
|
233
|
+
schema: {
|
|
234
|
+
tags: ["auth"],
|
|
235
|
+
summary: "Master-password login. Sets httpOnly session cookie.",
|
|
236
|
+
body: LoginBody
|
|
237
|
+
}
|
|
238
|
+
}, async (req, reply) => {
|
|
239
|
+
const { password } = LoginBody.parse(req.body);
|
|
240
|
+
const ok = verifyMasterPassword(password);
|
|
241
|
+
if (!ok) {
|
|
242
|
+
// Add a small constant-time penalty so timing attacks against the
|
|
243
|
+
// env-lookup branch aren't easier than the rate limit makes them.
|
|
244
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
245
|
+
return reply.code(401).send({ error: "invalid_password" });
|
|
246
|
+
}
|
|
247
|
+
const ua = req.headers["user-agent"] ?? null;
|
|
248
|
+
const { plaintext, session } = await createSession(ctx.db, { userAgent: ua });
|
|
249
|
+
reply.setCookie("botnote_session", plaintext, {
|
|
250
|
+
httpOnly: true,
|
|
251
|
+
secure: process.env.NODE_ENV === "production",
|
|
252
|
+
sameSite: "lax",
|
|
253
|
+
path: "/",
|
|
254
|
+
maxAge: 60 * 60 * 24 * 30 // 30 days
|
|
255
|
+
});
|
|
256
|
+
return { ok: true, expiresAt: session.expiresAt };
|
|
257
|
+
});
|
|
258
|
+
app.post("/v1/auth/logout", {
|
|
259
|
+
schema: {
|
|
260
|
+
tags: ["auth"],
|
|
261
|
+
summary: "Clear session cookie and revoke the server-side row."
|
|
262
|
+
}
|
|
263
|
+
}, async (req, reply) => {
|
|
264
|
+
const plaintext = req.cookies?.botnote_session;
|
|
265
|
+
if (plaintext)
|
|
266
|
+
await revokeSession(ctx.db, plaintext);
|
|
267
|
+
reply.clearCookie("botnote_session", { path: "/" });
|
|
268
|
+
return { ok: true };
|
|
269
|
+
});
|
|
270
|
+
app.get("/v1/auth/whoami", {
|
|
271
|
+
schema: {
|
|
272
|
+
tags: ["auth"],
|
|
273
|
+
summary: "Confirm authentication state + how the caller got in."
|
|
274
|
+
}
|
|
275
|
+
}, async (req) => {
|
|
276
|
+
// If the request reached this handler, the auth gate already passed —
|
|
277
|
+
// either via cookie, bearer, or trusted tailnet origin.
|
|
278
|
+
const hasCookie = !!req.cookies?.botnote_session;
|
|
279
|
+
const hasBearer = !!req.headers.authorization;
|
|
280
|
+
const viaProxy = !!req.headers["cf-connecting-ip"] || !!req.headers["x-forwarded-for"];
|
|
281
|
+
return {
|
|
282
|
+
authenticated: true,
|
|
283
|
+
via: hasCookie
|
|
284
|
+
? "cookie"
|
|
285
|
+
: hasBearer
|
|
286
|
+
? "bearer"
|
|
287
|
+
: viaProxy
|
|
288
|
+
? "unknown"
|
|
289
|
+
: "trusted_origin"
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
// ----- tokens -----
|
|
293
|
+
app.get("/v1/tokens", { schema: { tags: ["tokens"], summary: "List API tokens (no hash, no plaintext)" } }, async () => (await listTokens(ctx.db)).map(publicToken));
|
|
294
|
+
// Token write endpoints get a much tighter cap (30/hour per IP). These are
|
|
295
|
+
// low-frequency operations — bumping them limits the blast radius if a token
|
|
296
|
+
// leaks or someone hammers the create endpoint.
|
|
297
|
+
app.post("/v1/tokens", {
|
|
298
|
+
config: { rateLimit: { max: 30, timeWindow: "1 hour" } },
|
|
299
|
+
schema: {
|
|
300
|
+
tags: ["tokens"],
|
|
301
|
+
summary: "Create a new API token (plaintext returned once)",
|
|
302
|
+
body: CreateTokenInput
|
|
303
|
+
}
|
|
304
|
+
}, async (req) => {
|
|
305
|
+
const body = CreateTokenInput.parse(req.body);
|
|
306
|
+
const { token, plaintext } = await createToken(ctx.db, body);
|
|
307
|
+
return { ...publicToken(token), plaintext };
|
|
308
|
+
});
|
|
309
|
+
app.delete("/v1/tokens/:id", {
|
|
310
|
+
config: { rateLimit: { max: 30, timeWindow: "1 hour" } },
|
|
311
|
+
schema: { tags: ["tokens"], summary: "Revoke an API token", params: IdParams }
|
|
312
|
+
}, async (req, reply) => {
|
|
313
|
+
const { id } = IdParams.parse(req.params);
|
|
314
|
+
const ok = await revokeToken(ctx.db, id);
|
|
315
|
+
if (!ok)
|
|
316
|
+
return reply.code(404).send({ error: "not_found" });
|
|
317
|
+
return reply.code(204).send();
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/rest/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type FastifyInstance } from "fastify";
|
|
2
|
+
import type { Database } from "../db/client.js";
|
|
3
|
+
import type { EmbeddingService } from "../service/embedding.js";
|
|
4
|
+
export interface ServerContext {
|
|
5
|
+
db: Database["db"];
|
|
6
|
+
embedding: EmbeddingService;
|
|
7
|
+
}
|
|
8
|
+
export interface BuildServerOpts extends ServerContext {
|
|
9
|
+
logLevel?: string;
|
|
10
|
+
webDist?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function buildServer(opts: BuildServerOpts): Promise<FastifyInstance>;
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
import cookie from "@fastify/cookie";
|
|
2
|
+
import cors from "@fastify/cors";
|
|
3
|
+
import rateLimit from "@fastify/rate-limit";
|
|
4
|
+
import fastifyStatic from "@fastify/static";
|
|
5
|
+
import swagger from "@fastify/swagger";
|
|
6
|
+
import swaggerUi from "@fastify/swagger-ui";
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import Fastify from "fastify";
|
|
11
|
+
import { hasZodFastifySchemaValidationErrors, isResponseSerializationError, jsonSchemaTransform, serializerCompiler, validatorCompiler } from "fastify-type-provider-zod";
|
|
12
|
+
import { consumeSession } from "../service/sessions.js";
|
|
13
|
+
import { consumeToken } from "../service/tokens.js";
|
|
14
|
+
import { VERSION } from "../version.js";
|
|
15
|
+
import { registerRoutes } from "./routes.js";
|
|
16
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
17
|
+
function resolveWebDist(explicit) {
|
|
18
|
+
const envPath = process.env.BOTNOTE_WEB_DIST;
|
|
19
|
+
const candidates = [
|
|
20
|
+
explicit,
|
|
21
|
+
envPath,
|
|
22
|
+
path.resolve(__dirname, "../../web/dist"),
|
|
23
|
+
path.resolve(__dirname, "../../../web/dist")
|
|
24
|
+
].filter(Boolean);
|
|
25
|
+
for (const c of candidates) {
|
|
26
|
+
if (existsSync(path.join(c, "index.html")))
|
|
27
|
+
return c;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
/** Default CORS allowlist. Add public origins via BOTNOTE_CORS_ORIGINS
|
|
32
|
+
* (comma-separated). The tailnet + localhost entries stay for ops access. */
|
|
33
|
+
function defaultCorsOrigins() {
|
|
34
|
+
const built = [
|
|
35
|
+
"http://127.0.0.1:4280",
|
|
36
|
+
"http://localhost:4280",
|
|
37
|
+
"http://100.68.185.53:4280",
|
|
38
|
+
"https://botnote.net"
|
|
39
|
+
];
|
|
40
|
+
const extra = process.env.BOTNOTE_CORS_ORIGINS;
|
|
41
|
+
if (extra) {
|
|
42
|
+
for (const o of extra.split(",").map((s) => s.trim()).filter(Boolean)) {
|
|
43
|
+
if (!built.includes(o))
|
|
44
|
+
built.push(o);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return built;
|
|
48
|
+
}
|
|
49
|
+
/** Read the caller's real IP. cf-connecting-ip is set by Cloudflare for every
|
|
50
|
+
* request that goes through CF Tunnel — without it, rate-limit would key on
|
|
51
|
+
* the tunnel's loopback address and the whole world would share one bucket. */
|
|
52
|
+
function clientIp(req) {
|
|
53
|
+
const cf = req.headers["cf-connecting-ip"];
|
|
54
|
+
if (typeof cf === "string" && cf)
|
|
55
|
+
return cf;
|
|
56
|
+
const xff = req.headers["x-forwarded-for"];
|
|
57
|
+
if (typeof xff === "string" && xff)
|
|
58
|
+
return xff.split(",")[0].trim();
|
|
59
|
+
return req.ip;
|
|
60
|
+
}
|
|
61
|
+
export async function buildServer(opts) {
|
|
62
|
+
const isProduction = process.env.NODE_ENV === "production";
|
|
63
|
+
const app = Fastify({
|
|
64
|
+
logger: { level: opts.logLevel ?? "info" },
|
|
65
|
+
// We're behind CF Tunnel (or tailscale serve). Trust the immediate proxy so
|
|
66
|
+
// req.ip is sensible, but rely on clientIp() above for rate-limit keying.
|
|
67
|
+
trustProxy: true
|
|
68
|
+
}).withTypeProvider();
|
|
69
|
+
app.setValidatorCompiler(validatorCompiler);
|
|
70
|
+
app.setSerializerCompiler(serializerCompiler);
|
|
71
|
+
await app.register(cookie, {
|
|
72
|
+
// No global secret/sign — session cookies carry an opaque random token
|
|
73
|
+
// that maps to a row in `sessions`; tamper-detection isn't useful since
|
|
74
|
+
// we already validate every value against the DB.
|
|
75
|
+
hook: "onRequest"
|
|
76
|
+
});
|
|
77
|
+
await app.register(cors, {
|
|
78
|
+
origin: defaultCorsOrigins(),
|
|
79
|
+
// credentials: true so cookies can be sent across origins (browser will
|
|
80
|
+
// refuse to attach cookies on a fetch to a different origin otherwise).
|
|
81
|
+
credentials: true,
|
|
82
|
+
methods: ["GET", "POST", "PATCH", "DELETE", "OPTIONS"],
|
|
83
|
+
allowedHeaders: ["content-type", "authorization"],
|
|
84
|
+
maxAge: 86400
|
|
85
|
+
});
|
|
86
|
+
await app.register(rateLimit, {
|
|
87
|
+
// 1000 req/min per IP — generous enough that a Claude Code session burst
|
|
88
|
+
// and the Web UI's 30s polling combined don't trip it; tight enough that a
|
|
89
|
+
// leaked token can't quietly exfil thousands of entities per second.
|
|
90
|
+
max: 1000,
|
|
91
|
+
timeWindow: "1 minute",
|
|
92
|
+
keyGenerator: (req) => clientIp(req),
|
|
93
|
+
// /health is a probe; /assets/* are dozens of static files per page load.
|
|
94
|
+
// Counting them would cap real API headroom for no benefit.
|
|
95
|
+
allowList: (req) => {
|
|
96
|
+
const u = req.url;
|
|
97
|
+
return u === "/health" || u.startsWith("/health?") || u.startsWith("/assets/");
|
|
98
|
+
},
|
|
99
|
+
errorResponseBuilder: (_req, ctx) => ({
|
|
100
|
+
error: "rate_limited",
|
|
101
|
+
message: `Too many requests. Try again in ${Math.ceil(ctx.ttl / 1000)}s.`,
|
|
102
|
+
retryAfter: Math.ceil(ctx.ttl / 1000)
|
|
103
|
+
})
|
|
104
|
+
});
|
|
105
|
+
if (!isProduction) {
|
|
106
|
+
await app.register(swagger, {
|
|
107
|
+
openapi: {
|
|
108
|
+
info: {
|
|
109
|
+
title: "botnote",
|
|
110
|
+
version: VERSION,
|
|
111
|
+
description: "Notion for bots. A lightweight, agent-first project + notes + memory store."
|
|
112
|
+
},
|
|
113
|
+
servers: [
|
|
114
|
+
{
|
|
115
|
+
url: `http://localhost:${process.env.BOTNOTE_PORT ?? 4280}`,
|
|
116
|
+
description: "local"
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
tags: [
|
|
120
|
+
{ name: "projects", description: "Project lifecycle and AGENTS.md." },
|
|
121
|
+
{ name: "entities", description: "Tasks and notes." },
|
|
122
|
+
{ name: "tasks", description: "Task calendar queries (range + backlog)." },
|
|
123
|
+
{ name: "search", description: "Hybrid retrieval." },
|
|
124
|
+
{ name: "brief", description: "Opening brief — agent context bundle." },
|
|
125
|
+
{ name: "tokens", description: "API token management." }
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
transform: jsonSchemaTransform
|
|
129
|
+
});
|
|
130
|
+
await app.register(swaggerUi, {
|
|
131
|
+
routePrefix: "/docs",
|
|
132
|
+
uiConfig: {
|
|
133
|
+
docExpansion: "list",
|
|
134
|
+
deepLinking: false
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
app.log.info("swagger UI mounted at /docs (dev mode)");
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
app.log.info("swagger UI disabled (production)");
|
|
141
|
+
}
|
|
142
|
+
// ----- error handler -----
|
|
143
|
+
// Strip Node stack traces from all responses. Zod validation errors return
|
|
144
|
+
// field-level info but no schema dump; Fastify's own errors get a safe
|
|
145
|
+
// summary.
|
|
146
|
+
app.setErrorHandler((error, req, reply) => {
|
|
147
|
+
const err = error;
|
|
148
|
+
if (hasZodFastifySchemaValidationErrors(err)) {
|
|
149
|
+
return reply.code(400).send({
|
|
150
|
+
error: "validation_error",
|
|
151
|
+
message: "Request body or params failed validation.",
|
|
152
|
+
details: err.validation.map((v) => ({
|
|
153
|
+
path: v.instancePath || v.schemaPath || "",
|
|
154
|
+
message: v.message ?? ""
|
|
155
|
+
}))
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
if (isResponseSerializationError(err)) {
|
|
159
|
+
req.log.error({ err }, "response serialization failed");
|
|
160
|
+
return reply.code(500).send({ error: "server_error" });
|
|
161
|
+
}
|
|
162
|
+
// fastify-rate-limit's errorResponseBuilder result lands here without a
|
|
163
|
+
// statusCode prop, so it would otherwise fall through to the 500 bucket
|
|
164
|
+
// and lose the retryAfter signal. Surface it explicitly.
|
|
165
|
+
const errAny = err;
|
|
166
|
+
if (errAny.error === "rate_limited") {
|
|
167
|
+
return reply.code(429).send({
|
|
168
|
+
error: "rate_limited",
|
|
169
|
+
message: err.message,
|
|
170
|
+
retryAfter: errAny.retryAfter
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const sc = err.statusCode;
|
|
174
|
+
if (typeof sc === "number" && sc >= 400 && sc < 500) {
|
|
175
|
+
return reply.code(sc).send({ error: err.code ?? "client_error", message: err.message });
|
|
176
|
+
}
|
|
177
|
+
req.log.error({ err }, "unhandled error");
|
|
178
|
+
return reply.code(500).send({ error: "server_error" });
|
|
179
|
+
});
|
|
180
|
+
// ----- auth -----
|
|
181
|
+
// Trust direct connections (tailnet IP, 127.0.0.1, the macmini-local Web UI)
|
|
182
|
+
// and only enforce bearer tokens on requests that came through Cloudflare
|
|
183
|
+
// Tunnel (or any reverse proxy that sets cf-connecting-ip / x-forwarded-for).
|
|
184
|
+
// This lets Boss browse the local tailnet URL without a login flow while
|
|
185
|
+
// keeping the public botnote.net surface fully token-gated.
|
|
186
|
+
const requireAuth = process.env.BOTNOTE_REQUIRE_AUTH === "1";
|
|
187
|
+
if (requireAuth) {
|
|
188
|
+
// IMPORTANT: register at onRequest, not preHandler. fastify-type-provider-zod
|
|
189
|
+
// runs schema validation at preValidation, which is BEFORE preHandler — so
|
|
190
|
+
// a preHandler auth gate lets an unauthenticated POST get a 400 zod error
|
|
191
|
+
// back, leaking the request schema. onRequest fires first; no body has been
|
|
192
|
+
// parsed yet, but headers and cookies are all we need.
|
|
193
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
194
|
+
if (!req.url.startsWith("/v1/"))
|
|
195
|
+
return;
|
|
196
|
+
// The login endpoint is the entry point — gating it would be a
|
|
197
|
+
// chicken-and-egg loop. Logout is fine to leave open too (it's a noop
|
|
198
|
+
// when no cookie is present).
|
|
199
|
+
if (req.url.startsWith("/v1/auth/login") ||
|
|
200
|
+
req.url.startsWith("/v1/auth/logout")) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
const viaProxy = !!req.headers["cf-connecting-ip"] || !!req.headers["x-forwarded-for"];
|
|
204
|
+
if (!viaProxy)
|
|
205
|
+
return; // direct tailnet/localhost — trusted
|
|
206
|
+
// Browser path: session cookie set by /v1/auth/login.
|
|
207
|
+
const cookieToken = req.cookies?.botnote_session;
|
|
208
|
+
if (cookieToken) {
|
|
209
|
+
const session = await consumeSession(opts.db, cookieToken);
|
|
210
|
+
if (session)
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// CLI / MCP path: bearer token.
|
|
214
|
+
const auth = req.headers.authorization;
|
|
215
|
+
if (auth && auth.startsWith("Bearer ")) {
|
|
216
|
+
const plaintext = auth.slice("Bearer ".length).trim();
|
|
217
|
+
const tok = await consumeToken(opts.db, plaintext);
|
|
218
|
+
if (tok)
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
return reply.code(401).send({ error: "unauthenticated" });
|
|
222
|
+
});
|
|
223
|
+
app.log.info("auth: required for /v1/* via reverse proxy (cookie or bearer; direct tailnet trusted)");
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
app.log.info("auth: disabled (set BOTNOTE_REQUIRE_AUTH=1 to enforce)");
|
|
227
|
+
}
|
|
228
|
+
await registerRoutes(app, { db: opts.db, embedding: opts.embedding });
|
|
229
|
+
const webDist = resolveWebDist(opts.webDist);
|
|
230
|
+
if (webDist) {
|
|
231
|
+
await app.register(fastifyStatic, {
|
|
232
|
+
root: webDist,
|
|
233
|
+
prefix: "/",
|
|
234
|
+
// wildcard: true lets fastify-static answer for every file under root
|
|
235
|
+
// (notably /assets/<hash>.js / .css). Without it, those paths fall
|
|
236
|
+
// through to the not-found handler and the SPA fallback sends back
|
|
237
|
+
// index.html — the browser then tries to parse HTML as a JS module and
|
|
238
|
+
// the page renders blank.
|
|
239
|
+
wildcard: true,
|
|
240
|
+
decorateReply: true,
|
|
241
|
+
cacheControl: true,
|
|
242
|
+
maxAge: "1d",
|
|
243
|
+
immutable: false
|
|
244
|
+
});
|
|
245
|
+
const indexHtmlPath = path.join(webDist, "index.html");
|
|
246
|
+
// Re-read on each request. The file is ~700 bytes so the cost is trivial,
|
|
247
|
+
// and this guarantees we never serve a stale HTML that points to an old
|
|
248
|
+
// asset hash after `pnpm build` overwrites the bundle — that's how the
|
|
249
|
+
// page goes blank when the daemon outlives the previous build.
|
|
250
|
+
const readIndex = () => readFileSync(indexHtmlPath, "utf8");
|
|
251
|
+
// Explicit `/` route wins over fastify-static's wildcard so the homepage
|
|
252
|
+
// always serves fresh HTML with no-cache, sidestepping the long max-age
|
|
253
|
+
// applied to /assets/*.
|
|
254
|
+
app.get("/", async (_req, reply) => {
|
|
255
|
+
reply.type("text/html; charset=utf-8");
|
|
256
|
+
reply.header("cache-control", "no-cache");
|
|
257
|
+
return readIndex();
|
|
258
|
+
});
|
|
259
|
+
app.setNotFoundHandler(async (req, reply) => {
|
|
260
|
+
const url = req.url;
|
|
261
|
+
if (url.startsWith("/v1") ||
|
|
262
|
+
url.startsWith("/docs") ||
|
|
263
|
+
url === "/health" ||
|
|
264
|
+
url.startsWith("/health?") ||
|
|
265
|
+
url.startsWith("/assets/")) {
|
|
266
|
+
return reply.code(404).send({ error: "not_found", path: url });
|
|
267
|
+
}
|
|
268
|
+
// SPA fallback for client-side routes (/today, /p/:key, etc.). Send the
|
|
269
|
+
// file fresh from disk with no-cache so the browser never pins it to
|
|
270
|
+
// an old asset hash after a redeploy.
|
|
271
|
+
reply.type("text/html; charset=utf-8");
|
|
272
|
+
reply.header("cache-control", "no-cache");
|
|
273
|
+
return readIndex();
|
|
274
|
+
});
|
|
275
|
+
app.log.info(`web UI served from ${webDist}`);
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
app.log.warn("web/dist not found; UI not served (BOTNOTE_WEB_DIST or build required)");
|
|
279
|
+
}
|
|
280
|
+
return app;
|
|
281
|
+
}
|
|
282
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/rest/server.ts"],"names":[],"mappings":"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"}
|