feat(web): add account management page and refactor providers page - #64
Conversation
The self-hosted single-admin account had no way to change its password or email after setup, and no documented recovery path if the password was forgotten entirely. Add an Account page (/account) to the dashboard: - Change email (updates users.email; login uses the new address). - Change password (current password required to confirm; validates the new password against the shared policy and writes a fresh salt+hash). - Wrong current password is rejected with a clear error. Backend: DbClient::find_user_by_id / update_password / update_email, auth service change_password / change_email, DTOs, WebServices facades, and ApiError derives Debug. Recovery: memayu reset-password '<new>' CLI command resets the admin password without the login gate (memayu-identity::reset_password, works for libsql and postgres). Documented in the README. Tests: 5 auth service tests (change works, old rejected, wrong current rejected, weak/mismatch rejected, email change used by login), 2 identity reset_password tests, and a dashboard HTTP integration test covering the full /account flow.
- Split LLM and embedder settings into dedicated cards - Add interactive local model selection and backend controls - Correct multilingual model dimensions and language labels - Remove redundant setup model dimension badge
|
Review of PR #64 at 🔴 BlockingNone. CI at review time: 🟡 Warning #1 — Password hashing is single-iteration SHA-256 (existing, not introduced here)
🟡 Warning #2 —
|
Summary
Implements issue #50: a self-hosted instance had no way to change its password or email after first-run setup, and no documented recovery path if the password was forgotten.
This PR adds an
/accountpage to the dashboard plus the supporting backend:memayu-identitycrate (already onmain; this PR extends it):reset_password(config, password): new recovery path that bypasses the login gate — the "forgot password" flow for a self-hosted single-admin instance. Validates the new password against the shared policy and writes a fresh salt + hash.new_api_key/generate_api_key:mmyu_…API key generation (shared with terminal setup; stores only the SHA-256 hash, raw key shown once).crates/memayu-web/src/pages/account.rs): Change Email and Change Password forms.Change Passwordrequires the current password, the shared password policy (8+ chars; upper/lower/digit), and a confirmation match.lib.rs): protected/account(and the new auth mutations) mounted on the authenticated/homerouter;logoutredirect fixed to/login.memayu reset-password <new-password>subcommand backed by the newmemayu-identity::reset_password(physical-access recovery path).ref(web): Refine provider configuration UI): split the provider form into a shared, reusable component and wired it into both the first-run setup flow and the account/providers dashboard page./account→ 401; wrong current password → 400; success → 204 + reflected change) + a dashboardaccount_page_loads_*test.Migration / breaking
userstable DDL (email UNIQUE,salt NOT NULL,is_admin) is unchanged; new mutations reuse existing columns.hash_passwordcontinues to use SHA-256(salt‖password) with a per-account 16-byte salt — see Security note below.memayu-identity::{reset_password, new_api_key, generate_api_key}andmemayu-api::{ChangePasswordRequest, ChangeEmailRequest}.Security note (out of scope to fix here)
Password hashing is SHA-256 with one iteration and a 128-bit per-account salt. This is fast and brute-forceable offline given a DB leak. It is existing behavior of
memayu-identity(not introduced by this PR) and is adequate only for a single-admin self-hosted tool with no network-exposed password hash; a follow-up should move to a memory-hard KDF (argon2id/bcrypt/scrypt orpbkdf2with many iterations). Flagged here as a tracking note, not a review blocker.Checklist
memayu-identitycrate so terminal and web use identical account logic.accountsubcommands.Issues
memayu-identityenforces the documentedmmyu_…format.