SPB Git

Repository Public

page.tsx 3.7 KB 49 lines tsx

1import { Notice, PageHead, Panel } from "@/components/studio/ui";2import { requireRole } from "@/lib/auth";3import { changePassword, confirmTotp, disableTotp, startTotp, totpQr } from "../../actions";45export const metadata = { title: "Account" };6export default async function Account({ searchParams }: { searchParams: Promise<{ ok?: string; err?: string; first?: string; enroll?: string }> }) {7  const sp = await searchParams;8  const user = await requireRole("viewer", { allowPasswordChange: true });9  const qr = sp.enroll && !user.must_change_password ? await totpQr() : null;10  return (11    <>12      <PageHead title="Account" sub={user.email} />13      <Notice ok={sp.ok} err={sp.err ?? (user.must_change_password ? "Choose your own password before using the studio. The one-time password stops working once you do." : undefined)} />14      <div className="grid gap-5 lg:grid-cols-2">15        <Panel title="Password">16          <form action={changePassword} className="space-y-4">17            <div><label htmlFor="current" className="label">Current password</label><input id="current" name="current" type="password" autoComplete="current-password" required className="field mt-1" /></div>18            <div><label htmlFor="next" className="label">New password (12 characters or more)</label><input id="next" name="next" type="password" autoComplete="new-password" minLength={12} required className="field mt-1" /></div>19            <div><label htmlFor="confirm" className="label">Repeat the new password</label><input id="confirm" name="confirm" type="password" autoComplete="new-password" minLength={12} required className="field mt-1" /></div>20            <button className="btn btn-primary">Change password</button>21          </form>22        </Panel>23        <Panel title="Two-factor authentication (TOTP)">24          {user.must_change_password ? <p className="text-sm text-[var(--color-muted)]">Change your password first.</p> : user.totp_enabled ? (25            <form action={disableTotp} className="space-y-4">26              <p className="text-sm text-[var(--color-ok)]">On. A code from your authenticator app is required at every sign-in.</p>27              <div><label htmlFor="pw" className="label">Password (to turn it off)</label><input id="pw" name="password" type="password" autoComplete="current-password" required className="field mt-1" /></div>28              <button className="btn btn-danger">Turn off</button>29            </form>30          ) : qr ? (31            <form action={confirmTotp} className="space-y-4">32              <p className="text-sm text-[var(--color-muted)]">Scan this code with an authenticator app (1Password, Authy, Google Authenticator…), then enter the 6-digit code it shows.</p>33              <div className="inline-block rounded-xl bg-white p-3" dangerouslySetInnerHTML={{ __html: qr.svg }} aria-label="QR code for the authenticator app" role="img" />34              <p className="text-xs text-[var(--color-faint)]">Manual key: <span className="mono break-all">{qr.secret}</span></p>35              <div><label htmlFor="code" className="label">Code</label><input id="code" name="code" inputMode="numeric" autoComplete="one-time-code" required className="field mt-1 max-w-[12rem] text-center text-xl tracking-[0.3em]" /></div>36              <button className="btn btn-primary">Turn on</button>37            </form>38          ) : (39            <form action={startTotp} className="space-y-4">40              <p className="text-sm text-[var(--color-warn)]">Off. Anyone with your password can enter the studio and spend the generation budget.</p>41              <button className="btn btn-primary">Set up two-factor authentication</button>42            </form>43          )}44        </Panel>45      </div>46    </>47  );48}49
Files main
Menu

Theme