diff --git a/db/migrations/20260310094104_initial.sql b/db/migrations/20260310094104_initial.sql index 6c8afc4..22544a5 100644 --- a/db/migrations/20260310094104_initial.sql +++ b/db/migrations/20260310094104_initial.sql @@ -171,7 +171,7 @@ CREATE TABLE audit_logs ( metadata JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -CREATE INDEX idx_audit_logs_team_time ON audit_logs(team_id, created_at DESC); +CREATE INDEX idx_audit_logs_team_time ON audit_logs(team_id, created_at DESC, id DESC); CREATE INDEX idx_audit_logs_team_resource ON audit_logs(team_id, resource_type, created_at DESC); -- sandbox_metrics_snapshots diff --git a/db/queries/users.sql b/db/queries/users.sql index 1c902a3..783f22b 100644 --- a/db/queries/users.sql +++ b/db/queries/users.sql @@ -4,10 +4,10 @@ VALUES ($1, $2, $3, $4) RETURNING *; -- name: GetUserByEmail :one -SELECT * FROM users WHERE email = $1; +SELECT * FROM users WHERE email = $1 AND deleted_at IS NULL; -- name: GetUserByID :one -SELECT * FROM users WHERE id = $1; +SELECT * FROM users WHERE id = $1 AND deleted_at IS NULL; -- name: InsertUserOAuth :one INSERT INTO users (id, email, name) diff --git a/envd/internal/services/process/handler/multiplex.go b/envd/internal/services/process/handler/multiplex.go index a3f6ea3..88f0916 100644 --- a/envd/internal/services/process/handler/multiplex.go +++ b/envd/internal/services/process/handler/multiplex.go @@ -35,27 +35,27 @@ func NewMultiplexedChannel[T any](buffer int) *MultiplexedChannel[T] { c.mu.RUnlock() } + c.mu.Lock() c.exited.Store(true) - for _, cons := range c.channels { close(cons) } + c.mu.Unlock() }() return c } func (m *MultiplexedChannel[T]) Fork() (chan T, func()) { + m.mu.Lock() + defer m.mu.Unlock() + if m.exited.Load() { ch := make(chan T) close(ch) - return ch, func() {} } - m.mu.Lock() - defer m.mu.Unlock() - consumer := make(chan T, 4096) m.channels = append(m.channels, consumer) diff --git a/frontend/src/app.css b/frontend/src/app.css index 823f8ce..d76358b 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -180,9 +180,11 @@ body { 50% { transform: translateY(-6px); } } -/* CSS containment — isolate layout/paint for independent UI regions */ +/* CSS containment — isolate paint for independent UI regions. + Note: `contain: layout` is omitted because it creates a containing block + that breaks `position: fixed` popups rendered inside
. */ main { - contain: layout style; + contain: style; } /* Respect user motion preferences — covers both CSS class animations and inline style animations */ diff --git a/frontend/src/lib/components/AuthModal.svelte b/frontend/src/lib/components/AuthModal.svelte deleted file mode 100644 index fddedaa..0000000 --- a/frontend/src/lib/components/AuthModal.svelte +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - - - - - -
- -
- - {title} - - - {subtitle} - -
- - - - - -
-
- or -
-
- - -
- {#if mode === 'signup'} -
-
- -
- -
- {/if} - -
-
- -
- -
- -
-
- -
- - -
- - {#if mode === 'signin'} -
- -
- {/if} - - -
- - -

- {switchText} - -

-
-
-
-
- - diff --git a/frontend/src/lib/components/FilesTab.svelte b/frontend/src/lib/components/FilesTab.svelte index bdc2a75..46c2d4a 100644 --- a/frontend/src/lib/components/FilesTab.svelte +++ b/frontend/src/lib/components/FilesTab.svelte @@ -81,7 +81,7 @@ ); // Breadcrumb segments from currentPath - const breadcrumbs = $derived(() => { + const breadcrumbs = $derived.by(() => { const parts = currentPath.split('/').filter(Boolean); const crumbs: { name: string; path: string }[] = [{ name: '/', path: '/' }]; for (let i = 0; i < parts.length; i++) { @@ -517,7 +517,7 @@ - {#each breadcrumbs() as crumb, i} + {#each breadcrumbs as crumb, i} {#if i > 0} @@ -526,7 +526,7 @@ {t.message}
+ + {#snippet skeletonRows()}
diff --git a/frontend/src/routes/admin/teams/+page.svelte b/frontend/src/routes/admin/teams/+page.svelte index a09b3d9..90a8db2 100644 --- a/frontend/src/routes/admin/teams/+page.svelte +++ b/frontend/src/routes/admin/teams/+page.svelte @@ -360,7 +360,7 @@ -