admin: completeness + UI consistency pass
Arcadia wiring: - home: real Overview dashboard (tenants/users/audit/health probe) replacing the inherited Vibespace welcome tiles; skeleton loaders, refresh button, registers admin context - profile: split into Account (synced via getUser/updateUser of session user) and local Preferences; updateSessionUser keeps the appbar in sync after edits - session: drop unused signIn mock, add updateSessionUser, refresh tests - profile schema: drop redundant Profile.name/email (session is the source of truth) - routes: delete orphaned resources route + lib Auth flows that previously 404'd: - /signup, /login/forgot, /login/reset, /login/2fa wired via @crema/arcadia-auth-ui - shared AuthShell + AuthBrand wrapper Assistant tools (admin-tools.ts): - +10 tools: deactivate_tenant, set_user_status, delete_user, list_memberships, list_roles, revoke_api_key, create_user, update_user, assign_role, remove_role - list_memberships gains user_id filter for "tenants this user belongs to" queries - search_kb / read_chunk: new token resolution (window override → VITE_ARCADIA_SEARCH_TOKEN service token → operator session JWT → "dev"); on 401/403 emit a tailored hint based on which token was used UI consistency: - new PageHeader component - AppShell.title was unrendered — dropped; first-child padding on #main-content keeps the floating actions pill from colliding with header content - removed dead "Sign in required" fallback cards from 14 routes (AppShell already redirects) - stripped p-6 from outer wrappers across 14 routes (was double-padding under AppShell's own p-6) - migrated home + tenants to PageHeader arcadia-search ergonomics: - scripts/mint-search-token.mjs + `npm run mint:search-token` mints HS512 JWT with required tenant_id claim, upserts VITE_ARCADIA_SEARCH_TOKEN into .env.local - README/.env document the new VITE_ARCADIA_SEARCH_URL / VITE_ARCADIA_SEARCH_TOKEN knobs - .env.local now gitignored Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ const ARCADIA = resolve(ROOT, "../reference/arcadia-app")
|
||||
const OUT = resolve(ROOT, "public/docs-index.json")
|
||||
|
||||
const SOURCES = [
|
||||
// Arcadia platform docs (resolved against ARCADIA = ../reference/arcadia-app).
|
||||
{ path: "README.md", tags: ["core"] },
|
||||
{ path: "docs/ARCADIA.md", tags: ["core"] },
|
||||
{ path: "docs/MODULAR_MONOLITH.md", tags: ["core"] },
|
||||
@@ -29,6 +30,16 @@ const SOURCES = [
|
||||
{ path: "DEPLOY.md", tags: ["ops"] },
|
||||
{ path: "DEV_DEPLOY.md", tags: ["ops"] },
|
||||
{ path: "DEV_SETUP.md", tags: ["ops"] },
|
||||
|
||||
// RAG ecosystem docs — pulled from sibling repos via per-source
|
||||
// rootDir override. Lets the assistant answer "how do I add a
|
||||
// tenant to arcadia-search" or "what does the browser RAG do"
|
||||
// without leaving the chat.
|
||||
{ rootDir: "../../arcadia-search", path: "README.md", tags: ["rag", "search-service"] },
|
||||
{ rootDir: "../../arcadia-search", path: "MULTI_TENANT.md", tags: ["rag", "search-service"] },
|
||||
{ rootDir: "../../arcadia-search", path: "ARCADIA_INTEGRATION.md", tags: ["rag", "integration"] },
|
||||
{ rootDir: "../../lib-lexical-rag-ui", path: "README.md", tags: ["rag", "browser"] },
|
||||
{ rootDir: "../../arcadia-admin", path: "docs/RAG.md", tags: ["rag", "overview"] },
|
||||
]
|
||||
|
||||
buildIndex({
|
||||
|
||||
84
scripts/mint-search-token.mjs
Executable file
84
scripts/mint-search-token.mjs
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env node
|
||||
// Mint an HMAC-signed JWT for arcadia-search and (optionally) write it
|
||||
// into .env.local as VITE_ARCADIA_SEARCH_TOKEN.
|
||||
//
|
||||
// arcadia-search expects:
|
||||
// - HS512 by default when only JWT_HMAC_SECRET is set on the server
|
||||
// (no PEM). HS256/HS384 work too if JWT_ALGORITHM is overridden.
|
||||
// - a `tenant_id` claim (configurable server-side via JWT_TENANT_CLAIM).
|
||||
//
|
||||
// Usage:
|
||||
// node scripts/mint-search-token.mjs # uses defaults, writes .env.local
|
||||
// node scripts/mint-search-token.mjs --print # print only, don't write
|
||||
// node scripts/mint-search-token.mjs --tenant=foo --days=30
|
||||
// JWT_HMAC_SECRET=xyz node scripts/mint-search-token.mjs
|
||||
|
||||
import { createHmac } from "node:crypto"
|
||||
import { readFileSync, writeFileSync, existsSync } from "node:fs"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { dirname, resolve } from "node:path"
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url))
|
||||
const PROJECT_ROOT = resolve(HERE, "..")
|
||||
const ENV_LOCAL = resolve(PROJECT_ROOT, ".env.local")
|
||||
|
||||
function arg(name, fallback) {
|
||||
const prefix = `--${name}=`
|
||||
const hit = process.argv.find((a) => a.startsWith(prefix))
|
||||
return hit ? hit.slice(prefix.length) : fallback
|
||||
}
|
||||
|
||||
const flag = (name) => process.argv.includes(`--${name}`)
|
||||
|
||||
const SECRET = process.env.JWT_HMAC_SECRET ?? "test-secret-change-me"
|
||||
const TENANT = arg("tenant", process.env.VITE_ARCADIA_TENANT ?? "platform-admin")
|
||||
const SUBJECT = arg("sub", "arcadia-admin")
|
||||
const ALGORITHM = (arg("alg", "HS512")).toUpperCase()
|
||||
const DAYS = Number(arg("days", "365"))
|
||||
const PRINT_ONLY = flag("print")
|
||||
|
||||
const HMAC_ALG = { HS256: "sha256", HS384: "sha384", HS512: "sha512" }[ALGORITHM]
|
||||
if (!HMAC_ALG) {
|
||||
console.error(`Unsupported algorithm "${ALGORITHM}". Use HS256, HS384, or HS512.`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const b64 = (v) =>
|
||||
Buffer.from(typeof v === "string" ? v : JSON.stringify(v)).toString("base64url")
|
||||
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const header = b64({ alg: ALGORITHM, typ: "JWT" })
|
||||
const payload = b64({
|
||||
sub: SUBJECT,
|
||||
tenant_id: TENANT,
|
||||
iat: now,
|
||||
exp: now + DAYS * 86400,
|
||||
})
|
||||
const signature = createHmac(HMAC_ALG, SECRET)
|
||||
.update(`${header}.${payload}`)
|
||||
.digest("base64url")
|
||||
const token = `${header}.${payload}.${signature}`
|
||||
|
||||
if (PRINT_ONLY) {
|
||||
console.log(token)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
// Upsert VITE_ARCADIA_SEARCH_TOKEN in .env.local without disturbing other keys.
|
||||
const KEY = "VITE_ARCADIA_SEARCH_TOKEN"
|
||||
let existing = ""
|
||||
if (existsSync(ENV_LOCAL)) existing = readFileSync(ENV_LOCAL, "utf8")
|
||||
const line = `${KEY}=${token}`
|
||||
const next = new RegExp(`^${KEY}=.*$`, "m").test(existing)
|
||||
? existing.replace(new RegExp(`^${KEY}=.*$`, "m"), line)
|
||||
: (existing.endsWith("\n") || existing === "" ? existing : existing + "\n") + line + "\n"
|
||||
writeFileSync(ENV_LOCAL, next)
|
||||
|
||||
console.log(`Wrote ${KEY} to ${ENV_LOCAL}`)
|
||||
console.log(` alg: ${ALGORITHM}`)
|
||||
console.log(` tenant: ${TENANT}`)
|
||||
console.log(` sub: ${SUBJECT}`)
|
||||
console.log(` expires: ${new Date((now + DAYS * 86400) * 1000).toISOString()}`)
|
||||
console.log(` secret: ${SECRET === "test-secret-change-me" ? "(default — override with JWT_HMAC_SECRET=)" : "(from JWT_HMAC_SECRET)"}`)
|
||||
console.log(``)
|
||||
console.log(`Restart 'npm run dev' so Vite picks up the new env var.`)
|
||||
Reference in New Issue
Block a user