From c8f6d0f8925b3209d85a6bf7e7d37d5d80ec8a45 Mon Sep 17 00:00:00 2001 From: artheru Date: Wed, 10 Jun 2026 06:08:19 +0800 Subject: [PATCH] public documentation (0.39.0) - amerc-api: GET /docs + /docs/:id are public (reads); writes still require auth - DocsApp/ConsoleShell: publicView mode renders docs read-only when logged out, with a 'Log in to edit' header CTA; edit/new/delete hidden for anonymous - Docs nav link now shown to everyone - seeded 5 quality guides: Quickstart, Publish an agent, Embed a chatbox, Showcase, API reference --- index.html | 2 +- package.json | 2 +- server/amerc-api.mjs | 4 ++-- src/ConsoleShell.jsx | 11 ++++++----- src/DocsApp.jsx | 10 +++++----- src/Scene2D.jsx | 4 ++-- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index a585a43..cc9f986 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - + diff --git a/package.json b/package.json index 643d487..6007410 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "amerc-site", - "version": "0.38.0-npc", + "version": "0.39.0-docs", "private": true, "type": "module", "scripts": { diff --git a/server/amerc-api.mjs b/server/amerc-api.mjs index 2276925..84eebdd 100644 --- a/server/amerc-api.mjs +++ b/server/amerc-api.mjs @@ -285,11 +285,11 @@ const server = http.createServer(async (req, res) => { const needAuth = () => send(res, 401, { ok: false, error: 'login or api key required' }); // DOCUMENTS - if (path === '/docs' && method === 'GET') { if (!id) return needAuth(); return send(res, 200, { ok: true, docs: db.prepare('SELECT id,title,folder,updated_by,created_at,updated_at FROM documents ORDER BY folder,title').all() }); } + if (path === '/docs' && method === 'GET') { return send(res, 200, { ok: true, docs: db.prepare('SELECT id,title,folder,updated_by,created_at,updated_at FROM documents ORDER BY folder,title').all() }); } if (path === '/docs' && method === 'POST') { if (!id) return needAuth(); const b = await readBody(req) || {}; const now = Date.now(); const info = db.prepare('INSERT INTO documents(title,folder,body,updated_by,created_at,updated_at) VALUES(?,?,?,?,?,?)').run(b.title || 'Untitled', b.folder || '', b.body || '', who(id), now, now); return send(res, 200, { ok: true, id: Number(info.lastInsertRowid) }); } - if ((m = path.match(/^\/docs\/(\d+)$/)) && method === 'GET') { if (!id) return needAuth(); const d = db.prepare('SELECT * FROM documents WHERE id=?').get(Number(m[1])); return d ? send(res, 200, { ok: true, doc: d }) : send(res, 404, { ok: false, error: 'not found' }); } + if ((m = path.match(/^\/docs\/(\d+)$/)) && method === 'GET') { const d = db.prepare('SELECT * FROM documents WHERE id=?').get(Number(m[1])); return d ? send(res, 200, { ok: true, doc: d }) : send(res, 404, { ok: false, error: 'not found' }); } if ((m = path.match(/^\/docs\/(\d+)$/)) && method === 'PATCH') { if (!id) return needAuth(); const d = db.prepare('SELECT * FROM documents WHERE id=?').get(Number(m[1])); if (!d) return send(res, 404, { ok: false, error: 'not found' }); const b = await readBody(req) || {}; db.prepare('UPDATE documents SET title=?,folder=?,body=?,updated_by=?,updated_at=? WHERE id=?').run(b.title ?? d.title, b.folder ?? d.folder, b.body ?? d.body, who(id), Date.now(), d.id); return send(res, 200, { ok: true }); } if ((m = path.match(/^\/docs\/(\d+)$/)) && method === 'DELETE') { if (id?.kind !== 'user') return send(res, 403, { ok: false, error: 'human login required to delete' }); db.prepare('DELETE FROM documents WHERE id=?').run(Number(m[1])); return send(res, 200, { ok: true }); } diff --git a/src/ConsoleShell.jsx b/src/ConsoleShell.jsx index 2ae93be..48dffba 100644 --- a/src/ConsoleShell.jsx +++ b/src/ConsoleShell.jsx @@ -42,11 +42,11 @@ function ConsoleLogin({ auth }) { ); } -export default function ConsoleShell({ title, accent = '#27d8ff', current, requireRole, children }) { +export default function ConsoleShell({ title, accent = '#27d8ff', current, requireRole, publicView, children }) { const auth = useAuth(); if (!auth.ready) return
Loading…
; - if (!auth.user) return
; - if (requireRole && auth.user.role !== requireRole) return ( + if (!auth.user && !publicView) return
; + if (requireRole && auth.user && auth.user.role !== requireRole) return (
amerc
@@ -65,8 +65,9 @@ export default function ConsoleShell({ title, accent = '#27d8ff', current, requi {APPS.map((a) => {a.label})}
- {auth.user.handle}{auth.user.role === 'admin' ? ' · admin' : ''} - + {auth.user + ? <>{auth.user.handle}{auth.user.role === 'admin' ? ' · admin' : ''} + : Log in to edit}
{typeof children === 'function' ? children(auth) : children}
diff --git a/src/DocsApp.jsx b/src/DocsApp.jsx index 9e5d4f8..b438fa6 100644 --- a/src/DocsApp.jsx +++ b/src/DocsApp.jsx @@ -5,7 +5,7 @@ import { api } from './api.js'; marked.setOptions({ breaks: true }); -function DocsWorkspace() { +function DocsWorkspace({ canEdit }) { const [docs, setDocs] = useState([]); const [sel, setSel] = useState(null); const [doc, setDoc] = useState(null); @@ -39,7 +39,7 @@ function DocsWorkspace() {