feat: RBAC, atomic deployments, and fleet-wide backups (Pro) (#181)

* feat: add RBAC viewer accounts, atomic deployments, and fleet-wide backups (Pro)

Introduces three Pro-tier features:

- RBAC: Multi-user system with admin/viewer roles, user management UI,
  automatic migration from single-admin credentials, viewer restrictions
  across the entire UI (read-only editor, hidden action buttons)

- Atomic Deployments: Pre-deploy file backup to .sencho-backup/, automatic
  rollback on health probe failure, manual rollback button, health probes
  added to stack updates, webhook-triggered deploys use atomic rollback

- Fleet-Wide Backups: Point-in-time snapshots of compose files across all
  nodes (local + remote), stored centrally in SQLite, per-stack restore
  with optional redeploy, graceful handling of offline nodes

* fix(settings): use correct ProGate prop name in UsersSection

* fix(settings): remove unused isPro prop from UsersSection

* fix(auth): fetch user info after login and setup so isAdmin is set correctly
This commit is contained in:
Anso
2026-03-26 12:51:30 -04:00
committed by GitHub
parent 72670ffb42
commit db73d7671a
21 changed files with 2466 additions and 292 deletions
@@ -15,7 +15,7 @@ interface UserProfileDropdownProps {
}
export function UserProfileDropdown({ theme, setTheme, onOpenSettings }: UserProfileDropdownProps) {
const { logout } = useAuth();
const { logout, user, isAdmin } = useAuth();
const { license, isPro } = useLicense();
return (
@@ -33,8 +33,12 @@ export function UserProfileDropdown({ theme, setTheme, onOpenSettings }: UserPro
<User className="w-4 h-4 text-muted-foreground" />
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium truncate">admin</p>
<p className="text-sm font-medium truncate">{user?.username ?? 'admin'}</p>
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<span className={`px-1.5 py-0.5 rounded text-[10px] font-medium uppercase ${isAdmin ? 'bg-primary/10 text-primary' : 'bg-muted text-muted-foreground'}`}>
{user?.role ?? 'admin'}
</span>
<span className="text-muted-foreground/40">·</span>
{isPro ? <ProBadge /> : <span>Community</span>}
</div>
</div>