forked from wrenn/wrenn
Add user names, team-scoped sandbox guard, and login robustness fixes
- Add name column to users (migration + sqlc regen); propagate through JWT claims, auth context, all auth/OAuth handlers, service layer, and frontend - Sidebar and team page show name instead of email; team page splits Name/Email into separate columns - Block sandbox creation in UI and API when user has no active team context - loginTeam helper falls back to first active team when no default is set, fixing login for invited users with no is_default membership - Exclude soft-deleted teams from GetDefaultTeamForUser, GetBYOCTeams queries - Guard host creation against soft-deleted teams in service/host.go - SwitchTeam re-fetches name from DB instead of trusting stale JWT claim - Reset teams store on login so stale data from a previous session never persists - Update openapi.yaml: add name to SignupRequest and AuthResponse schemas
This commit is contained in:
@ -2,6 +2,7 @@
|
||||
import Sidebar from '$lib/components/Sidebar.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import { toast } from '$lib/toast.svelte';
|
||||
import { auth } from '$lib/auth.svelte';
|
||||
import {
|
||||
listCapsules,
|
||||
createCapsule,
|
||||
@ -440,7 +441,9 @@
|
||||
|
||||
<button
|
||||
onclick={() => { showCreateDialog = true; createError = null; }}
|
||||
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-4 py-2 text-ui font-semibold text-white transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0"
|
||||
disabled={!auth.teamId}
|
||||
title={!auth.teamId ? 'No active team — re-authenticate to create capsules' : undefined}
|
||||
class="flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-4 py-2 text-ui font-semibold text-white transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0 disabled:pointer-events-none disabled:opacity-40"
|
||||
>
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
|
||||
@ -494,7 +497,8 @@
|
||||
</p>
|
||||
<button
|
||||
onclick={() => { showCreateDialog = true; createError = null; }}
|
||||
class="mt-6 flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-5 py-2.5 text-ui font-semibold text-white transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0"
|
||||
disabled={!auth.teamId}
|
||||
class="mt-6 flex items-center gap-2 rounded-[var(--radius-button)] bg-[var(--color-accent)] px-5 py-2.5 text-ui font-semibold text-white transition-all duration-150 hover:brightness-115 hover:-translate-y-px active:translate-y-0 disabled:pointer-events-none disabled:opacity-40"
|
||||
>
|
||||
Launch a Capsule
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
||||
|
||||
@ -209,8 +209,8 @@
|
||||
);
|
||||
toast.success(
|
||||
newRole === 'admin'
|
||||
? `${member.email} is now an admin`
|
||||
: `${member.email} is now a member`
|
||||
? `${member.name || member.email} is now an admin`
|
||||
: `${member.name || member.email} is now a member`
|
||||
);
|
||||
} else {
|
||||
toast.error(result.error);
|
||||
@ -675,12 +675,17 @@
|
||||
>
|
||||
<!-- Table header -->
|
||||
<div
|
||||
class="grid grid-cols-[1fr_120px_140px_120px] border-b border-[var(--color-border)] bg-[var(--color-bg-3)]"
|
||||
class="grid grid-cols-[1fr_1fr_120px_140px_120px] border-b border-[var(--color-border)] bg-[var(--color-bg-3)]"
|
||||
>
|
||||
<div
|
||||
class="px-5 py-3 text-label font-semibold uppercase tracking-[0.05em] text-[var(--color-text-muted)]"
|
||||
>
|
||||
Member
|
||||
Name
|
||||
</div>
|
||||
<div
|
||||
class="px-4 py-3 text-label font-semibold uppercase tracking-[0.05em] text-[var(--color-text-muted)]"
|
||||
>
|
||||
Email
|
||||
</div>
|
||||
<div
|
||||
class="px-4 py-3 text-label font-semibold uppercase tracking-[0.05em] text-[var(--color-text-muted)]"
|
||||
@ -699,15 +704,15 @@
|
||||
|
||||
{#each members as member, i (member.user_id)}
|
||||
<div
|
||||
class="grid grid-cols-[1fr_120px_140px_120px] items-center border-b border-[var(--color-border)] transition-colors duration-150 hover:bg-[var(--color-bg-3)] last:border-b-0 {recentlyAddedId === member.user_id ? 'member-flash' : ''}"
|
||||
class="grid grid-cols-[1fr_1fr_120px_140px_120px] items-center border-b border-[var(--color-border)] transition-colors duration-150 hover:bg-[var(--color-bg-3)] last:border-b-0 {recentlyAddedId === member.user_id ? 'member-flash' : ''}"
|
||||
in:fly={{ y: 6, duration: 200, delay: i * 30, easing: cubicOut }}
|
||||
out:fly={{ x: -16, duration: 180, easing: cubicOut }}
|
||||
>
|
||||
<!-- Email -->
|
||||
<!-- Name -->
|
||||
<div class="min-w-0 px-5 py-4">
|
||||
<div class="flex min-w-0 items-center gap-2">
|
||||
<span class="truncate text-ui text-[var(--color-text-bright)]"
|
||||
>{member.email}</span
|
||||
>{member.name || member.email}</span
|
||||
>
|
||||
{#if member.user_id === auth.userId}
|
||||
<span class="shrink-0 rounded-[2px] bg-[var(--color-bg-4)] px-1.5 py-0.5 text-badge font-semibold uppercase tracking-[0.05em] text-[var(--color-text-muted)]">you</span>
|
||||
@ -715,6 +720,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div class="min-w-0 px-4 py-4">
|
||||
<span class="truncate font-mono text-ui text-[var(--color-text-secondary)]">{member.email}</span>
|
||||
</div>
|
||||
|
||||
<!-- Role badge -->
|
||||
<div class="px-4 py-4">
|
||||
{#if updatingRoleId === member.user_id}
|
||||
@ -1092,7 +1102,7 @@
|
||||
</h2>
|
||||
<p class="mt-2 text-ui text-[var(--color-text-tertiary)]">
|
||||
Remove <span class="font-medium text-[var(--color-text-secondary)]"
|
||||
>{removeTarget.email}</span
|
||||
>{removeTarget.name || removeTarget.email}</span
|
||||
> from the team? They will lose access immediately.
|
||||
</p>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user