Both toasts printed the endpoint's own message field verbatim, so a Russian UI
answered "Terminated 0 sessions". That string is assembled in Python and can
never be translated where it is built, so the endpoints now also return the raw
count and the phrase is composed on the client.
message is kept: it goes to the audit log and may have other consumers, and
adding a field breaks no one.
Russian needs three forms, and zero does not land in the obvious one — 0 is the
"many" category ("сессий", not "сессии") — hence four keys against English's
two.
Fixes both call sites: the per-user button in the table row and Terminate all
sessions on the Sessions tab, which shared the same untranslated string.
Extract ServerStatusBadge so every server list renders the auth-error
state identically instead of repeating the online/offline ternary, and
expose last_error_kind from the cluster topology and user-assignment
endpoints, which build their payloads by hand and so did not inherit it
from the server schema.
Add per-user quotas (max instances, vCPU, RAM, disk) enforced at deploy
time and surfaced to users self-service.
Backend:
- UserQuota model (one-to-one, NULL = unlimited) + migration #28
- quota_service: usage from vm_instances cache, check_quota raises 429
- enforce in VM (templates.py) and LXC (lxc.py) deploy paths
- RBAC quota:view / quota:manage, granted to admin
- admin API GET/PUT /admin/api/users/{id}/quota
- self-service GET /settings/api/quota
- tighten VM list isolation via can_view_all_instances
Frontend:
- admin quota dialog in UsersPage with usage bars
- quota card on Settings page (own limits)
- remaining-quota hint in CreateInstanceWizard config step
- useUserQuota/useSetUserQuota, useMyQuota hooks; RU/EN locales
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace all legacy dotted permission codes (vms.view, proxmox.manage, ...)
in PermissionChecker/has_permission across app/api with the new
resource:action format (vm:view, server:manage, ...), per LEGACY_PERMISSION_MAP.
All 33 codes used by the API are now registered in the permission registry.
Also switch the /api/auth/me admin branch to get_all_permissions_v2 so the
endpoint emits permissions in the new format only.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop dead route handlers that have an active replacement and zero callers
(frontend, backend, tests):
- create-lxc, create-lxc-smart -> superseded by POST /lxc/deploy
- clone-lxc -> superseded by container/{vmid}/clone
- {server_id}/lxc-templates -> duplicate of /lxc-templates/{server_id}
- dashboard/stats -> dashboard uses resources/all + alerts
- permissions (v1) -> superseded by permissions/v2
The identically-named ProxmoxClient/SecurityService methods are kept (they
back the surviving endpoints).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Migration 20: user_servers association table (User ↔ ProxmoxServer)
- API: GET/PUT /api/users/{id}/servers — assign servers to users with workspace-conflict validation
- API: GET /api/users/{id}/server-assignments — enriched server list (assigned + compatible flags)
- Non-admin server filter: intersection of assigned_servers and workspace servers
- Migration 21: dashboard:view = false for user role (new key format)
- Migration 22: proxmox.cluster.manage → cluster:manage for admin role
- API: GET/PUT /api/{server_id}/vm/{vmid}/owner — VM/LXC owner assignment (admin-only)
- Login: smart redirect based on user permissions after authentication
- Login: returnUrl param support for post-login redirect
- Roles API: user_count field per role in GET /api/roles
- node_uptime added to server detail API response
- Remove redundant PermissionChecker from GET /users HTML route
- Add password field to UserUpdate schema for password change on edit
- Implement password validation in update_user endpoint
- Add i18n translations for password validation errors (ru/en)
- Update SecurityService.validate_password to support language parameter
- Add GitHub icon and repository name 'pvemanager' to header button
- Fix internal server error when updating user password
- Clean up hardcoded translations in frontend