16 Commits

Author SHA1 Message Date
0c6ea1ccff Fix QA review findings: server-side DOB validation, naive date max prop
- W-01: Add date_of_birth validators to RegisterRequest and ProfileUpdate
  (reject future dates and years before 1900)
- W-05: Replace .toISOString().slice() with local date formatting for
  DatePicker max prop on registration form

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 16:59:54 +08:00
013f9ec010 Add custom DatePicker component, replace all native date inputs
Custom date-picker.tsx with date/datetime modes, portal popup with
month/year dropdowns, min/max constraints, and hidden input for form
validation. Replaces all 10 native <input type="date"> and
<input type="datetime-local"> across LockScreen, SettingsPage,
PersonForm, TodoForm, TodoDetailPanel, TaskForm, TaskDetailPanel,
ProjectForm, ReminderForm, and ReminderDetailPanel. Adds Chromium
calendar icon invert CSS fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-03 02:30:52 +08:00
e8109cef6b Add required email + date of birth to registration, shared validators, partial index
- S-01: Extract _EMAIL_REGEX, _validate_email_format, _validate_name_field
  shared helpers in schemas/auth.py — used by RegisterRequest, ProfileUpdate,
  and admin.CreateUserRequest (eliminates 3x duplicated regex)
- S-04: Migration 038 replaces plain unique constraint on email with a
  partial unique index WHERE email IS NOT NULL
- Email is now required on registration (was optional)
- Date of birth is now required on registration, editable in settings
- User model gains date_of_birth (Date, nullable) column
- ProfileUpdate/ProfileResponse include date_of_birth
- Registration form adds required Email, Date of Birth fields
- Settings Profile card adds Date of Birth input (save-on-blur)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:21:11 +08:00
45f3788fb0 Add preferred name + email to registration, profile card to settings
Registration form now collects optional preferred_name and email fields.
Settings page Profile card expanded with first name, last name, and email
(editable via new GET/PUT /api/auth/profile endpoints). Email uniqueness
enforced on both registration and profile update. No migrations needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 19:02:42 +08:00
1aeb725410 Fix issues from QA review: hash upgrade ordering, interceptor scope, guard
- W-01: Move is_active check before hash upgrade so disabled accounts
  don't get their password hash silently mutated on rejected login
- W-02: Narrow interceptor exclusion to specific auth endpoints instead
  of blanket /auth/* prefix (future-proofs against new auth routes)
- W-03: Add null guard on optimistic setQueryData to handle undefined
  cache gracefully instead of spreading undefined
- S-01: Clear loginError when switching from register back to login mode
- S-03: Add detail dict to auth.login_blocked_inactive audit event

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 02:13:48 +08:00
5426657b2e Fix login error alert disappearing due to browser autofill
After a failed login, the browser's password manager fires onChange
events on the username/password inputs (clearing or resetting them).
The onChange handlers were calling setLoginError(null), which wiped
the error alert immediately after it appeared.

Fix: remove setLoginError(null) from input onChange handlers. The
error now clears at the start of the next submit attempt via the
existing setLoginError(null) in handleCredentialSubmit.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 01:37:57 +08:00
b2d81f7015 Block inactive user login + fix login flicker + inline error alerts
- Backend: reject is_active=False users with HTTP 403 after password
  verification but before session creation (prevents last_login_at
  update, lockout reset, and MFA token issuance for disabled accounts)
- Frontend: optimistic setQueryData on successful login eliminates the
  form flash between mutation success and auth query refetch
- LockScreen: replace lockoutMessage + toast.error with unified
  loginError inline alert for 401/403/423 responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 01:21:06 +08:00
72e00f3a69 Fix QA review #2: backup code flow, audit filters, schema hardening
C-01: verifyTotp now sends backup_code field when in backup mode
C-02: Backup code input filter allows alphanumeric chars (not digits only)
W-01: Audit log ACTION_TYPES aligned with actual backend action strings
W-02: Added extra="forbid" to SetupRequest and LoginRequest schemas

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 04:59:29 +08:00
464b8b911f Phase 8: Registration flow & MFA enforcement UI
- backend: add POST /auth/totp/enforce-setup and /auth/totp/enforce-confirm
  endpoints that operate on mfa_enforce_token (not session cookie), generate
  TOTP secret/QR/backup codes, verify confirmation code, enable TOTP, clear
  mfa_enforce_pending flag, and issue a full session cookie
- frontend: expand LockScreen to five modes — login, first-run setup,
  open registration, TOTP challenge, MFA enforcement setup (QR -> verify ->
  backup codes), and forced password change; all modes share AmbientBackground
  and the existing card layout; registration visible only when
  authStatus.registration_open is true

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 18:39:18 +08:00
f5265a589e Fix form validation: red outline only on submit, add required asterisks
- Remove instant invalid:ring/border from Input component (was showing
  red outline on empty required fields before any interaction)
- Add CSS rule: form[data-submitted] input:invalid shows red border
- Add global submit listener in main.tsx that sets data-submitted on forms
- Add required prop to Labels missing asterisks: PersonForm (First Name),
  LocationForm (Location Name), CalendarForm (Name), LockScreen
  (Username, Password, Confirm Password)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:53:15 +08:00
b0af07c270 Add lock screen, auto-lock timeout, and login visual upgrade
- Backend: POST /verify-password endpoint for lock screen re-auth,
  auto_lock_enabled/auto_lock_minutes columns on Settings with migration 025
- Frontend: LockProvider context with idle detection (throttled activity
  listeners, pauses during mutations), Lock button in sidebar, full-screen
  LockOverlay with password re-entry and "Switch account" option
- Settings: Security card with auto-lock toggle and configurable timeout (1-60 min)
- Visual: Upgraded login screen with large title, animated floating gradient
  orbs (3 drift keyframes), subtle grid overlay, shared AmbientBackground component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 10:03:12 +08:00
15c99152d3 Address QA review: model registry, NOT NULL constraint, variable naming, toggle defaults, lockout UX
- C3: Register User, UserSession, NtfySent, TOTPUsage, BackupCode in models/__init__.py
- C4: Enforce settings.user_id NOT NULL after backfill in migration 023, update model
- W4: Rename misleading current_user → current_settings in dashboard.py
- W5: Match NtfySettingsSection initial state defaults to backend (true/1/2)
- W8: Clear lockout banner on username/password input change in LockScreen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 04:34:21 +08:00
5a8819c4a5 Stage 6 Phase 2-3: LockScreen rewrite + SettingsPage restructure
- LockScreen: full rewrite — username/password auth (setup/login/TOTP states),
  ambient glow blobs, UMBRA wordmark in flex flow, animate-slide-up card,
  HTTP 423 lockout banner, Loader2 spinner, client-side password validation
- SettingsPage: two-column lg grid (Profile/Appearance/Weather left,
  Calendar/Dashboard right), fixed h-16 page header, icon-anchored CardHeaders,
  labeled accent swatch grid with aria-pressed, max-w-xs removed from name
  input, upcoming days onBlur save with NaN+no-op guard, Security card removed
- useSettings: remove deprecated changePin/isChangingPin stubs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 04:06:53 +08:00
1aaa2b3a74 Fix code review findings: security hardening and frontend fixes
Backend:
- Add rate limiting to login (5 attempts / 5 min window)
- Add secure flag to session cookies with helper function
- Add PIN min-length validation via Pydantic field_validator
- Fix naive datetime usage in todos.py (datetime.now() not UTC)
- Disable SQLAlchemy echo in production
- Remove auto-commit from get_db to prevent double commits
- Add lower bound filter to upcoming events query
- Add SECRET_KEY default warning on startup
- Remove create_all from lifespan (Alembic handles migrations)

Frontend:
- Fix ReminderForm remind_at slice for datetime-local input
- Add window.confirm() dialogs on all destructive actions
- Redirect authenticated users away from login screen
- Replace error: any with getErrorMessage helper in LockScreen

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 07:49:21 +08:00
e6387065ad updated name from lifemanager to umbra, 2026-02-15 20:21:55 +08:00
1f6519635f Initial commit 2026-02-15 16:13:41 +08:00