Fix team-mode first-run: /setup no longer bounces to /sign-in
Some checks failed
CI / ci (push) Has been cancelled
CI / docker-build (push) Has been cancelled
Publish Docker image / docker (push) Has been cancelled
Upload sourcemaps / upload (push) Has been cancelled

- _auth.setup.tsx: the "owner already exists?" check ran in beforeLoad, which
  executes during SSR where fetchTeamSetupStatus's relative fetch to
  /api/team-setup fails — so it always concluded an owner existed and
  redirected to /sign-in, making the create-owner screen unreachable. Move the
  check into a client-side effect with a loading state.
- setup-status.ts: add BETTER_AUTH_URL to CHECK_ENV_VARS so /api/health stops
  falsely reporting "team mode requires BETTER_AUTH_URL" when it is set (the
  Docker preflight already saw it; only the runtime health check's env
  allowlist was missing it).

Verified locally end to end against a D1 build in AUTH_MODE=team: owner
bootstrap, self-disable + 409 on repeat, Better Auth sign-in issues a session
cookie, and get-session resolves the shared organization.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
metatroncubeswdev 2026-09-09 14:19:28 -04:00
parent acc35c055c
commit 29bda614a4
2 changed files with 31 additions and 11 deletions

View File

@ -1,5 +1,5 @@
import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router";
import { useState } from "react";
import { createFileRoute, useNavigate } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import { AuthPageCard } from "@/client/features/auth/AuthPage";
import {
bootstrapTeamOwner,
@ -7,27 +7,38 @@ import {
} from "@/client/features/auth/teamSetup";
import { authClient } from "@/lib/auth-client";
// First-run screen for `team` mode: creates the single owner account. It
// redirects to /sign-in the moment an owner exists, so it can't be used to add
// more users.
// First-run screen for `team` mode: creates the single owner account. The
// "already has an owner?" check runs client-side (it needs a DB round trip via
// /api/team-setup, which a server-side beforeLoad can't reach with a relative
// URL) and redirects to /sign-in once an owner exists.
export const Route = createFileRoute("/_auth/setup")({
beforeLoad: async () => {
const status = await fetchTeamSetupStatus();
if (!status.needsOwner) {
throw redirect({ to: "/sign-in", search: {} });
}
},
component: SetupPage,
});
function SetupPage() {
const navigate = useNavigate();
const [checking, setChecking] = useState(true);
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
useEffect(() => {
let cancelled = false;
void fetchTeamSetupStatus().then((status) => {
if (cancelled) return;
if (!status.needsOwner) {
void navigate({ to: "/sign-in", search: {} });
return;
}
setChecking(false);
});
return () => {
cancelled = true;
};
}, [navigate]);
async function handleSubmit(event: React.FormEvent) {
event.preventDefault();
setError(null);
@ -57,6 +68,14 @@ function SetupPage() {
}
}
if (checking) {
return (
<div className="flex justify-center py-10">
<span className="loading loading-spinner loading-md" />
</div>
);
}
return (
<AuthPageCard
title="Create the owner account"

View File

@ -27,6 +27,7 @@ const CHECK_ENV_VARS = [
"DATAFORSEO_API_KEY",
"GOOGLE_CLIENT_ID",
"GOOGLE_CLIENT_SECRET",
"BETTER_AUTH_URL",
"BETTER_AUTH_SECRET",
"OPENROUTER_API_KEY",
] as const;