feat: public platform, admin console, templates, landing docs

Projects become publicly browsable after admin approval; management and
secret-adjacent reads (logs, variables, exec, connection URLs) require
owner/member/admin auth, and platform controls sit behind RequireAdmin.

- Backend: OptionalAuth middleware, read/authed/admin route groups,
  projects.is_approved + service_templates.owner_id migration,
  visibility-aware sqlc queries, admin overview/users/approval endpoints,
  EnsureAdminAccount bootstrap (ADMIN_* env), 30 seeded templates with
  owner CRUD, managed-database connection URLs gated owner+admin, fix
  DB.Stats() infinite recursion.
- Frontend: expandable persisted sidebar + clickable user menu, public
  landing page with searchable docs browser (GitHub sync, bundled
  snapshot + localStorage fallback), DocsBrowser shared with /docs,
  template create/edit/JSON-upload for owned templates, database create
  dialog, admin console (stats, approvals, project edit/delete, user
  admin toggles), HA/Security/Usage polish with dead CSS tokens mapped
  to real theme vars, anonymous-safe gating on every mutation control.
- Tests: safe window.localStorage access fixes happy-dom crashes in
  PlatformShell and canvas storage.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-22 19:58:59 +02:00
1 parent 8f5789bf93
commit 8b6214a0f5
62 files changed
+4986 -1133

No files matched your search

+103
View File
@@ -1582,3 +1582,106 @@ textarea {
.v-tick .sig.r { background: var(--error); }
/* Rail active indicator handled via .nav-item.active::before */
/* Rendered documentation */
.docs-article {
color: var(--text-secondary);
font-size: 13.5px;
line-height: 1.7;
max-width: 72ch;
}
.docs-article h1 {
color: var(--text-primary);
font-size: 22px;
font-weight: 700;
letter-spacing: -0.02em;
margin: 0 0 14px;
}
.docs-article h2 {
color: var(--text-primary);
font-size: 16.5px;
font-weight: 650;
margin: 26px 0 10px;
padding-top: 14px;
border-top: 1px solid var(--border-subtle);
}
.docs-article h3 {
color: var(--text-primary);
font-size: 14px;
font-weight: 600;
margin: 20px 0 8px;
}
.docs-article h4 {
color: var(--text-primary);
font-size: 13px;
font-weight: 600;
margin: 16px 0 6px;
}
.docs-article p { margin: 10px 0; }
.docs-article a {
color: var(--accent-primary);
text-decoration: none;
border-bottom: 1px solid var(--accent-primary-glow);
}
.docs-article a:hover { border-bottom-color: var(--accent-primary); }
.docs-article ul, .docs-article ol {
margin: 10px 0;
padding-left: 22px;
}
.docs-article ul { list-style: disc; }
.docs-article ol { list-style: decimal; }
.docs-article li { margin: 4px 0; }
.docs-article li::marker { color: var(--text-tertiary); }
.docs-article code {
font-family: 'JetBrains Mono', monospace;
font-size: 12px;
background: var(--surface-muted);
border: 1px solid var(--border-subtle);
border-radius: 5px;
padding: 1.5px 5px;
color: var(--accent-secondary);
}
.docs-article pre {
background: var(--bg-void);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-md);
padding: 14px 16px;
margin: 14px 0;
overflow-x: auto;
}
.docs-article pre code {
background: none;
border: none;
padding: 0;
font-size: 12px;
color: var(--text-secondary);
}
.docs-article blockquote {
border-left: 3px solid var(--accent-primary);
padding: 2px 0 2px 14px;
margin: 14px 0;
color: var(--text-tertiary);
}
.docs-article table {
width: 100%;
border-collapse: collapse;
margin: 14px 0;
font-size: 12.5px;
}
.docs-article th {
text-align: left;
font-weight: 600;
color: var(--text-primary);
padding: 8px 12px;
border-bottom: 1px solid var(--border-default);
}
.docs-article td {
padding: 8px 12px;
border-bottom: 1px solid var(--border-subtle);
}
.docs-article hr {
border: none;
border-top: 1px solid var(--border-subtle);
margin: 24px 0;
}
.docs-article img { max-width: 100%; border-radius: var(--radius-md); }