Files
espresso_frame/server/app/templates/setup.html
T
tfaour 1e8d6803ac Redesign phase B: users, sessions, first-run setup, admin panel
Real identity on top of phase A's schema: scrypt-hashed passwords
(stdlib, no new deps -- parameters baked into each stored hash),
server-side sessions (sha256 of the cookie value stored, 30-day rolling
expiry), and per-session CSRF tokens enforced on every mutating
session-authed request -- via X-CSRF-Token for the JSON API (a fetch()
wrapper in base.html injects it, so the existing page scripts didn't
need touching) and a hidden form field for the HTML forms.

/setup runs once while no users exist: creates admin #1, links every
existing frame to them (owner + controller), and inherits the migrated
Immich creds onto their account -- per-user creds are now the primary
source, with env vars still winning as the operator fallback. /login,
/logout, /settings (display name, Immich creds, password change), and
/admin (enroll users, reset passwords, link users to frames, close a
frame's legacy-token window, delete) round out the pages, all in the
existing template/card style.

The legacy shared token stays accepted on browser routes so the
deployed frame's on-panel manage QR keeps working until phase C swaps
it for the limited manage page; token access renders without nav or
CSRF shim and is exempt from CSRF (explicit credential, not an ambient
cookie). Device routes untouched -- the legacy curl suite passes
verbatim.

Identity is provider-pluggable (identity_provider/provider_subject
already modeled) so OIDC can land later without schema surgery.
2026-07-21 23:28:14 -04:00

31 lines
1.1 KiB
HTML

{% extends "base.html" %}
{% block page_class %}page-narrow{% endblock %}
{% block subtitle %}
<p class="sub">First-run setup</p>
{% endblock %}
{% block content %}
<section class="card">
<h2 class="card-title">Create the admin account</h2>
<p class="sub">This server has no users yet. The account you create here
is the administrator: it can enroll other users and manage every
frame. Any frame this server already knows about is linked to it
automatically.</p>
{% if error %}<div class="status err">{{ error }}</div>{% endif %}
<form method="post" action="/setup">
<label>Username
<input type="text" name="username" maxlength="64" required autofocus autocomplete="username">
</label>
<label>Display name (optional)
<input type="text" name="display_name" maxlength="64" autocomplete="name">
</label>
<label>Password
<input type="password" name="password" minlength="8" required autocomplete="new-password">
</label>
<button type="submit">Create admin account</button>
</form>
</section>
{% endblock %}