From 95e30a74c7f8ab7f1bca518e8fb26ac78fa98639 Mon Sep 17 00:00:00 2001 From: egecankomur Date: Tue, 12 May 2026 15:45:33 +0300 Subject: [PATCH] fix: use admin client in onboarding guard to prevent accidental workspace creation Session client (getUserTeams) can return null when the session token is expired or stale. The old guard 'if (teams && teams.total > 0) redirect' was bypassed when teams was null, allowing users to create duplicate workspaces. New guard uses admin client (API key, never session-dependent): - lists user memberships via users.listMemberships(userId) - checks for a tenant_settings row in this app - redirects to /dashboard if found; shows form only for genuinely new users --- src/app/onboarding/page.tsx | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/app/onboarding/page.tsx b/src/app/onboarding/page.tsx index 8472cf0..f253e6a 100644 --- a/src/app/onboarding/page.tsx +++ b/src/app/onboarding/page.tsx @@ -1,8 +1,10 @@ import type { Metadata } from "next"; import { redirect } from "next/navigation"; +import { Query } from "node-appwrite"; -import { getCurrentUser } from "@/lib/appwrite/server"; -import { getUserTeams, getCrossAppTeams } from "@/lib/appwrite/tenant"; +import { getCurrentUser, createAdminClient } from "@/lib/appwrite/server"; +import { getCrossAppTeams } from "@/lib/appwrite/tenant"; +import { DATABASE_ID, TABLES } from "@/lib/appwrite/schema"; import { CreateWorkspaceForm } from "./components/create-workspace-form"; export const metadata: Metadata = { @@ -14,8 +16,26 @@ export default async function OnboardingPage() { const user = await getCurrentUser(); if (!user) redirect("/sign-in"); - const teams = await getUserTeams(); - if (teams && teams.total > 0) redirect("/dashboard"); + // Use admin client — never fails due to expired session tokens. + // If user already has a team with CRM settings, send them to dashboard. + try { + const { users, tablesDB } = createAdminClient(); + const memberships = await users.listMemberships(user.$id); + if (memberships.total > 0) { + const teamIds = memberships.memberships.map((m) => m.teamId); + const settings = await tablesDB.listRows({ + databaseId: DATABASE_ID, + tableId: TABLES.tenantSettings, + queries: [Query.equal("tenantId", teamIds), Query.limit(1)], + }); + if (settings.rows.length > 0) redirect("/dashboard"); + } + } catch { + // Admin client uses an API key (not a session), so this is extremely rare. + // If it does fail, show a 500 rather than silently letting the user create + // a duplicate workspace. + throw new Error("Onboarding guard check failed — server configuration issue."); + } const crossAppTeams = await getCrossAppTeams();