diff --git a/CLAUDE.md b/CLAUDE.md index b8767ea..089eac8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -98,6 +98,10 @@ tests/ unit/ # Vitest unit tests e2e/ # Playwright e2e tests (Gherkin-style) helpers/fixtures.ts # Shared authenticatedPage fixture +website/ # Static landing page (crumbs.bretzel.app) + index.html # Landing page with feature cards, screenshots, deploy section + styles.css # Landing page styles (retro parchment theme) + assets/ # Favicon, screenshots ``` ## Commands diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 5a44334..bcad068 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -22,36 +22,31 @@ export const handle: Handle = async ({ event, resolve }) => { return resolve(event); } - // MCP endpoint: authenticate via Bearer token (API key) - if (pathname.startsWith('/api/mcp')) { + // API routes: authenticate via Bearer token if present + if (pathname.startsWith('/api/')) { const authHeader = event.request.headers.get('authorization'); const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null; - if (!token) { - return new Response(JSON.stringify({ error: 'Unauthorized' }), { - status: 401, - headers: { 'Content-Type': 'application/json' } - }); - } + if (token) { + const { valid, userId } = validateApiKey(token); + if (!valid || !userId) { + return new Response(JSON.stringify({ error: 'Unauthorized' }), { + status: 401, + headers: { 'Content-Type': 'application/json' } + }); + } - const { valid, userId } = validateApiKey(token); - if (!valid || !userId) { - return new Response(JSON.stringify({ error: 'Unauthorized' }), { - status: 401, - headers: { 'Content-Type': 'application/json' } - }); - } + const user = getUserForApiKey(userId); + if (!user) { + return new Response(JSON.stringify({ error: 'Unauthorized' }), { + status: 401, + headers: { 'Content-Type': 'application/json' } + }); + } - const user = getUserForApiKey(userId); - if (!user) { - return new Response(JSON.stringify({ error: 'Unauthorized' }), { - status: 401, - headers: { 'Content-Type': 'application/json' } - }); + event.locals.user = user; + return resolve(event); } - - event.locals.user = user; - return resolve(event); } // Check if setup is complete diff --git a/src/routes/(app)/settings/+layout.svelte b/src/routes/(app)/settings/+layout.svelte index 8f6e89f..51174eb 100644 --- a/src/routes/(app)/settings/+layout.svelte +++ b/src/routes/(app)/settings/+layout.svelte @@ -32,7 +32,7 @@ class="flex items-center gap-2 rounded-sm px-3 py-2 text-sm whitespace-nowrap transition-colors {pathname === '/settings/mcp' ? 'bg-[var(--primary)]/15 text-[var(--primary)] font-medium' : 'text-[var(--text)] hover:bg-[var(--bg-base)]'}" > - MCP Server + API {#if data.user?.role === 'admin'} diff --git a/src/routes/(app)/settings/mcp/+page.svelte b/src/routes/(app)/settings/mcp/+page.svelte index f38d018..a3db93d 100644 --- a/src/routes/(app)/settings/mcp/+page.svelte +++ b/src/routes/(app)/settings/mcp/+page.svelte @@ -122,7 +122,7 @@ ${mcpJsonConfig}`);

API Keys

- API keys allow external tools (like Claude Code) to access your notes via the MCP protocol. + API keys allow external tools to access your notes via the MCP protocol and the REST API.

@@ -229,6 +229,16 @@ ${mcpJsonConfig}`); {/if} + +
+

REST API

+

+ Use your API key to authenticate requests to any /api/* endpoint: +

+
curl -H "Authorization: Bearer {activeApiKey}" \
+  {baseUrl}/api/notes
+
+

Client Configuration

diff --git a/website/index.html b/website/index.html index 495e71b..f203c13 100644 --- a/website/index.html +++ b/website/index.html @@ -145,8 +145,8 @@
-

MCP Server

-

Built-in AI integration. Connect Claude, Cursor, or any MCP client to read and write your notes.

+

API & MCP

+

Full REST API with token auth. Integrate with n8n, Zapier, or scripts. Built-in MCP server for AI tools like Claude and Cursor.