Files
beardrive/internal/webapp/static/index.html
T
Snow LeeandClaude Fable 5 baad9e6dc2 feat(hub): signup gating, admin lifecycle, and onboarding UX
Makes a self-hosted hub safe to expose on a public URL and operable
without hand-editing JSON — addressing the blocker/major findings from
the persona usability evaluations.

Signup gating (config auth block, all optional):
- allowed_domains: signup email must match (e.g. only @runbear.io)
- require_verification: email-link activation before sign-in (reuses mailer)
- require_approval: hub admins approve new accounts (admins list)
- brand shown on the sign-in page; allow_signup:false already hid Sign up
Accounts carry a Status (active/unverified/pending); non-active accounts
cannot authenticate.

Admin lifecycle (endpoints + web UI):
- org: rename, member role change, member remove (last-owner guarded),
  invite list + revoke
- project: create (web), rename, delete (from the org panel)
- hub admins: approve/deny pending signups (sidebar bell + panel)
- org-wide public-share audit with revoke

UX: onboarding empty-state (explains invites, paste-invite + create-project)
instead of a blank sidebar; visible "Search ⌘K" button; toasts replace
blocking alert(); responsive layout with an off-canvas sidebar; joining
via #join now survives a logged-out click (token carried through login).
Web uploads are attributed to the signed-in account, not the server.
Login/signup are rate-limited per IP.

Tests: domain/verification/approval gates, auth rate limit, org+project
lifecycle, owner-only guards, invite→join→role→remove over HTTP.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7Q9ZKSZRTdvrSJkYLUmYs
2026-07-08 22:23:27 -07:00

54 lines
2.1 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>BearDrive</title>
<link rel="stylesheet" href="style.css">
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>&#128193;</text></svg>">
</head>
<body>
<div id="sb-backdrop"></div>
<aside id="sidebar">
<header id="vault">
<span id="vault-name"></span>
<div class="vault-actions">
<button id="adminbar" class="adminbar" hidden></button>
<a id="signout" href="/auth/logout" hidden title="Sign out"></a>
</div>
</header>
<nav id="projects" aria-label="Projects" hidden></nav>
<nav id="tree" aria-label="Files"></nav>
<footer id="orgbar" hidden>
<span id="org-name" title="Manage organization"></span>
<button id="invite-btn" hidden title="Manage this organization">Manage</button>
</footer>
</aside>
<main id="main">
<header id="topbar">
<button id="menu-btn" class="icon-btn" title="Menu" aria-label="Menu"></button>
<span id="crumb"></span>
<span id="meta"></span>
<button id="search-btn" class="btn ghost" title="Search (⌘K)">🔍 Search <kbd>⌘K</kbd></button>
<button id="share-btn" class="btn" hidden>Share</button>
<button id="history-btn" class="btn" hidden>History</button>
<button id="upload-btn" class="btn" hidden>Upload</button>
<input id="upload-input" type="file" hidden>
<a id="download" class="btn" hidden download>Download</a>
</header>
<article id="content" class="markdown">
<div class="empty">Select a file from the sidebar</div>
</article>
</main>
<div id="palette-overlay" hidden>
<div id="palette" role="dialog" aria-label="Search and quick actions">
<input id="palette-input" type="text" placeholder="Search files, projects, actions…"
autocomplete="off" spellcheck="false">
<ul id="palette-results"></ul>
<footer id="palette-hint">↑↓ navigate · ⏎ select · esc close</footer>
</div>
</div>
<script src="app.js"></script>
</body>
</html>