Skip to content

feat(auth): migrate from Clerk to Better Auth#543

Merged
saddlepaddle merged 3 commits intomainfrom
fix-electric-setup
Jan 6, 2026
Merged

feat(auth): migrate from Clerk to Better Auth#543
saddlepaddle merged 3 commits intomainfrom
fix-electric-setup

Conversation

@saddlepaddle
Copy link
Copy Markdown
Collaborator

@saddlepaddle saddlepaddle commented Dec 29, 2025

Summary

  • Migrates authentication from Clerk to Better Auth with organization plugin
  • Adds bearer token plugin for desktop app authentication
  • Moves auth tables to dedicated auth PostgreSQL schema with UUID columns
  • Migrates existing users, organizations, and members from backup tables
  • Backfills personal workspaces for users without an organization

Changes

Auth System

  • Better Auth configuration with drizzle adapter, organization plugin, and bearer plugin
  • Session tokens with 30-day expiry, daily refresh on activity
  • Cross-subdomain cookies enabled
  • GitHub and Google OAuth providers

Database

  • New auth schema with tables: users, sessions, accounts, verifications, organizations, members, invitations
  • UUID columns with database-generated IDs (generateId: false)
  • Data migration from old tables (preserves existing UUIDs)
  • Workspace backfill for users without organizations

Desktop App

  • Simplified auth flow using Better Auth bearer tokens
  • Single session token instead of 4-token system (access, refresh, expiries)

Cleanup

  • Removed Clerk dependencies
  • Removed DESKTOP_AUTH_SECRET, CLERK env vars
  • Removed sync-user-from-clerk utility

Test plan

  • Sign in with GitHub OAuth
  • Sign in with Google OAuth
  • Verify session persists across page reloads
  • Verify organization switching works
  • Test desktop app OAuth flow
  • Verify Electric SQL sync with bearer token

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • New authentication backend with desktop sign-in flow and a desktop redirect component.
    • Desktop and client apps: token/session-driven organization switching.
  • Improvements

    • Simplified web sign-in and sign-up experiences; unified session handling across web, admin, marketing, and desktop.
    • Avatar rendering standardized across apps.
  • Removed

    • "Deleted Users" page and its sidebar entry.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Dec 29, 2025

📝 Walkthrough

Walkthrough

Replace Clerk with a new Better Auth stack: add @superset/auth package and auth DB schema, remove Clerk integrations across apps, convert middleware/TRPC/context to session-based flows, introduce server-side desktop success handling, refactor desktop auth/collections/providers, update CI/env vars and DB migrations.

Changes

Cohort / File(s) Summary
New auth package
packages/auth/*, packages/auth/src/*
Add @superset/auth package with server bootstrap (src/index.ts), client (src/client.ts), and env (src/env.ts); exports auth, Session, and User.
DB migrations & schema
packages/db/drizzle/0005_*.sql, 0006_*.sql, 0007_*.sql, packages/db/src/schema/auth.ts, packages/db/src/schema/*, packages/db/drizzle/meta/*
Add new auth schema and tables, backup old tables, migrate data, update relations and snapshots; new exported auth schema and types.
Env, CI & deps
.env.example, .github/workflows/*, apps/*/package.json, packages/*/package.json
Remove Clerk env vars; add BETTER_AUTH_SECRET; replace @clerk/* deps with @superset/auth and better-auth; update workflow env propagation.
API auth & routes
apps/api/src/app/api/auth/[...all]/route.ts, apps/api/src/app/api/auth/desktop/connect/route.ts, apps/api/src/app/api/auth/desktop/*
Add generic Next handler for auth via toNextJsHandler(auth), add desktop/connect social endpoint; remove many Clerk-based desktop token/refresh/github/google route implementations.
Middleware & TRPC context
apps/api/src/proxy.ts, apps/api/src/trpc/context.ts, apps/*/src/trpc/react.tsx
Replace Clerk middleware with session-based auth using auth.api.getSession; TRPC client/server headers simplified (credentials: include, static x-trpc-source); proxy handlers updated for CORS/preflight and session-based redirects.
Electric proxy & utils
apps/api/src/app/api/electric/[...path]/route.ts, .../utils.ts
Switch proxy auth to session and activeOrganizationId, change allowed table set to auth.* names, rebuild where-clause handling for auth names.
Web, Admin, Marketing apps
apps/web/src/**, apps/admin/src/**, apps/marketing/src/**
Remove ClerkProvider and Clerk middleware; use @superset/auth client/session (authClient/auth.api.getSession); update sign-in/signup/social flows, desktop success flow, PostHog identification, and admin navigation (remove Deleted Users page/nav).
Desktop auth refactor
apps/desktop/src/main/lib/auth/*, apps/desktop/src/main/index.ts, apps/desktop/src/lib/trpc/routers/auth/*
Remove legacy PKCE/token-storage/deep-link-handler/auth-service files; add new consolidated auth.ts with authService, parseAuthDeepLink, subscriptions, setActiveOrganization, and updated TRPC auth router endpoints/subscriptions.
Desktop providers & collections
apps/desktop/src/renderer/contexts/*, apps/desktop/src/renderer/contexts/CollectionsProvider/*, apps/desktop/src/renderer/screens/*
Introduce new CollectionsProvider and createCollections factory (org-scoped); remove older AuthProvider and ActiveOrganizationProvider; update components to use collections, change avatar property (avatarUrl → image).
Desktop UI & window
apps/desktop/src/main/windows/main.ts, apps/desktop/package.json
Change window sizing (primary display), remove window-state persistence, add Electron partition, bump desktop deps and add @superset/auth.
Misc UI & terminal
apps/*/src/**/LeaderboardTable, UsersTable, NavUser, AvatarDropdown, AccountSettings, Terminal/Terminal.tsx
Replace avatar property usages to image, change sign-out to authClient flows, remove in-terminal file-link handling and related terminal guards.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as User Browser
    participant Web as Next.js App
    participant Auth as Better Auth (`@superset/auth`)
    participant DB as Database
    participant Desktop as Desktop App

    rect rgb(230,245,255)
    Note over Browser,Auth: Social sign-in (web flow)
    Browser->>Web: GET /api/auth/desktop/connect?provider=google
    Web->>Auth: auth.api.signInSocial(provider, { callbackURL })
    Auth->>DB: findOrCreate user, create session
    DB-->>Auth: user + session
    Auth-->>Web: response with set-cookie + redirect URL
    Web-->>Browser: redirect to provider / set cookies
    end

    rect rgb(245,255,230)
    Note over Browser,Desktop: Desktop deep-link callback / session-based desktop success
    Browser->>Web: GET /auth/desktop/success?desktop_state=...
    Web->>Auth: auth.api.getSession({ headers })
    Auth->>DB: validate session, return activeOrganizationId
    DB-->>Auth: session + user + org
    Auth-->>Web: session data
    Web->>Browser: render DesktopRedirect with desktop URL (token+state)
    Browser->>Desktop: open desktop:// callback URL
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • AviPeltz
  • Kitenite

Poem

🐰 Hops of change across the stack,

Clerk steps down, Better Auth on track.
Tables bloom and sessions wake,
Desktop links that no longer break.
A tiny rabbit cheers — new paths we make!

Pre-merge checks

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(auth): migrate from Clerk to Better Auth' clearly and concisely describes the main change in the pull request, which is a complete authentication system migration.
Description check ✅ Passed The PR description is comprehensive and covers the key aspects: summary of migration, auth system changes, database schema updates, desktop app changes, cleanup tasks, and a test plan. It aligns well with the template structure and provides sufficient detail about the changeset.

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c592ee1 and 5bd0723.

📒 Files selected for processing (2)
  • .github/workflows/deploy-preview.yml
  • .github/workflows/deploy-production.yml
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/workflows/deploy-preview.yml
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Deploy Admin
  • GitHub Check: Deploy Web
  • GitHub Check: Deploy Docs
  • GitHub Check: Deploy API
  • GitHub Check: Deploy Marketing
  • GitHub Check: Build
🔇 Additional comments (5)
.github/workflows/deploy-production.yml (5)

76-76: LGTM — BETTER_AUTH_SECRET correctly added to deploy-api.

The secret is properly defined in the env: block and passed to the Vercel deploy command.

Also applies to: 107-107


165-165: LGTM — BETTER_AUTH_SECRET correctly added to deploy-web.

Also applies to: 178-178


223-223: LGTM — BETTER_AUTH_SECRET correctly added to deploy-marketing.

Also applies to: 234-234


279-279: LGTM — BETTER_AUTH_SECRET correctly added to deploy-admin.

Also applies to: 294-294


332-352: No changes needed—docs app does not use authentication.

The docs application is a Nextra documentation site with no authentication libraries in its dependencies. The only "auth"-related configuration is Sentry's authToken for error reporting. Since the docs app has no authentication framework (no better-auth, next-auth, or similar), omitting BETTER_AUTH_SECRET from the deploy-docs step is correct.

Likely an incorrect or invalid review comment.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Dec 29, 2025

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch
  • ⚠️ Electric Fly.io app

Thank you for your contribution! 🎉

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/trpc/src/router/organization/organization.ts (2)

59-81: Manual UUID generation conflicts with database defaults.

Lines 62 and 72 use crypto.randomUUID() to generate IDs, but the database schema (based on the migration) defines columns with DEFAULT gen_random_uuid(). The PR summary mentions generateId: false, which suggests database-generated IDs are disabled in the Better Auth config.

However, this creates inconsistency:

  1. Some code paths rely on database defaults (e.g., when values are not explicitly provided)
  2. Other code paths use manual generation
  3. Different UUID generation methods can have different formatting/casing

For consistency, either:

  1. Remove the database DEFAULT gen_random_uuid() from the migration if manual generation is always used
  2. Or rely on database defaults by omitting the id field from the insert values
🔎 Recommended approach: Let database handle IDs
 const [organization] = await db
   .insert(organizations)
-  .values({
-    id: crypto.randomUUID(),
+  .values({
     name: input.name,
     slug: input.slug,
     logo: input.logo,
     createdAt: new Date(),
   })
   .returning();

 if (organization) {
   await db.insert(members).values({
-    id: crypto.randomUUID(),
     organizationId: organization.id,
     userId: ctx.session.user.id,
     role: "owner",
     createdAt: new Date(),
   });
 }

83-99: Weakened type validation on organization ID.

Line 86 changed the id validation from z.string().uuid() to z.string(). This allows non-UUID strings to pass validation, which will fail at the database level since the id column is of type uuid.

🔎 Proposed fix
 .input(
   z.object({
-    id: z.string(),
+    id: z.string().uuid(),
     name: z.string().min(1).optional(),
     logo: z.string().url().optional(),
   }),
 )

Based on library documentation, Zod 4 maintains .uuid() validation for strings, so this should work correctly.

♻️ Duplicate comments (1)
apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (1)

32-46: Same loading state concern applies to GitHub sign-in.

This has the same potential issue as the Google sign-in handler—verify that the loading state management works correctly with OAuth redirects.

🧹 Nitpick comments (16)
apps/desktop/src/main/lib/auth/auth.ts (3)

103-105: Empty catch block silently ignores all errors.

While ENOENT (file doesn't exist) is expected, other errors (permission denied, disk full) would be silently swallowed. Consider logging unexpected errors.

🔎 Proposed fix
 async signOut(): Promise<void> {
   this.token = null;
   try {
     await fs.unlink(TOKEN_FILE);
-  } catch {}
+  } catch (err) {
+    // ENOENT is expected if token file doesn't exist
+    if ((err as NodeJS.ErrnoException).code !== "ENOENT") {
+      console.warn("[auth] Failed to remove token file:", err);
+    }
+  }
   this.emit("state-changed", this.getState());
 }

57-59: Add return type annotation for consistency.

getState() has an implicit return type while other methods are explicitly typed. Consider adding the return type for consistency and better IDE support.

🔎 Proposed fix
-getAccessToken(): string | null {
+getAccessToken(): string | null {
   return this.token;
 }

-getState() {
+getState(): { isSignedIn: boolean } {
   return { isSignedIn: !!this.token };
 }

66-69: State pruning only occurs during signIn calls.

The stale state cleanup only runs when signIn is called. If a user initiates sign-in but never completes it, and then doesn't attempt to sign in again, those stale states persist in memory indefinitely.

This is a minor concern since:

  • Memory impact is negligible (small strings)
  • App restarts clear the store
  • States older than 10 minutes are functionally useless anyway

Consider this acceptable for now, but if you want periodic cleanup:

🔎 Optional: Move cleanup to a separate method
+private pruneExpiredStates(): void {
+  const tenMinutesAgo = Date.now() - 10 * 60 * 1000;
+  for (const [s, ts] of stateStore) {
+    if (ts < tenMinutesAgo) stateStore.delete(s);
+  }
+}

 async signIn(provider: AuthProvider): Promise<SignInResult> {
   try {
     const state = crypto.randomBytes(32).toString("base64url");
     stateStore.set(state, Date.now());
-
-    const tenMinutesAgo = Date.now() - 10 * 60 * 1000;
-    for (const [s, ts] of stateStore) {
-      if (ts < tenMinutesAgo) stateStore.delete(s);
-    }
+    this.pruneExpiredStates();
apps/web/src/app/(dashboard)/components/Header/Header.tsx (1)

24-27: Consider adding error handling for the sign-out flow.

The async handleSignOut function doesn't handle potential errors from authClient.signOut(). If the sign-out request fails, the user would be redirected to /sign-in anyway, potentially causing confusion.

🔎 Proposed improvement
 const handleSignOut = async () => {
-	await authClient.signOut();
-	router.push("/sign-in");
+	try {
+		await authClient.signOut();
+		router.push("/sign-in");
+	} catch (error) {
+		console.error("Sign out failed:", error);
+		// Optionally show a toast or still redirect
+		router.push("/sign-in");
+	}
 };
packages/auth/src/env.ts (3)

6-6: Hardcoded relative path may be fragile.

The path "../../../.env" assumes a fixed directory structure relative to packages/auth/src/. This could break if the package is moved or if process.cwd() differs during execution (e.g., different build/test contexts).

🔎 Consider using a more robust path resolution
-config({ path: path.resolve(process.cwd(), "../../../.env") });
+// Find monorepo root by looking for a marker file or use a shared utility
+const monorepoRoot = path.resolve(__dirname, "../../../..");
+config({ path: path.resolve(monorepoRoot, ".env") });

Alternatively, rely on environment variables being set by the host application rather than loading from a specific file path.


9-15: Add minimum length validation for secrets to catch empty strings early.

While emptyStringAsUndefined: true converts empty strings to undefined, adding .min(1) provides clearer error messages when secrets are missing. This aligns with the pattern in apps/api/src/env.ts where secrets have minimum length validation.

🔎 Proposed validation enhancement
 server: {
-	GH_CLIENT_ID: z.string(),
-	GH_CLIENT_SECRET: z.string(),
-	GOOGLE_CLIENT_ID: z.string(),
-	GOOGLE_CLIENT_SECRET: z.string(),
-	BETTER_AUTH_SECRET: z.string(),
+	GH_CLIENT_ID: z.string().min(1),
+	GH_CLIENT_SECRET: z.string().min(1),
+	GOOGLE_CLIENT_ID: z.string().min(1),
+	GOOGLE_CLIENT_SECRET: z.string().min(1),
+	BETTER_AUTH_SECRET: z.string().min(32),
 },

26-26: Consider making skipValidation conditional.

With skipValidation: true, environment validation is always skipped, which could mask missing or invalid environment variables until runtime. Other env files in the codebase use a conditional like !!process.env.SKIP_ENV_VALIDATION.

🔎 Conditional validation pattern
-skipValidation: true,
+skipValidation: !!process.env.SKIP_ENV_VALIDATION,
apps/api/src/app/api/electric/[...path]/route.ts (1)

56-70: Consider adding a timeout to the fetch call.

The fetch call to the Electric origin has no timeout, which could cause the request to hang indefinitely if the upstream service is unresponsive. This could exhaust connection resources under load.

🔎 Add AbortSignal timeout
-const response = await fetch(originUrl.toString());
+const response = await fetch(originUrl.toString(), {
+	signal: AbortSignal.timeout(30_000), // 30 second timeout
+});
apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx (1)

16-46: Consider extracting shared OAuth logic.

The signUpWithGoogle and signUpWithGithub functions share nearly identical logic. Consider extracting a shared helper function:

const signUpWithProvider = async (
  provider: "google" | "github",
  setLoading: (loading: boolean) => void
) => {
  setLoading(true);
  setError(null);
  try {
    await authClient.signIn.social({
      provider,
      callbackURL: env.NEXT_PUBLIC_WEB_URL,
    });
  } catch (err) {
    console.error("Sign up failed:", err);
    setError("Failed to sign up. Please try again.");
    setLoading(false);
  }
};

This reduces duplication and makes future maintenance easier.

apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx (1)

9-11: Consider error handling for session retrieval.

The session retrieval looks correct, but auth.api.getSession() could potentially throw an error or return an unexpected value if the auth service is unavailable.

🔎 Optional: Add error boundary or fallback
 export async function CTAButtons() {
-	const session = await auth.api.getSession({ headers: await headers() });
+	let session;
+	try {
+		session = await auth.api.getSession({ headers: await headers() });
+	} catch (error) {
+		console.error("Failed to get session:", error);
+		session = null;
+	}

 	if (session) {

Alternatively, let the error propagate to the nearest error boundary if that's the intended behavior.

apps/api/src/lib/auth.ts (1)

3-7: Consider adding an explicit return type annotation.

While TypeScript can infer the return type from auth.api.getSession, adding an explicit return type would improve code clarity and help catch type mismatches earlier.

🔎 Proposed enhancement with explicit typing
+import type { Session } from "better-auth/types";
+
-export async function getSession(request: Request) {
+export async function getSession(request: Request): Promise<Session | null> {
 	return auth.api.getSession({
 		headers: request.headers,
 	});
 }
apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (1)

9-24: Consider using session data directly instead of additional TRPC query.

The component fetches user data via trpc.user.me.queryOptions() when session.user likely contains the necessary fields (id, email, name). The web app's equivalent component (apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx) uses session.user directly, avoiding the extra round-trip.

🔎 Proposed simplification matching the web app pattern
 export function PostHogUserIdentifier() {
 	const { data: session } = authClient.useSession();
-	const trpc = useTRPC();
-
-	const { data: user } = useQuery({
-		...trpc.user.me.queryOptions(),
-		enabled: !!session?.user,
-	});
 
 	useEffect(() => {
-		if (user) {
-			posthog.identify(user.id, { email: user.email, name: user.name });
+		if (session?.user) {
+			posthog.identify(session.user.id, {
+				email: session.user.email,
+				name: session.user.name,
+			});
-		} else if (!session?.user) {
+		} else if (session === null) {
 			posthog.reset();
 		}
-	}, [user, session?.user]);
+	}, [session]);
 
 	return null;
 }
apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx (1)

42-50: Consider adding error handling for sign-out failures.

The handleSignOut function lacks error handling for potential sign-out failures. While sign-out operations typically succeed, network errors or API issues could occur.

🔎 Proposed enhancement with error handling
 	const handleSignOut = async () => {
+		try {
 			await authClient.signOut({
 				fetchOptions: {
 					onSuccess: () => {
 						window.location.href = env.NEXT_PUBLIC_WEB_URL;
 					},
 				},
 			});
+		} catch (error) {
+			console.error("Sign out failed:", error);
+			// Optionally show a toast notification to the user
+		}
 	};
packages/trpc/src/router/integration/integration.ts (1)

21-23: Use TRPCError for consistent error handling.

The membership check throws a generic Error instead of TRPCError. This is inconsistent with the TRPC error handling pattern used elsewhere (e.g., in trpc.ts).

🔎 Proposed fix
+import { TRPCError } from "@trpc/server";
+
 // ...
 
 			if (!membership) {
-				throw new Error("Not a member of this organization");
+				throw new TRPCError({
+					code: "FORBIDDEN",
+					message: "Not a member of this organization",
+				});
 			}
apps/web/src/proxy.ts (1)

18-37: LGTM with optional refactor suggestion.

The proxy logic correctly handles authentication redirects:

  • Logged-in users are redirected away from sign-in/sign-up pages
  • Logged-out users are redirected to sign-in unless on a public route

For slightly better clarity and to avoid potential edge cases with startsWith (e.g., /sign-in-test matching /sign-in), consider using exact matches or the existing isPublicRoute helper:

🔎 Optional refactor for consistency
 const pathname = req.nextUrl.pathname;

 if (
   session &&
-  (pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up"))
+  (pathname === "/sign-in" || pathname === "/sign-up")
 ) {
   return NextResponse.redirect(new URL("/", req.url));
 }

Or if sign-in/sign-up have sub-paths, this is fine as-is.

packages/trpc/src/router/admin/admin.ts (1)

16-22: Clarify comment and add error handling for non-existent users.

The comment on line 19 is misleading. Cascading deletes are handled by the database-level foreign key constraints (configured with onDelete: "cascade" in the schema), not by the Better Auth library.

Additionally, the mutation currently returns { success: true } even if the user doesn't exist. Consider verifying that a user was actually deleted.

🔎 Proposed improvements
 	deleteUser: adminProcedure
 		.input(z.object({ userId: z.string() }))
 		.mutation(async ({ input }) => {
-			// Delete user - Better Auth handles cascading session cleanup
-			await db.delete(users).where(eq(users.id, input.userId));
-			return { success: true };
+			// Delete user - database FK constraints handle cascading deletes
+			const result = await db.delete(users).where(eq(users.id, input.userId));
+			
+			if (result.rowCount === 0) {
+				throw new TRPCError({
+					code: "NOT_FOUND",
+					message: "User not found",
+				});
+			}
+			
+			return { success: true };
 		}),

Note: You'll need to re-add the TRPCError import if you implement this suggestion.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8affa1 and fff4289.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (96)
  • .env.example
  • apps/admin/package.json
  • apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
  • apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx
  • apps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsx
  • apps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsx
  • apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/DeletedUsersTable.tsx
  • apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/index.ts
  • apps/admin/src/app/(dashboard)/users/deleted/page.tsx
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/admin/src/env.ts
  • apps/admin/src/proxy.ts
  • apps/admin/src/trpc/react.tsx
  • apps/api/next.config.ts
  • apps/api/package.json
  • apps/api/src/app/api/auth/[...all]/route.ts
  • apps/api/src/app/api/auth/desktop/connect/route.ts
  • apps/api/src/app/api/auth/desktop/github/route.ts
  • apps/api/src/app/api/auth/desktop/google/route.ts
  • apps/api/src/app/api/auth/desktop/refresh/route.ts
  • apps/api/src/app/api/auth/desktop/tokens.ts
  • apps/api/src/app/api/electric/[...path]/route.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • apps/api/src/app/api/integrations/linear/connect/route.ts
  • apps/api/src/env.ts
  • apps/api/src/lib/auth.ts
  • apps/api/src/proxy.ts
  • apps/api/src/trpc/context.ts
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/lib/trpc/routers/index.ts
  • apps/desktop/src/main/index.ts
  • apps/desktop/src/main/lib/auth/auth-service.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/crypto-storage.ts
  • apps/desktop/src/main/lib/auth/deep-link-handler.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/main/lib/auth/pkce.ts
  • apps/desktop/src/main/lib/auth/token-storage.ts
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/shared/auth.ts
  • apps/marketing/package.json
  • apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
  • apps/marketing/src/app/layout.tsx
  • apps/marketing/src/env.ts
  • apps/marketing/src/proxy.ts
  • apps/web/package.json
  • apps/web/src/app/(auth)/layout.tsx
  • apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
  • apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
  • apps/web/src/app/(auth)/sso-callback/page.tsx
  • apps/web/src/app/(dashboard)/components/Header/Header.tsx
  • apps/web/src/app/api/auth/desktop/github/route.ts
  • apps/web/src/app/api/auth/desktop/google/route.ts
  • apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx
  • apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
  • apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx
  • apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/web/src/app/layout.tsx
  • apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/web/src/env.ts
  • apps/web/src/proxy.ts
  • apps/web/src/trpc/react.tsx
  • packages/auth/package.json
  • packages/auth/src/client.ts
  • packages/auth/src/env.ts
  • packages/auth/src/index.ts
  • packages/auth/tsconfig.json
  • packages/db/drizzle/0005_back_up_user_tables.sql
  • packages/db/drizzle/0006_add_better_auth_tables.sql
  • packages/db/drizzle/meta/0005_snapshot.json
  • packages/db/drizzle/meta/0006_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/package.json
  • packages/db/src/env.ts
  • packages/db/src/schema/auth.ts
  • packages/db/src/schema/index.ts
  • packages/db/src/schema/relations.ts
  • packages/db/src/schema/schema.ts
  • packages/trpc/package.json
  • packages/trpc/src/env.ts
  • packages/trpc/src/router/admin/admin.ts
  • packages/trpc/src/router/analytics/analytics.ts
  • packages/trpc/src/router/integration/integration.ts
  • packages/trpc/src/router/integration/linear/linear.ts
  • packages/trpc/src/router/integration/linear/utils.ts
  • packages/trpc/src/router/organization/organization.ts
  • packages/trpc/src/router/task/task.ts
  • packages/trpc/src/router/user/user.ts
  • packages/trpc/src/router/user/utils/sync-user-from-clerk.ts
  • packages/trpc/src/trpc.ts
💤 Files with no reviewable changes (28)
  • apps/web/src/app/auth/desktop/success/components/LoadingFallback/LoadingFallback.tsx
  • apps/desktop/src/main/lib/auth/crypto-storage.ts
  • apps/api/src/env.ts
  • packages/trpc/src/env.ts
  • apps/web/src/app/api/auth/desktop/github/route.ts
  • apps/web/src/app/api/auth/desktop/google/route.ts
  • apps/web/src/app/auth/desktop/success/components/LoadingFallback/index.ts
  • apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/index.ts
  • apps/admin/src/app/(dashboard)/users/deleted/components/DeletedUsersTable/DeletedUsersTable.tsx
  • apps/desktop/src/main/lib/auth/token-storage.ts
  • apps/desktop/src/main/lib/auth/auth-service.ts
  • apps/web/src/app/(auth)/sso-callback/page.tsx
  • apps/marketing/src/env.ts
  • apps/admin/src/app/(dashboard)/users/deleted/page.tsx
  • apps/api/src/app/api/auth/desktop/refresh/route.ts
  • apps/admin/src/env.ts
  • apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/DesktopAuthSuccess.tsx
  • apps/desktop/src/main/lib/auth/deep-link-handler.ts
  • apps/desktop/src/shared/auth.ts
  • apps/marketing/src/proxy.ts
  • packages/trpc/src/router/user/utils/sync-user-from-clerk.ts
  • apps/web/src/app/auth/desktop/success/components/DesktopAuthSuccess/index.ts
  • apps/api/src/app/api/auth/desktop/github/route.ts
  • apps/api/next.config.ts
  • apps/api/src/app/api/auth/desktop/google/route.ts
  • apps/api/src/app/api/auth/desktop/tokens.ts
  • apps/web/src/env.ts
  • apps/desktop/src/main/lib/auth/pkce.ts
🧰 Additional context used
📓 Path-based instructions (8)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
  • apps/desktop/src/lib/trpc/routers/index.ts
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/main/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use type safety and avoid any types unless absolutely necessary in TypeScript files

Files:

  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • packages/db/src/schema/index.ts
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
  • packages/trpc/src/router/integration/linear/linear.ts
  • apps/web/src/app/layout.tsx
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
  • apps/api/src/app/api/auth/desktop/connect/route.ts
  • packages/auth/src/client.ts
  • apps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsx
  • packages/auth/src/index.ts
  • apps/admin/src/trpc/react.tsx
  • packages/trpc/src/router/task/task.ts
  • apps/admin/src/proxy.ts
  • packages/trpc/src/router/analytics/analytics.ts
  • apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx
  • packages/auth/src/env.ts
  • apps/desktop/src/lib/trpc/routers/index.ts
  • apps/api/src/app/api/auth/[...all]/route.ts
  • apps/api/src/app/api/integrations/linear/connect/route.ts
  • apps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsx
  • packages/db/src/schema/auth.ts
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
  • packages/trpc/src/router/integration/integration.ts
  • apps/web/src/proxy.ts
  • packages/db/src/env.ts
  • apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx
  • apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/web/src/app/(auth)/layout.tsx
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/admin/src/app/layout.tsx
  • apps/api/src/app/api/electric/[...path]/route.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • apps/web/src/trpc/react.tsx
  • apps/web/src/app/(dashboard)/components/Header/Header.tsx
  • packages/trpc/src/trpc.ts
  • apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
  • apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/marketing/src/app/layout.tsx
  • apps/api/src/trpc/context.ts
  • apps/api/src/lib/auth.ts
  • apps/desktop/src/main/index.ts
  • apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
  • packages/trpc/src/router/integration/linear/utils.ts
  • packages/db/src/schema/relations.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
  • packages/trpc/src/router/admin/admin.ts
  • packages/trpc/src/router/user/user.ts
  • packages/db/src/schema/schema.ts
  • apps/api/src/proxy.ts
  • packages/trpc/src/router/organization/organization.ts
apps/**/src/**/**/[A-Z]*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Structure component folders with one component per file using format ComponentName/ComponentName.tsx with index.ts barrel export

Files:

  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
  • apps/admin/src/app/(dashboard)/users/components/UsersTable/UsersTable.tsx
  • apps/admin/src/app/(dashboard)/components/AppSidebar/components/NavUser/NavUser.tsx
  • apps/admin/src/app/(dashboard)/components/LeaderboardTable/LeaderboardTable.tsx
  • apps/admin/src/app/(dashboard)/components/AppSidebar/AppSidebar.tsx
  • apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/web/src/app/(dashboard)/components/Header/Header.tsx
  • apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx
  • apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx
apps/desktop/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Move Node.js functionality needed in Electron renderer to src/main/lib/ and communicate via IPC

Files:

  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
  • apps/desktop/src/lib/trpc/routers/index.ts
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/main/index.ts
apps/desktop/src/{shared/ipc-channels.ts,main/**/*ipcs.ts,renderer/**/*.tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Define all Electron IPC channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Files:

  • apps/desktop/src/renderer/screens/main/components/TasksView/components/OrganizationSwitcher/OrganizationSwitcher.tsx
  • apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx
  • apps/desktop/src/renderer/screens/main/components/AvatarDropdown/AvatarDropdown.tsx
packages/db/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations in the packages/db package

Files:

  • packages/db/src/schema/index.ts
  • packages/db/src/schema/auth.ts
  • packages/db/src/env.ts
  • packages/db/src/schema/relations.ts
  • packages/db/src/schema/schema.ts
packages/db/src/schema/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Create database migrations by changing Drizzle schema and running drizzle-kit generate --name="<sample_name_snake_case>" on a new Neon branch

Files:

  • packages/db/src/schema/index.ts
  • packages/db/src/schema/auth.ts
  • packages/db/src/schema/relations.ts
  • packages/db/src/schema/schema.ts
apps/desktop/src/main/index.ts

📄 CodeRabbit inference engine (AGENTS.md)

Load environment variables in src/main/index.ts with override: true before any other imports in the desktop app

Files:

  • apps/desktop/src/main/index.ts
🧠 Learnings (12)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/main/index.ts : Load environment variables in `src/main/index.ts` with `override: true` before any other imports in the desktop app

Applied to files:

  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
  • packages/auth/src/env.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC

Applied to files:

  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx
  • apps/admin/src/proxy.ts
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/desktop/src/main/lib/auth/index.ts
  • apps/desktop/src/main/index.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Use alias as defined in `tsconfig.json` when possible

Applied to files:

  • apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts
  • packages/db/package.json
  • packages/auth/tsconfig.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branch

Applied to files:

  • packages/db/src/schema/index.ts
  • packages/db/drizzle/0006_add_better_auth_tables.sql
  • packages/db/package.json
  • packages/db/drizzle/meta/0005_snapshot.json
  • packages/db/src/schema/auth.ts
  • packages/db/src/schema/schema.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package

Applied to files:

  • packages/auth/src/index.ts
  • packages/db/package.json
  • apps/admin/package.json
  • packages/db/src/schema/auth.ts
  • packages/trpc/src/router/integration/integration.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • packages/db/src/schema/relations.ts
  • packages/db/src/schema/schema.ts
  • packages/trpc/src/router/organization/organization.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/{utils,hooks,constants,stores,providers,config,*.stories.tsx} : Co-locate utilities, hooks, constants, config files, and stories next to the files that use them

Applied to files:

  • packages/db/package.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to **/*.{ts,tsx} : Use type safety and avoid `any` types unless absolutely necessary in TypeScript files

Applied to files:

  • packages/db/package.json
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/**/**/[A-Z]*.tsx : Structure component folders with one component per file using format `ComponentName/ComponentName.tsx` with `index.ts` barrel export

Applied to files:

  • packages/db/package.json
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`

Applied to files:

  • apps/admin/src/trpc/react.tsx
  • apps/desktop/src/lib/trpc/routers/auth/index.ts
  • apps/web/src/trpc/react.tsx
  • packages/trpc/src/trpc.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/(middleware|proxy).ts : In Next.js 16, never create `middleware.ts`. Always use `proxy.ts` instead for request interception

Applied to files:

  • apps/admin/src/proxy.ts
  • apps/api/src/app/api/auth/[...all]/route.ts
  • apps/web/src/proxy.ts
  • apps/api/src/proxy.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from `trpc/server/observable` instead of async generators, as the library explicitly checks `isObservable(result)` and throws an error otherwise

Applied to files:

  • apps/desktop/src/lib/trpc/routers/auth/index.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/web/src/app/auth/desktop/success/page.tsx
🧬 Code graph analysis (30)
apps/desktop/src/renderer/screens/main/components/SettingsView/AccountSettings/AccountSettings.tsx (1)
packages/ui/src/components/ui/avatar.tsx (1)
  • AvatarImage (53-53)
packages/trpc/src/router/integration/linear/linear.ts (1)
packages/trpc/src/router/integration/linear/utils.ts (2)
  • verifyOrgMembership (55-71)
  • verifyOrgAdmin (73-81)
apps/web/src/app/layout.tsx (4)
packages/ui/src/lib/utils.ts (1)
  • cn (4-6)
apps/admin/src/app/providers.tsx (1)
  • Providers (13-31)
apps/web/src/app/providers.tsx (1)
  • Providers (13-31)
packages/ui/src/components/ui/sonner.tsx (1)
  • Toaster (40-40)
apps/api/src/app/api/auth/desktop/connect/route.ts (3)
packages/auth/src/env.ts (1)
  • env (8-27)
apps/api/src/env.ts (1)
  • env (4-47)
packages/auth/src/index.ts (1)
  • auth (9-71)
packages/auth/src/index.ts (2)
packages/auth/src/env.ts (1)
  • env (8-27)
packages/db/src/schema/auth.ts (1)
  • authSchema (11-11)
apps/admin/src/proxy.ts (4)
apps/web/src/proxy.ts (1)
  • proxy (18-37)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/admin/src/env.ts (1)
  • env (5-44)
packages/shared/src/constants.ts (1)
  • COMPANY (12-24)
packages/auth/src/env.ts (4)
apps/api/src/proxy.ts (1)
  • config (46-53)
packages/db/src/env.ts (1)
  • env (9-23)
apps/api/src/env.ts (1)
  • env (4-47)
apps/marketing/src/env.ts (1)
  • env (5-36)
apps/desktop/src/lib/trpc/routers/index.ts (1)
apps/desktop/src/lib/trpc/routers/auth/index.ts (1)
  • createAuthRouter (11-87)
apps/api/src/app/api/auth/[...all]/route.ts (1)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/desktop/src/lib/trpc/routers/auth/index.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (1)
  • authService (110-110)
apps/desktop/src/main/lib/auth/index.ts (1)
  • authService (2-2)
packages/trpc/src/router/integration/integration.ts (1)
packages/db/src/schema/auth.ts (1)
  • members (99-116)
apps/web/src/proxy.ts (3)
apps/admin/src/proxy.ts (1)
  • proxy (17-37)
apps/api/src/proxy.ts (1)
  • proxy (22-44)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx (2)
packages/auth/src/client.ts (1)
  • authClient (6-9)
apps/web/src/env.ts (1)
  • env (5-46)
apps/admin/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (2)
apps/web/src/components/PostHogUserIdentifier/PostHogUserIdentifier.tsx (1)
  • PostHogUserIdentifier (7-22)
packages/auth/src/client.ts (1)
  • authClient (6-9)
apps/web/src/app/(auth)/layout.tsx (1)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/desktop/src/main/lib/auth/auth.ts (3)
apps/desktop/src/main/lib/app-environment.ts (1)
  • SUPERSET_HOME_DIR (5-5)
packages/shared/src/constants.ts (1)
  • PROTOCOL_SCHEMES (6-9)
apps/desktop/src/main/lib/auth/crypto-storage.ts (2)
  • decrypt (97-119)
  • encrypt (76-90)
apps/api/src/app/api/electric/[...path]/route.ts (3)
apps/api/src/app/api/integrations/linear/connect/route.ts (1)
  • GET (7-53)
apps/api/src/lib/auth.ts (1)
  • getSession (3-7)
packages/db/src/schema/auth.ts (1)
  • members (99-116)
packages/trpc/src/trpc.ts (2)
packages/auth/src/index.ts (1)
  • Session (73-73)
packages/shared/src/constants.ts (1)
  • COMPANY (12-24)
apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (2)
packages/auth/src/client.ts (1)
  • authClient (6-9)
apps/web/src/env.ts (1)
  • env (5-46)
apps/api/src/trpc/context.ts (3)
apps/api/src/lib/auth.ts (1)
  • getSession (3-7)
packages/trpc/src/trpc.ts (1)
  • createTRPCContext (11-11)
packages/trpc/src/index.ts (1)
  • createTRPCContext (9-9)
apps/api/src/lib/auth.ts (1)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/desktop/src/main/index.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (2)
  • parseAuthDeepLink (20-39)
  • authService (110-110)
apps/desktop/src/main/lib/auth/index.ts (2)
  • parseAuthDeepLink (2-2)
  • authService (2-2)
apps/marketing/src/app/components/CTAButtons/CTAButtons.tsx (1)
packages/auth/src/index.ts (1)
  • auth (9-71)
packages/trpc/src/router/integration/linear/utils.ts (1)
packages/db/src/schema/auth.ts (1)
  • members (99-116)
packages/db/src/schema/relations.ts (2)
packages/db/src/schema/auth.ts (6)
  • users (13-24)
  • sessions (26-44)
  • accounts (46-68)
  • members (99-116)
  • invitations (118-138)
  • organizations (86-97)
packages/db/src/schema/schema.ts (3)
  • tasks (58-129)
  • integrationConnections (135-171)
  • repositories (29-53)
apps/web/src/app/auth/desktop/success/page.tsx (2)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx (1)
  • DesktopRedirect (7-26)
packages/trpc/src/router/admin/admin.ts (2)
packages/trpc/src/router/admin/index.ts (1)
  • adminRouter (1-1)
packages/trpc/src/trpc.ts (1)
  • adminProcedure (44-53)
packages/trpc/src/router/user/user.ts (2)
packages/trpc/src/trpc.ts (1)
  • protectedProcedure (33-42)
packages/db/src/schema/auth.ts (1)
  • members (99-116)
apps/api/src/proxy.ts (2)
apps/admin/src/proxy.ts (1)
  • proxy (17-37)
apps/web/src/proxy.ts (1)
  • proxy (18-37)
packages/trpc/src/router/organization/organization.ts (1)
packages/db/src/schema/auth.ts (1)
  • members (99-116)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Deploy API
  • GitHub Check: Deploy Web
  • GitHub Check: Deploy Admin
  • GitHub Check: Deploy Docs
  • GitHub Check: Build

Comment thread apps/api/src/app/api/auth/desktop/connect/route.ts
Comment on lines +29 to +33
if (!redirectUrl) {
return new Response(`Failed to initiate OAuth: ${JSON.stringify(result)}`, {
status: 500,
});
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid leaking internal details in error responses.

JSON.stringify(result) may expose internal auth library state or headers to clients. Use a generic error message instead.

🔎 Proposed fix
 	if (!redirectUrl) {
-		return new Response(`Failed to initiate OAuth: ${JSON.stringify(result)}`, {
+		return new Response("Failed to initiate OAuth flow", {
 			status: 500,
 		});
 	}
🤖 Prompt for AI Agents
In apps/api/src/app/api/auth/desktop/connect/route.ts around lines 29 to 33, the
error response currently returns JSON.stringify(result) which can leak internal
auth library state; change the response body to a generic message like "Failed
to initiate OAuth" and return the 500 status, and move any detailed information
about result into a server-side log (e.g., processLogger.error or console.error)
instead of sending it to the client.

Comment thread apps/web/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
Comment on lines 16 to 30
const signUpWithGoogle = async () => {
if (!isLoaded) return;

setIsLoadingGoogle(true);
setError(null);

try {
await signUp.authenticateWithRedirect({
strategy: "oauth_google",
redirectUrl: "/sso-callback",
redirectUrlComplete: "/",
await authClient.signIn.social({
provider: "google",
callbackURL: env.NEXT_PUBLIC_WEB_URL,
});
} catch (err) {
console.error("Sign up failed:", err);
setError("Failed to sign up. Please try again.");
} finally {
setIsLoadingGoogle(false);
}
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Find the auth client implementation
rg -n "signIn.*social" --type ts --type tsx -A 10

Repository: superset-sh/superset

Length of output: 184


🏁 Script executed:

# Also check the page.tsx file to understand the full context and check for type safety issues
head -50 apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsx 2>/dev/null || find . -path "**/sign-up/**/page.tsx" -type f

Repository: superset-sh/superset

Length of output: 121


🏁 Script executed:

# Search for authClient declaration and type
rg -n "authClient\s*=" -B 5 -A 2 apps/web/src/app/\(auth\)/sign-up/

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

cat -n ./apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsx | head -60

Repository: superset-sh/superset

Length of output: 148


🏁 Script executed:

# Search for authClient import and definition
rg -n "authClient|import.*auth" ./apps/web/src/app/\(auth\)/sign-up/\[\[...\]\]/page.tsx

Repository: superset-sh/superset

Length of output: 154


🏁 Script executed:

# Find auth client implementation in the codebase
find ./packages -name "*.ts" -o -name "*.tsx" | head -20 | xargs rg -l "signIn.*social" 2>/dev/null | head -5

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

# Use git ls-files to find the sign-up page
git ls-files | grep -i "sign-up.*page.tsx"

Repository: superset-sh/superset

Length of output: 119


🏁 Script executed:

# Use fd to find the file by pattern
fd "page.tsx" --path "*sign-up*"

Repository: superset-sh/superset

Length of output: 279


🏁 Script executed:

# Check if the repository structure matches expectations
ls -la apps/web/src/app/ 2>/dev/null | head -20

Repository: superset-sh/superset

Length of output: 667


🏁 Script executed:

# Use Python to read the file with proper path handling
python3 << 'EOF'
import os

file_path = "apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx"
try:
    with open(file_path, 'r') as f:
        lines = f.readlines()
        for i, line in enumerate(lines[:80], 1):
            print(f"{i:3}: {line}", end='')
except Exception as e:
    print(f"Error: {e}")
EOF

Repository: superset-sh/superset

Length of output: 150


🏁 Script executed:

# Search for authClient definition and setup
rg "authClient" apps/web/src -B 2 -A 2 --max-count 10

Repository: superset-sh/superset

Length of output: 4746


🏁 Script executed:

# Look for type definitions and any types in the file
rg "any" apps/web/src/app/ --type ts --type tsx

Repository: superset-sh/superset

Length of output: 91


🏁 Script executed:

# Find the auth client implementation
find . -path "*/auth/*" -name "client*" -type f

Repository: superset-sh/superset

Length of output: 93


🏁 Script executed:

# Look for the auth package structure
ls -la packages/auth/ 2>/dev/null || find . -path "*/packages/auth*" -type d

Repository: superset-sh/superset

Length of output: 347


🏁 Script executed:

# Search for signIn definition and social method
rg "signIn.*=" packages/ -A 5 --max-count 5

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

# Check for any types in the entire codebase
rg ": any" apps/web/src/app/\(auth\)/ --type ts -A 1

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

# Read the auth client implementation
cat -n ./packages/auth/src/client.ts

Repository: superset-sh/superset

Length of output: 387


🏁 Script executed:

# Check the auth package src directory
ls -la ./packages/auth/src/

Repository: superset-sh/superset

Length of output: 348


🏁 Script executed:

# Search for type definitions in auth package
find ./packages/auth -name "*.ts" -o -name "*.d.ts" | xargs cat

Repository: superset-sh/superset

Length of output: 2710


🏁 Script executed:

# Read the sign-up page file content
python3 << 'EOF'
import os

file_path = "apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx"
try:
    with open(file_path, 'r') as f:
        content = f.read()
        lines = content.split('\n')
        for i, line in enumerate(lines[:80], 1):
            print(f"{i:3}: {line}")
except Exception as e:
    print(f"Error: {e}")
EOF

Repository: superset-sh/superset

Length of output: 2699


🏁 Script executed:

# Look for better-auth types and signIn method
rg "better-auth" packages/ --type ts -A 3 | head -50

Repository: superset-sh/superset

Length of output: 772


🏁 Script executed:

# Search for any unused type annotations or error handling patterns
rg "catch.*err|any\b" apps/web/src/app/\(auth\)/ -B 2 -A 2

Repository: superset-sh/superset

Length of output: 1840


🏁 Script executed:

# Check if there's an eslint config for catch clause typing
find . -name "eslint*" -o -name ".eslintrc*" | head -5

Repository: superset-sh/superset

Length of output: 46


🏁 Script executed:

# Check tsconfig for strict settings
cat ./apps/web/tsconfig.json

Repository: superset-sh/superset

Length of output: 285


🏁 Script executed:

# Check the extended TypeScript config
find . -path "*typescript*" -name "*.json" | head -10

Repository: superset-sh/superset

Length of output: 237


🏁 Script executed:

# Look at the base TypeScript config
cat ./packages/typescript/next.json

Repository: superset-sh/superset

Length of output: 127


🏁 Script executed:

# Read the base TypeScript config
cat ./tooling/typescript/next.json

Repository: superset-sh/superset

Length of output: 271


🏁 Script executed:

# Also check the base config
cat ./tooling/typescript/base.json

Repository: superset-sh/superset

Length of output: 685


Add type annotation to the error parameter in catch block.

The error parameter err (line 25) lacks a type annotation, violating TypeScript strict mode and the type safety requirement. Under the strict configuration, use catch (err: unknown) to properly type the error, or narrow it to a more specific type like catch (err: Error) if you need to access error properties.

Note: The loading state behavior is correct—it remains true on successful OAuth redirect since the browser will navigate away; only reset it on error.

🤖 Prompt for AI Agents
In apps/web/src/app/(auth)/sign-up/[[...sign-up]]/page.tsx around lines 16 to
30, the catch block uses an untyped error parameter `err` which fails TypeScript
strict-mode; change the signature to `catch (err: unknown)` (or `catch (err:
Error)` if you only expect Error instances) and update the error
handling/logging to narrow the type before accessing properties (e.g., use `if
(err instanceof Error) console.error("Sign up failed:", err.message, err)` else
console.error("Sign up failed:", err) ), then keep setting the user-facing error
and resetting isLoadingGoogle as before.

Comment thread apps/web/src/app/layout.tsx
Comment thread packages/db/src/env.ts
*/
emptyStringAsUndefined: true,
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
skipValidation: true,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Unconditionally disabling environment validation removes critical safety checks.

Hardcoding skipValidation: true bypasses all zod schema validation for DATABASE_URL and DATABASE_URL_UNPOOLED. Missing or malformed database URLs will no longer be caught at startup, potentially causing runtime failures when database connections are attempted.

🔎 Proposed fix: restore conditional validation
-	skipValidation: true,
+	skipValidation: !!process.env.SKIP_ENV_VALIDATION,

This allows validation to be skipped only when explicitly needed (e.g., during certain build processes) while maintaining safety checks in production and development.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
skipValidation: true,
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
🤖 Prompt for AI Agents
In packages/db/src/env.ts around line 22, remove the unconditional
skipValidation: true and instead make skipValidation conditional (for example:
skipValidation: process.env.SKIP_ENV_VALIDATION === 'true' or only when NODE_ENV
=== 'test' or a dedicated build flag); this preserves zod validation for
DATABASE_URL and DATABASE_URL_UNPOOLED in normal dev/prod runs while allowing
callers to explicitly opt out when required.

Comment on lines +26 to +44
export const sessions = authSchema.table(
"sessions",
{
id: uuid("id").primaryKey().defaultRandom(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => new Date())
.notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
activeOrganizationId: uuid("active_organization_id"),
},
(table) => [index("sessions_user_id_idx").on(table.userId)],
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add foreign key constraint for activeOrganizationId.

Line 41 defines activeOrganizationId but doesn't add a foreign key reference to organizations.id. This creates referential integrity risk - sessions could reference non-existent organizations.

🔎 Proposed fix
 activeOrganizationId: uuid("active_organization_id")
+  .references(() => organizations.id, { onDelete: "set null" }),

This aligns with the same issue found in the migration SQL file (packages/db/drizzle/0006_add_better_auth_tables.sql).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const sessions = authSchema.table(
"sessions",
{
id: uuid("id").primaryKey().defaultRandom(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => new Date())
.notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
activeOrganizationId: uuid("active_organization_id"),
},
(table) => [index("sessions_user_id_idx").on(table.userId)],
);
export const sessions = authSchema.table(
"sessions",
{
id: uuid("id").primaryKey().defaultRandom(),
expiresAt: timestamp("expires_at").notNull(),
token: text("token").notNull().unique(),
createdAt: timestamp("created_at").defaultNow().notNull(),
updatedAt: timestamp("updated_at")
.$onUpdate(() => new Date())
.notNull(),
ipAddress: text("ip_address"),
userAgent: text("user_agent"),
userId: uuid("user_id")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
activeOrganizationId: uuid("active_organization_id")
.references(() => organizations.id, { onDelete: "set null" }),
},
(table) => [index("sessions_user_id_idx").on(table.userId)],
);
🤖 Prompt for AI Agents
In packages/db/src/schema/auth.ts around lines 26 to 44, the
sessions.activeOrganizationId column is defined but lacks a foreign key
reference to organizations.id; update the column definition to add
.references(() => organizations.id, { onDelete: "SET NULL" }) (or another
desired delete rule like "CASCADE") so sessions cannot reference non-existent
organizations and ensure this change matches the migration SQL in
packages/db/drizzle/0006_add_better_auth_tables.sql.

Comment thread packages/db/src/schema/auth.ts
Comment thread packages/db/src/schema/auth.ts
Comment thread packages/trpc/src/router/integration/integration.ts Outdated
@saddlepaddle
Copy link
Copy Markdown
Collaborator Author

Code review

Found 2 issues:

  1. Loss of Soft Delete Functionality - The PR removes deletedAt field filtering from organization queries and changes user deletion from soft delete to hard delete. The previous implementation had separate listActiveUsers/listDeletedUsers endpoints with a restoreUser mutation for recovery. Hard deletion is irreversible and prevents user recovery for compliance or user requests.

deleteUser: adminProcedure
.input(z.object({ userId: z.string() }))
.mutation(async ({ input }) => {
// Delete user - Better Auth handles cascading session cleanup
await db.delete(users).where(eq(users.id, input.userId));
return { success: true };
}),

  1. Loss of Offline-First Functionality for Desktop - The previous implementation (PR fix(desktop): offline sign out behavior fixed #512) had a three-way result system ("success" | "invalid" | "network_error") that preserved sessions during network errors for offline-first usage. The new Better Auth implementation has no token refresh mechanism and no distinction between network errors (should preserve session) vs invalid tokens (should clear session). This breaks the desktop app's offline-first capability.

}
}
class AuthService extends EventEmitter {
private token: string | null = null;
async initialize(): Promise<void> {
try {
this.token = decrypt(await fs.readFile(TOKEN_FILE));
console.log("[auth] Session restored");
} catch {
this.token = null;
}
}
getState() {
return { isSignedIn: !!this.token };
}

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/trpc/src/router/organization/organization.ts (3)

9-48: Missing soft-delete filtering in organization queries.

Based on the PR objectives, the reviewer (saddlepaddle) flagged the loss of soft-delete functionality, noting that "the PR removes deletedAt filtering from organization queries." The all, byId, and bySlug queries now return all organizations without filtering out soft-deleted records.

If soft-delete semantics need to be preserved (as requested by the reviewer), these queries should filter by deletedAt IS NULL or equivalent. However, the current auth schema (from relevant code snippets) shows that the organizations table lacks a deletedAt column entirely.

Based on PR objectives indicating reviewer concerns about soft-delete functionality.


79-95: Restore UUID validation for organization ID.

Line 82 weakens type safety by changing the id validation from z.string().uuid() to z.string(). The organization ID is a UUID field in the database schema (as confirmed by the relevant code snippets showing id: uuid("id")), and the input validation should enforce this constraint.

🔎 Restore UUID validation
 	update: protectedProcedure
 		.input(
 			z.object({
-				id: z.string(),
+				id: z.string().uuid(),
 				name: z.string().min(1).optional(),
 				logo: z.string().url().optional(),
 			}),
 		)

As per coding guidelines: Use type safety and avoid weakening type constraints in TypeScript files.


97-102: Hard delete prevents organization recovery.

Based on the PR objectives, the reviewer (saddlepaddle) flagged that "the PR converts user deletion from soft delete to hard delete" and noted that "previously there was a restoreUser mutation to recover deleted users; hard deletes prevent recovery and may conflict with compliance or user-requested restores."

The current implementation performs a hard delete on the organizations table, which:

  1. Permanently removes the organization and cascades to delete all associated members (due to foreign key constraints)
  2. Prevents any possibility of recovery or restoration
  3. May conflict with compliance requirements or audit trails

If soft-delete semantics need to be preserved (as requested by the reviewer), consider adding a deletedAt column to the schema and implementing soft-delete logic.

Based on PR objectives indicating reviewer concerns about soft-delete functionality.

♻️ Duplicate comments (2)
apps/api/src/app/api/auth/desktop/connect/route.ts (2)

6-13: Past issue not addressed: Provider validation is still missing.

The unsafe type assertion on line 8 remains unfixed. Without explicit validation that provider is either "google" or "github", malicious requests can pass arbitrary strings to signInSocial, potentially causing security issues or unexpected behavior.

🔎 Recommended fix (as previously suggested)
 export async function GET(request: Request) {
 	const url = new URL(request.url);
-	const provider = url.searchParams.get("provider") as "google" | "github";
+	const provider = url.searchParams.get("provider");
 	const state = url.searchParams.get("state");
 
-	if (!provider || !state) {
+	if (!provider || !state) {
 		return new Response("Missing provider or state", { status: 400 });
 	}
+
+	if (provider !== "google" && provider !== "github") {
+		return new Response("Invalid provider", { status: 400 });
+	}

29-33: Past issue not addressed: Error response still leaks internal details.

Line 30 still uses JSON.stringify(body) to expose internal auth library state to clients. This should be replaced with a generic error message, with detailed information logged server-side instead.

🔎 Recommended fix (as previously suggested)
 	if (!body.url) {
-		return new Response(`Failed to initiate OAuth: ${JSON.stringify(body)}`, {
+		console.error("OAuth initiation failed:", body);
+		return new Response("Failed to initiate OAuth flow", {
 			status: 500,
 		});
 	}
🧹 Nitpick comments (6)
apps/api/src/app/api/auth/desktop/connect/route.ts (1)

18-27: Add error handling for the auth API call.

The auth.api.signInSocial call (lines 18-24) lacks try-catch error handling. If the auth library throws an exception (network error, configuration issue, etc.), the entire route will crash with an unhandled error instead of returning a proper error response.

🔎 Proposed fix
+	let result;
+	try {
-	const result = await auth.api.signInSocial({
+		result = await auth.api.signInSocial({
 		body: {
 			provider,
 			callbackURL: successUrl.toString(),
 		},
 		asResponse: true,
 	});
+	} catch (error) {
+		console.error("Failed to initiate OAuth:", error);
+		return new Response("Failed to initiate OAuth flow", { status: 500 });
+	}
 
 	const cookies = result.headers.get("set-cookie");
packages/trpc/src/router/organization/organization.ts (1)

58-77: Consider removing unnecessary conditional check.

The conditional check on Line 68 is redundant. The .returning() method will either return the inserted record or the .insert() operation will throw an error. The organization value cannot be undefined/null at this point without an exception being raised first.

🔎 Suggested simplification
 		.mutation(async ({ ctx, input }) => {
 			const [organization] = await db
 				.insert(organizations)
 				.values({
 					name: input.name,
 					slug: input.slug,
 					logo: input.logo,
 				})
 				.returning();
 
-			if (organization) {
-				await db.insert(members).values({
-					organizationId: organization.id,
-					userId: ctx.session.user.id,
-					role: "owner",
-				});
-			}
+			await db.insert(members).values({
+				organizationId: organization.id,
+				userId: ctx.session.user.id,
+				role: "owner",
+			});
 
 			return organization;
 		}),
apps/api/src/proxy.ts (1)

5-9: Type inference produces a mixed array before filtering.

The conditional expression on line 8 produces string | false, so allowedOrigins is typed as (string | false)[] before the .filter(Boolean) call. While this works at runtime, TypeScript's type narrowing with filter(Boolean) doesn't automatically narrow to string[].

Consider adding explicit type assertion for clarity:

Suggested improvement
 const allowedOrigins = [
 	env.NEXT_PUBLIC_WEB_URL,
 	env.NEXT_PUBLIC_ADMIN_URL,
 	env.NODE_ENV === "development" && "http://localhost:5927",
-].filter(Boolean);
+].filter(Boolean) as string[];
apps/web/src/proxy.ts (1)

23-28: Minor redundancy in route checking.

The check pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up") duplicates logic that isPublicRoute already handles. Consider reusing the helper for consistency:

Suggested simplification
 	if (
 		session &&
-		(pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up"))
+		(pathname.startsWith("/sign-in") || pathname.startsWith("/sign-up"))
 	) {
 		return NextResponse.redirect(new URL("/", req.url));
 	}

Alternatively, extract sign-in/sign-up paths into a constant shared with publicRoutes to avoid drift.

apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql (1)

3-9: Consider adding foreign key constraints for referential integrity.

The members table references organization_id and user_id but doesn't define foreign key constraints to auth.organizations and auth.users. This could allow orphaned records and referential integrity violations.

🔎 Suggested foreign key additions
 CREATE TABLE "auth"."members" (
 	"id" uuid PRIMARY KEY NOT NULL,
 	"organization_id" uuid NOT NULL,
 	"user_id" uuid NOT NULL,
 	"role" text NOT NULL,
-	"created_at" timestamp NOT NULL
+	"created_at" timestamp NOT NULL,
+	CONSTRAINT "members_organization_fk" FOREIGN KEY ("organization_id") REFERENCES "auth"."organizations"("id") ON DELETE CASCADE,
+	CONSTRAINT "members_user_fk" FOREIGN KEY ("user_id") REFERENCES "auth"."users"("id") ON DELETE CASCADE
 );
apps/desktop/src/renderer/lib/pglite/schema/auth.ts (1)

42-55: Add foreign key constraints for data integrity.

The members table has organization_id and user_id fields but no foreign key constraints. While Drizzle will work without them, adding constraints prevents orphaned records and ensures referential integrity, especially important for synced data.

🔎 Recommended foreign key additions
 export const members = authSchema.table(
 	"members",
 	{
 		id: uuid("id").primaryKey(),
-		organization_id: uuid("organization_id").notNull(),
-		user_id: uuid("user_id").notNull(),
+		organization_id: uuid("organization_id")
+			.notNull()
+			.references(() => organizations.id, { onDelete: "cascade" }),
+		user_id: uuid("user_id")
+			.notNull()
+			.references(() => users.id, { onDelete: "cascade" }),
 		role: text("role").notNull(),
 		created_at: timestamp("created_at").notNull(),
 	},

Note: Adding foreign keys requires regenerating the migration with drizzle-kit generate.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fff4289 and 8378043.

📒 Files selected for processing (23)
  • apps/admin/src/proxy.ts
  • apps/api/src/app/api/auth/desktop/connect/route.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • apps/api/src/proxy.ts
  • apps/desktop/drizzle.config.ts
  • apps/desktop/src/main/lib/api-client.ts
  • apps/desktop/src/main/lib/auth/auth.ts
  • apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql
  • apps/desktop/src/renderer/lib/pglite/migrations/index.ts
  • apps/desktop/src/renderer/lib/pglite/migrations/meta/0001_snapshot.json
  • apps/desktop/src/renderer/lib/pglite/migrations/meta/_journal.json
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
  • apps/desktop/src/renderer/lib/pglite/schema/index.ts
  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/src/renderer/lib/pglite/sync.ts
  • apps/marketing/src/proxy.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/web/src/proxy.ts
  • packages/db/drizzle/0007_add_created_at_default.sql
  • packages/db/drizzle/meta/0007_snapshot.json
  • packages/db/drizzle/meta/_journal.json
  • packages/db/src/schema/auth.ts
  • packages/trpc/src/router/organization/organization.ts
💤 Files with no reviewable changes (1)
  • apps/marketing/src/proxy.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/desktop/src/renderer/lib/pglite/migrations/meta/0001_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/db/src/schema/auth.ts
  • apps/desktop/src/main/lib/auth/auth.ts
🧰 Additional context used
📓 Path-based instructions (3)
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/drizzle.config.ts
  • apps/desktop/src/renderer/lib/pglite/schema/index.ts
  • apps/desktop/src/renderer/lib/pglite/sync.ts
  • apps/desktop/src/renderer/lib/pglite/migrations/index.ts
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
  • apps/desktop/src/main/lib/api-client.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use type safety and avoid any types unless absolutely necessary in TypeScript files

Files:

  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/drizzle.config.ts
  • apps/desktop/src/renderer/lib/pglite/schema/index.ts
  • apps/api/src/app/api/auth/desktop/connect/route.ts
  • apps/desktop/src/renderer/lib/pglite/sync.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/desktop/src/renderer/lib/pglite/migrations/index.ts
  • apps/api/src/proxy.ts
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
  • packages/trpc/src/router/organization/organization.ts
  • apps/admin/src/proxy.ts
  • apps/desktop/src/main/lib/api-client.ts
  • apps/web/src/proxy.ts
apps/desktop/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Move Node.js functionality needed in Electron renderer to src/main/lib/ and communicate via IPC

Files:

  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/src/renderer/lib/pglite/schema/index.ts
  • apps/desktop/src/renderer/lib/pglite/sync.ts
  • apps/desktop/src/renderer/lib/pglite/migrations/index.ts
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
  • apps/desktop/src/main/lib/api-client.ts
🧠 Learnings (4)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branch

Applied to files:

  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/drizzle.config.ts
  • packages/db/drizzle/meta/0007_snapshot.json
  • apps/desktop/src/renderer/lib/pglite/migrations/index.ts
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package

Applied to files:

  • apps/desktop/src/renderer/lib/pglite/schema/schema.ts
  • apps/desktop/drizzle.config.ts
  • apps/api/src/app/api/electric/[...path]/utils.ts
  • apps/desktop/src/renderer/lib/pglite/schema/auth.ts
  • packages/trpc/src/router/organization/organization.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC

Applied to files:

  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/admin/src/proxy.ts
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/**/src/(middleware|proxy).ts : In Next.js 16, never create `middleware.ts`. Always use `proxy.ts` instead for request interception

Applied to files:

  • apps/api/src/proxy.ts
  • apps/admin/src/proxy.ts
  • apps/web/src/proxy.ts
🧬 Code graph analysis (8)
apps/api/src/app/api/auth/desktop/connect/route.ts (3)
apps/api/src/env.ts (1)
  • env (4-47)
packages/auth/src/env.ts (1)
  • env (8-27)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/api/src/app/api/electric/[...path]/utils.ts (2)
apps/desktop/src/renderer/lib/pglite/schema/auth.ts (2)
  • members (42-55)
  • organizations (26-37)
packages/db/src/schema/auth.ts (2)
  • members (99-116)
  • organizations (86-97)
apps/web/src/app/auth/desktop/success/page.tsx (4)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/desktop/src/main/lib/api-client.ts (1)
  • headers (19-27)
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/DesktopRedirect.tsx (1)
  • DesktopRedirect (7-26)
apps/web/src/app/auth/desktop/success/components/DesktopRedirect/index.ts (1)
  • DesktopRedirect (1-1)
apps/api/src/proxy.ts (2)
apps/admin/src/proxy.ts (2)
  • proxy (9-23)
  • config (25-31)
apps/web/src/proxy.ts (2)
  • proxy (16-35)
  • config (37-43)
packages/trpc/src/router/organization/organization.ts (2)
apps/desktop/src/renderer/lib/pglite/schema/auth.ts (2)
  • organizations (26-37)
  • members (42-55)
packages/db/src/schema/auth.ts (2)
  • organizations (86-97)
  • members (99-116)
apps/admin/src/proxy.ts (3)
apps/web/src/proxy.ts (1)
  • proxy (16-35)
apps/desktop/src/main/lib/api-client.ts (1)
  • headers (19-27)
packages/shared/src/constants.ts (1)
  • COMPANY (12-24)
apps/desktop/src/main/lib/api-client.ts (2)
apps/desktop/src/main/lib/auth/auth.ts (1)
  • authService (148-148)
apps/desktop/src/main/lib/auth/index.ts (1)
  • authService (2-2)
apps/web/src/proxy.ts (4)
apps/admin/src/proxy.ts (2)
  • proxy (9-23)
  • config (25-31)
apps/api/src/proxy.ts (2)
  • proxy (22-37)
  • config (39-46)
packages/auth/src/index.ts (1)
  • auth (9-71)
apps/desktop/src/main/lib/api-client.ts (1)
  • headers (19-27)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Deploy API
  • GitHub Check: Deploy Marketing
  • GitHub Check: Deploy Docs
  • GitHub Check: Deploy Admin
  • GitHub Check: Deploy Web
  • GitHub Check: Build
🔇 Additional comments (32)
apps/desktop/drizzle.config.ts (1)

4-7: LGTM! Clean separation of auth schema.

The migration from a single schema file to an array supporting both auth.ts and schema.ts aligns well with the PR's Better Auth migration objectives. This separation improves modularity and maintainability by keeping authentication-related tables isolated from the main schema.

apps/api/src/app/api/electric/[...path]/utils.ts (4)

7-11: LGTM!

The AllowedTable type correctly reflects the new auth.* schema naming convention introduced with the Better Auth migration. The type is well-defined with proper string literals.


18-28: LGTM!

The build helper function is well-typed and its logic is preserved from the previous implementation. The approach of extracting the WHERE fragment via regex is functional for the expected SQL format.


38-42: LGTM!

The case labels correctly use the new auth.* prefixed table names, and the column references (members.organizationId, organizations.id) align with the schema definitions in packages/db/src/schema/auth.ts.


44-62: LGTM!

The users case correctly:

  1. Queries db.query.members (renamed from organizationMembers)
  2. Uses the correct camelCase column reference userId that matches the server schema
  3. Deduplicates user IDs using Set
  4. Builds an IN clause for filtering users belonging to the organization

The logic is sound and the migration to the new auth.users table is properly handled.

packages/db/drizzle/0007_add_created_at_default.sql (1)

1-2: LGTM! Well-structured migration adding timestamp defaults.

This migration correctly adds DEFAULT now() to the created_at columns for auth.members and auth.organizations, ensuring consistency with other auth tables. This is a non-breaking change that aligns with the PR's goal to rely on database-generated defaults rather than manual timestamp generation.

packages/db/drizzle/meta/_journal.json (1)

39-60: LGTM! Migration journal correctly updated.

The three new migration entries are properly formatted and sequentially ordered, tracking the Better Auth migration (0005-0007). The journal structure follows Drizzle conventions.

packages/db/drizzle/meta/0007_snapshot.json (1)

1-1525: LGTM! Schema snapshot correctly reflects migration 0007.

The snapshot accurately captures the database state after adding created_at defaults to auth.members and auth.organizations. The schema is well-structured with:

  • Appropriate foreign key cascade behaviors
  • Proper indexing on key columns
  • Consistent use of UUIDs with database-generated defaults
  • All auth tables now having created_at defaults

The snapshot aligns with the Better Auth migration objectives.

apps/desktop/src/main/lib/api-client.ts (3)

7-13: Clear documentation of offline-first behavior.

The documentation effectively explains the distinction between auth errors and network errors, which directly addresses the reviewer's concern about preserving offline-first functionality.


28-44: Excellent offline-first error handling implementation.

This fetch override correctly distinguishes between authentication failures and network errors, directly addressing the reviewer's concern:

  • 401/403 responses: Properly triggers sign-out to clear invalid/revoked tokens
  • Network errors: Preserves the session by catching and re-throwing, allowing offline work to continue

The implementation properly awaits both the fetch call and the sign-out operation, avoiding race conditions.


19-27: The code is correct. The getAccessToken() method is synchronous and returns string | null directly (not a Promise), as shown in the auth service implementation at line 84-89 of apps/desktop/src/main/lib/auth/auth.ts. Removing the await is appropriate and does not break authentication.

Likely an incorrect or invalid review comment.

apps/api/src/app/api/auth/desktop/connect/route.ts (1)

1-4: LGTM!

Imports are appropriate and necessary for the desktop OAuth connect functionality.

packages/trpc/src/router/organization/organization.ts (4)

2-2: LGTM: Import aligns with new auth schema structure.

The switch from organizationMembers and users to members and organizations correctly reflects the new auth schema introduced in this PR.


50-57: LGTM: Input schema correctly defines organization fields.

The logo field appropriately replaces the previous avatarUrl/githubOrg fields with proper URL validation.


104-121: LGTM: Member addition correctly uses new schema.

The mutation properly inserts into the members table with the appropriate role ("member") and references the new auth schema structure.


123-140: LGTM: Member removal correctly uses new schema.

The mutation properly deletes from the members table with appropriate composite key matching using organizationId and userId.

apps/api/src/proxy.ts (2)

22-37: LGTM!

The synchronous proxy function correctly handles CORS preflight requests and attaches CORS headers to all responses. The API authentication is appropriately delegated to the TRPC context layer rather than being handled at the proxy level.


39-46: LGTM!

The matcher configuration correctly excludes Next.js internals, static assets, and telemetry routes while ensuring the proxy runs for all API and TRPC routes. The pattern is consistent with the other proxy files in the codebase.

apps/web/src/proxy.ts (3)

16-19: LGTM!

The session retrieval pattern using auth.api.getSession with await headers() is consistent with the admin proxy implementation and correctly integrates with the new Better Auth system.


37-43: LGTM!

The matcher configuration is consistent with the other proxy files in the monorepo, correctly excluding internal routes and static assets.


5-14: No changes needed - the review comment is based on an incorrect assumption about the application architecture.

The web app and API app are separate Next.js applications. Better Auth callbacks are handled by the API app at apps/api/src/app/api/auth/[...all]/route.ts, not by the web app. The web app makes HTTP requests to the separate API app (via NEXT_PUBLIC_API_URL), so Better Auth callback routes never pass through the web app's proxy middleware. The current publicRoutes configuration is correct for the web app's actual routing needs.

Likely an incorrect or invalid review comment.

apps/admin/src/proxy.ts (2)

9-23: LGTM!

The admin proxy correctly enforces authentication and email domain restrictions. The pattern is consistent with the web proxy, and using COMPANY.EMAIL_DOMAIN from shared constants ensures the domain check stays synchronized across the codebase.


25-31: LGTM!

The matcher configuration is consistent with the API and web proxy files.

apps/desktop/src/renderer/lib/pglite/migrations/meta/_journal.json (1)

12-18: LGTM!

The migration journal entry follows the correct drizzle-kit format with proper versioning and timestamp.

apps/desktop/src/renderer/lib/pglite/migrations/index.ts (2)

15-15: LGTM!

The migration import follows the correct pattern for browser-compatible bundled migrations using Vite's ?raw suffix.


25-25: LGTM!

The migration entry is correctly added with matching timestamp from _journal.json.

apps/desktop/src/renderer/lib/pglite/schema/index.ts (1)

1-2: LGTM!

The re-export correctly exposes the new auth schema module alongside existing schema exports.

apps/desktop/src/renderer/lib/pglite/schema/schema.ts (1)

31-32: LGTM!

The comment update correctly reflects that auth-related tables have been moved to the auth schema, leaving only the tasks table here.

apps/desktop/src/renderer/lib/pglite/schema/auth.ts (1)

1-58: Schema misalignment requires fixes across multiple areas.

The desktop schema diverges significantly from the server schema:

  1. Critical type mismatch: email_verified is text on desktop but boolean on server (defaults to false)
  2. Incomplete schema: Desktop is missing 4 tables—sessions, accounts, verifications, invitations
  3. Missing defaults and constraints:
    • UUIDs lack .defaultRandom()
    • Timestamps lack .defaultNow() or .$onUpdate()
    • members.role missing default value "member"
  4. Index type mismatch: organizations.slug uses index() instead of uniqueIndex()
  5. Missing foreign key relationships: No cascade delete references in members, sessions, accounts, invitations
  6. Property naming inconsistency: Desktop uses snake_case (email_verified, created_at) while server uses camelCase (emailVerified, createdAt)

Update the desktop schema to match the server implementation exactly.

⛔ Skipped due to learnings
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/src/schema/**/*.ts : Create database migrations by changing Drizzle schema and running `drizzle-kit generate --name="<sample_name_snake_case>"` on a new Neon branch
apps/web/src/app/auth/desktop/success/page.tsx (3)

6-12: LGTM! Correct Next.js 15 async searchParams pattern.

The function signature properly types searchParams as a Promise and correctly awaits it before destructuring. This aligns with Next.js 15 conventions.


38-42: The 5-minute safety buffer is correctly implemented in the desktop app's token validation logic (apps/desktop/src/main/lib/auth/auth.ts, lines 70-75 in the isExpired() method), not in the web app's URL generation. The web app correctly passes the actual expiresAt value; the desktop app applies the buffer during token validation when it receives the deep link. This is the intended design and the PR objectives are satisfied.

Likely an incorrect or invalid review comment.


38-39: Add explicit type annotation for the session variable and verify the property access pattern.

The session variable from auth.api.getSession() lacks an explicit type annotation. While a null check exists on line 27, the code accesses session.session.token and session.session.expiresAt, which differs from the pattern used elsewhere in the codebase (session.user.*). Ensure this property structure matches Better Auth's return type, or add explicit typing: const session: Session | null = await auth.api.getSession(...).

Comment thread apps/admin/src/proxy.ts
Comment on lines +14 to 20
if (!session?.user) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}

const user = await db.query.users.findFirst({
where: eq(users.clerkId, clerkId),
});

if (!user?.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add null check for user email.

If session.user.email is null or undefined (possible with some OAuth providers that don't return email), calling .endsWith() will throw a runtime error. Consider adding a defensive check:

Suggested fix
 	if (!session?.user) {
 		return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
 	}

-	if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
+	if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) {
 		return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!session?.user) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
const user = await db.query.users.findFirst({
where: eq(users.clerkId, clerkId),
});
if (!user?.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
if (!session.user.email.endsWith(COMPANY.EMAIL_DOMAIN)) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
if (!session?.user) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) {
return NextResponse.redirect(new URL(env.NEXT_PUBLIC_WEB_URL));
}
🤖 Prompt for AI Agents
In apps/admin/src/proxy.ts around lines 14 to 20, the code calls
session.user.email.endsWith(...) without ensuring email is a non-empty string;
when email is null/undefined this throws. Add a defensive check that verifies
typeof session.user.email === "string" and that it is non-empty (or use optional
chaining plus a string guard) before calling .endsWith, and if the check fails
treat the user as unauthenticated and redirect to env.NEXT_PUBLIC_WEB_URL.

Comment thread apps/api/src/app/api/auth/desktop/connect/route.ts Outdated
Comment thread apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql Outdated
Comment on lines +32 to +34
DROP TABLE "organization_members" CASCADE;--> statement-breakpoint
DROP TABLE "organizations" CASCADE;--> statement-breakpoint
DROP TABLE "users" CASCADE;--> statement-breakpoint
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql

Repository: superset-sh/superset

Length of output: 1739


🏁 Script executed:

# Check for other migration files that might contain data migration logic
ls -la apps/desktop/src/renderer/lib/pglite/migrations/

Repository: superset-sh/superset

Length of output: 430


🏁 Script executed:

# Search for backup tables or references to old table names
rg "organization_members|organizations|users" apps/desktop/src/renderer/lib/pglite/migrations/ -A 2 -B 2

Repository: superset-sh/superset

Length of output: 18941


🏁 Script executed:

# Search for Electric sync configuration and data persistence strategy
rg "Electric|ephemeral|rebuild|sync" apps/desktop/src/renderer/lib/pglite/ -i

Repository: superset-sh/superset

Length of output: 4995


🏁 Script executed:

# Check if there's any migration or setup documentation
find apps/desktop -type f -name "*.md" -o -name "*.ts" -o -name "*.js" | xargs rg -l "auth.*schema|migration|backup" -i | head -20

Repository: superset-sh/superset

Length of output: 437


🏁 Script executed:

cat -n apps/desktop/src/renderer/lib/pglite/migrations/0000_init.sql | head -100

Repository: superset-sh/superset

Length of output: 4057


🏁 Script executed:

# Look for clerk field references
rg "clerk_id|clerk_org_id" apps/desktop/src/renderer/lib/pglite/ -B 2 -A 2

Repository: superset-sh/superset

Length of output: 7721


🏁 Script executed:

# Check the schema definition files
cat -n apps/desktop/src/renderer/lib/pglite/schema/auth.ts

Repository: superset-sh/superset

Length of output: 2307


🏁 Script executed:

# Check SYNCED_TABLES definition
cat -n apps/desktop/src/renderer/lib/pglite/sync.ts | head -50

Repository: superset-sh/superset

Length of output: 1076


🏁 Script executed:

# Check server schema to understand email_verified type
find apps -name "*.ts" -o -name "*.sql" | xargs rg "email_verified" -B 2 -A 2 | head -50

Repository: superset-sh/superset

Length of output: 1050


Add foreign key constraints for referential integrity in the auth schema.

The migration removes the clerk_id and clerk_org_id fields (intentional for auth restructuring) and adds new fields like email_verified and metadata. However, the new auth.members table lacks foreign key constraints linking organization_id and user_id to their respective parent tables, which can cause referential integrity issues.

Additionally, while the migration drops old tables with CASCADE, data is safely repopulated via Electric sync (configured in sync.ts to sync auth.users, auth.organizations, and auth.members from the server). For clarity, consider adding a comment to the migration explaining this dependency on Electric sync.

🤖 Prompt for AI Agents
In
apps/desktop/src/renderer/lib/pglite/migrations/0001_auth_schema_migration.sql
around lines 32-34, the new auth.members table lacks foreign key constraints for
organization_id and user_id and the migration drops legacy tables with CASCADE
without documenting reliance on Electric sync; update the migration to add
foreign keys linking auth.members.organization_id -> auth.organizations(id) and
auth.members.user_id -> auth.users(id) (choose ON DELETE CASCADE or RESTRICT per
domain rules) via ALTER TABLE or in the CREATE TABLE statement, and add a short
SQL comment above the DROP TABLEs noting that data is repopulated by Electric
sync (sync.ts) so the drops are intentional.

Comment thread apps/desktop/src/renderer/lib/pglite/schema/auth.ts Outdated
Comment thread apps/web/src/app/auth/desktop/success/page.tsx
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
packages/trpc/src/router/integration/integration.ts (1)

13-13: **UUID validation is now correctly applied using Zod 4 syntax.**The past review comment flagged relaxed validation, but the current code now uses z.uuid() — a Zod 4 top-level function where string format validators moved from methods. The syntax z.string().uuid() becomes z.uuid(). z.uuid() validates RFC 9562/4122 compliant UUIDs.

This correctly validates UUIDs and addresses the prior concern.

🧹 Nitpick comments (1)
apps/admin/src/app/(dashboard)/layout.tsx (1)

38-40: Consider case-insensitive email domain check.

The endsWith() method is case-sensitive, which could reject valid emails with different casing (e.g., user@Superset.SH). While email domains are typically normalized to lowercase, RFC 5321 allows mixed case.

🔎 Proposed case-insensitive check
-	if (!session.user.email?.endsWith(COMPANY.EMAIL_DOMAIN)) {
+	if (!session.user.email?.toLowerCase().endsWith(COMPANY.EMAIL_DOMAIN)) {
 		redirect(env.NEXT_PUBLIC_WEB_URL);
 	}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b078c31 and 7b2fca9.

📒 Files selected for processing (5)
  • apps/admin/src/app/(dashboard)/layout.tsx
  • apps/api/src/app/api/auth/desktop/connect/route.ts
  • apps/web/src/app/(dashboard)/layout.tsx
  • apps/web/src/app/auth/desktop/success/page.tsx
  • packages/trpc/src/router/integration/integration.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/src/app/api/auth/desktop/connect/route.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use type safety and avoid any types unless absolutely necessary in TypeScript files

Files:

  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/admin/src/app/(dashboard)/layout.tsx
  • packages/trpc/src/router/integration/integration.ts
  • apps/web/src/app/(dashboard)/layout.tsx
🧠 Learnings (2)
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to apps/desktop/src/**/*.{ts,tsx} : Move Node.js functionality needed in Electron renderer to `src/main/lib/` and communicate via IPC

Applied to files:

  • apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-28T01:56:39.031Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-28T01:56:39.031Z
Learning: Applies to packages/db/**/*.ts : Use Drizzle ORM for all database operations in the `packages/db` package

Applied to files:

  • packages/trpc/src/router/integration/integration.ts
🧬 Code graph analysis (2)
apps/admin/src/app/(dashboard)/layout.tsx (2)
apps/admin/src/env.ts (1)
  • env (5-44)
packages/shared/src/constants.ts (1)
  • COMPANY (12-24)
apps/web/src/app/(dashboard)/layout.tsx (1)
packages/auth/src/index.ts (1)
  • auth (9-71)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Deploy API
  • GitHub Check: Deploy Admin
  • GitHub Check: Deploy Docs
  • GitHub Check: Deploy Marketing
  • GitHub Check: Deploy Web
  • GitHub Check: Build
🔇 Additional comments (10)
packages/trpc/src/router/integration/integration.ts (2)

2-2: LGTM!

Import updates align with the schema migration to use the new members table. Removing the users import is appropriate since the user ID is now obtained directly from the session context.


15-23: LGTM!

The membership query correctly uses the new members table with session-based authentication. The and() and eq() constructs are properly used, and accessing ctx.session.user.id directly from the session context is cleaner than the previous user lookup approach.

apps/web/src/app/(dashboard)/layout.tsx (1)

1-3: LGTM: Imports are appropriate for authentication gate.

The imports correctly bring in the Better Auth instance, Next.js headers utility, and redirect function needed for the session-based route guard.

apps/admin/src/app/(dashboard)/layout.tsx (4)

1-2: LGTM! Clean imports for authentication guard.

The imports are well-organized and appropriate for implementing server-side authentication with Better Auth in Next.js 15+.

Also applies to: 17-18, 20-20


34-36: LGTM! Proper session validation with optional chaining.

The check correctly handles cases where the session is missing or doesn't contain a user, redirecting unauthorized users appropriately.


42-47: LGTM! Improved error handling with redirect.

The change from throwing an error to redirecting when user data is missing is a good UX improvement. This provides a cleaner user experience by redirecting to the web app instead of showing an error page.

The separate user data fetch via TRPC after session validation is appropriate, as the session likely contains minimal user data while AppSidebar needs the complete user profile.


30-32: This follows the established pattern used throughout the codebase. The auth.api.getSession call without try-catch is consistent with other layout files and middleware (e.g., apps/web/src/app/(dashboard)/layout.tsx, apps/admin/src/proxy.ts). Unhandled errors will propagate to Next.js error boundaries, which is the standard approach here.

If explicit error handling is desired, consider addressing this systematically across the codebase rather than in isolated files.

apps/web/src/app/auth/desktop/success/page.tsx (3)

1-4: LGTM!

Imports are clean and appropriate for a Next.js server component with Better Auth integration.


6-22: LGTM!

State parameter validation is properly implemented with good type safety and user-friendly error messaging.


40-49: LGTM!

Session existence check is appropriate and handles the missing session case correctly with user-friendly messaging.

Comment on lines +15 to +21
const session = await auth.api.getSession({
headers: await headers(),
});

if (!session) {
redirect("/sign-in");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling to prevent layout crashes.

If auth.api.getSession() throws an error (e.g., database connection failure, auth service unavailable), the entire dashboard will crash. Wrap the auth call in try-catch to gracefully handle failures.

🔎 Proposed fix with error handling
+	let session;
+	try {
-		const session = await auth.api.getSession({
+		session = await auth.api.getSession({
 			headers: await headers(),
 		});
+	} catch (error) {
+		console.error("Failed to fetch session:", error);
+		redirect("/sign-in");
+	}

 	if (!session) {
 		redirect("/sign-in");
 	}

Consider explicit type annotation for better type safety.

Adding an explicit type to the session variable improves code clarity and aligns with the TypeScript coding guidelines.

As per coding guidelines, "Use type safety and avoid any types unless absolutely necessary in TypeScript files."

🔎 Suggested type annotation
-	const session = await auth.api.getSession({
+	const session: Awaited<ReturnType<typeof auth.api.getSession>> | null = await auth.api.getSession({
 		headers: await headers(),
 	});

Or import and use the Session type if exported from @superset/auth:

+	import type { Session } from "@superset/auth";
 	// ...
-	const session = await auth.api.getSession({
+	const session: Session | null = await auth.api.getSession({
 		headers: await headers(),
 	});
🤖 Prompt for AI Agents
In apps/web/src/app/(dashboard)/layout.tsx around lines 15 to 21, the call to
auth.api.getSession is unguarded and can throw, causing the dashboard to crash;
wrap the await auth.api.getSession(...) in a try-catch so any thrown error is
handled (log the error and redirect to "/sign-in" or render a fallback UI) to
prevent a hard crash, and add an explicit type annotation for the session
variable (import and use the Session type from the auth package or define the
appropriate interface) so the variable is strongly typed rather than inferred.

Comment on lines +24 to +38
// Get session from Better Auth
let session;
try {
session = await auth.api.getSession({ headers: await headers() });
} catch (error) {
console.error("Failed to get session for desktop auth:", error);
return (
<div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
<p className="text-xl text-muted-foreground">Authentication failed</p>
<p className="text-muted-foreground/70">
Please try signing in again from the desktop app.
</p>
</div>
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Distinguish network errors from authentication failures to preserve offline-first functionality.

While the try-catch addresses the previous review comment about error handling, it treats all errors identically and breaks the offline-first behavior. According to the PR objectives, the reviewer specifically flagged this regression: the previous implementation (PR #512) distinguished network errors from invalid tokens to preserve sessions during transient network failures.

The current implementation would clear the session on any error, including temporary network issues, preventing the desktop app from functioning offline or on flaky networks.

Impact:

  • Users on unstable connections would be forced to re-authenticate repeatedly
  • Offline desktop functionality is broken
  • Transient errors become permanent failures

Based on PR objectives, you should:

  1. Distinguish error types (network vs authentication failures)
  2. Preserve sessions during network errors (return error state without clearing session)
  3. Clear sessions only on invalid/401/403 responses
  4. Consider adding token refresh handling for expired tokens
🔎 Guidance for error handling

The error handling should check the error type and HTTP status:

try {
    session = await auth.api.getSession({ headers: await headers() });
} catch (error) {
    console.error("Failed to get session for desktop auth:", error);
    
    // Check if it's a network error vs auth failure
    const isNetworkError = 
        error instanceof Error && 
        (error.message.includes('fetch failed') || 
         error.message.includes('network') ||
         error.cause instanceof TypeError);
    
    if (isNetworkError) {
        // Network error - preserve session, allow retry
        return (
            <div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
                <p className="text-xl text-muted-foreground">Network error</p>
                <p className="text-muted-foreground/70">
                    Unable to verify session. Please check your connection and try again.
                </p>
            </div>
        );
    }
    
    // Auth failure - clear session
    return (
        <div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
            <p className="text-xl text-muted-foreground">Authentication failed</p>
            <p className="text-muted-foreground/70">
                Please try signing in again from the desktop app.
            </p>
        </div>
    );
}

Note: The exact error detection logic depends on how Better Auth reports errors. You may need to check for specific error properties or status codes.

Comment thread apps/web/src/app/auth/desktop/success/page.tsx Outdated
Better Auth uses BETTER_AUTH_SECRET, not DESKTOP_AUTH_SECRET.
The DESKTOP_AUTH_SECRET was a legacy name that is no longer used in the codebase.

Changes:
- Removed DESKTOP_AUTH_SECRET env var from deploy-production.yml
- Removed DESKTOP_AUTH_SECRET env var from deploy-preview.yml
- BETTER_AUTH_SECRET is the only auth secret needed

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/desktop/src/main/windows/main.ts (1)

34-57: Window state persistence removed—document the UX tradeoff or reintroduce it.

The window state persistence feature (size, position, maximized state) was removed in the session isolation commit. Users will lose their window layout on every restart. While this simplifies the code and the session isolation is necessary for the auth fix, the UX impact should be explicitly acknowledged.

Either:

  1. Reintroduce window state persistence with the new session partition setup, or
  2. If this removal is intentional, document the decision and ensure the default window size (primary display work area) provides acceptable UX across different display configurations.
🤖 Fix all issues with AI Agents
In @apps/desktop/src/main/windows/main.ts:
- Line 32: Wrap the call to screen.getPrimaryDisplay().workAreaSize in a
try/catch inside main.ts so the app doesn't crash if the screen API throws; on
error log the exception (or use processLogger) and fall back to safe default
dimensions (e.g., width=1024, height=768) and continue startup, replacing the
direct destructuring of { width, height } with values from the try block or the
fallback.

In @apps/web/src/app/auth/desktop/success/page.tsx:
- Around line 70-79: Wrap the db.insert(sessions).values(...) call in a
try-catch to handle DB errors gracefully: catch any error thrown by
db.insert(sessions), log it with contextual info (e.g., token, session.user.id,
session.session.activeOrganizationId) using the existing logger or
console.error, and then return or throw a controlled error/user-friendly
response instead of letting Next.js surface a generic crash; update the block
around the insert in page.tsx (the await db.insert(sessions).values({...})
statement) to perform this try-catch and logging.
🧹 Nitpick comments (2)
apps/web/src/app/auth/desktop/success/page.tsx (1)

66-68: Extract session expiry duration to a shared constant.

The 30-day expiry (60 * 60 * 24 * 30 * 1000) is duplicated here and in packages/auth/src/index.ts (line 25: expiresIn: 60 * 60 * 24 * 30). If the auth config changes, this value could drift out of sync.

Consider exporting the session duration from @superset/auth and importing it here.

🔎 Suggested approach

In packages/auth/src/index.ts:

export const SESSION_EXPIRY_SECONDS = 60 * 60 * 24 * 30; // 30 days

Then in this file:

+import { SESSION_EXPIRY_SECONDS } from "@superset/auth";
+
+const SESSION_EXPIRY_MS = SESSION_EXPIRY_SECONDS * 1000;
+
 // ...
 const expiresAt = new Date(
-	Date.now() + 60 * 60 * 24 * 30 * 1000, // 30 days (matching auth config)
+	Date.now() + SESSION_EXPIRY_MS,
 );
apps/desktop/src/main/windows/main.ts (1)

53-55: Extract partition name to a named constant.

Per coding guidelines, hardcoded string values should be extracted to named constants at the module top for maintainability.

🔎 Proposed refactor

At the top of the file (after imports):

+// Electron session partition for desktop auth isolation
+const DESKTOP_SESSION_PARTITION = "persist:superset";
+
 // Singleton IPC handler to prevent duplicate handlers on window reopen (macOS)
 let ipcHandler: ReturnType<typeof createIPCHandler> | null = null;

Then update the usage:

 		webPreferences: {
 			preload: join(__dirname, "../preload/index.js"),
 			webviewTag: true,
 			// Isolate Electron session from system browser cookies
 			// This ensures desktop uses bearer token auth, not web cookies
-			partition: "persist:superset",
+			partition: DESKTOP_SESSION_PARTITION,
 		},
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b5ff3d and c592ee1.

📒 Files selected for processing (3)
  • apps/desktop/src/main/windows/main.ts
  • apps/web/src/app/auth/desktop/success/page.tsx
  • packages/auth/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use object parameters for functions with 2+ parameters instead of positional arguments
Functions with 2+ parameters should accept a single params object with named properties for self-documentation and extensibility
Use prefixed console logging with context pattern: [domain/operation] message
Extract magic numbers and hardcoded values to named constants at module top
Use lookup objects/maps instead of repeated if (type === ...) conditionals
Avoid using any type - maintain type safety in TypeScript code
Never swallow errors silently - at minimum log them with context
Import from concrete files directly when possible - avoid barrel file abuse that creates circular dependencies
Avoid deep nesting (4+ levels) - use early returns, extract functions, and invert conditions
Use named properties in options objects instead of boolean parameters to avoid boolean blindness

Files:

  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/desktop/src/main/windows/main.ts
**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

One component per file - do not create multi-component files

Files:

  • apps/web/src/app/auth/desktop/success/page.tsx
apps/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Drizzle ORM for all database operations - never use raw SQL

Files:

  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/desktop/src/main/windows/main.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Biome for formatting and linting - run at root level with bun run lint:fix or biome check --write

Files:

  • apps/web/src/app/auth/desktop/success/page.tsx
  • apps/desktop/src/main/windows/main.ts
apps/desktop/**/*.{ts,tsx}

📄 CodeRabbit inference engine (apps/desktop/AGENTS.md)

apps/desktop/**/*.{ts,tsx}: For Electron interprocess communication, ALWAYS use tRPC as defined in src/lib/trpc
Use alias as defined in tsconfig.json when possible
Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.
For tRPC subscriptions with trpc-electron, ALWAYS use the observable pattern from @trpc/server/observable instead of async generators, as the library explicitly checks isObservable(result) and throws an error otherwise

Files:

  • apps/desktop/src/main/windows/main.ts
🧠 Learnings (8)
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to **/*.{ts,tsx} : Never swallow errors silently - at minimum log them with context

Applied to files:

  • apps/web/src/app/auth/desktop/success/page.tsx
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : Prefer zustand for state management if it makes sense. Do not use effect unless absolutely necessary.

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/electron-router-dom.ts : Do not import Node.js modules like node:path or dotenv in electron-router-dom.ts and similar shared files - they run in both main and renderer processes

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/renderer/**/*.{ts,tsx} : Never import Node.js modules (fs, path, os, net) in renderer process or shared code - they are externalized for browser compatibility

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/lib/*.ts : Never import Node.js modules in shared code like electron-router-dom.ts - it runs in both main and renderer processes

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2025-12-21T04:39:28.543Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: apps/desktop/AGENTS.md:0-0
Timestamp: 2025-12-21T04:39:28.543Z
Learning: Applies to apps/desktop/**/*.{ts,tsx} : For Electron interprocess communication, ALWAYS use tRPC as defined in `src/lib/trpc`

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/shared/ipc-channels.ts : Define IPC channel types in apps/desktop/src/shared/ipc-channels.ts before implementing handlers

Applied to files:

  • apps/desktop/src/main/windows/main.ts
📚 Learning: 2026-01-02T06:50:28.671Z
Learnt from: CR
Repo: superset-sh/superset PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-02T06:50:28.671Z
Learning: Applies to apps/desktop/src/main/index.ts : Load environment variables from monorepo root .env in desktop app with override: true before any imports in src/main/index.ts and electron.vite.config.ts

Applied to files:

  • apps/desktop/src/main/windows/main.ts
🧬 Code graph analysis (1)
apps/web/src/app/auth/desktop/success/page.tsx (1)
packages/auth/src/index.ts (1)
  • auth (11-101)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Deploy Docs
  • GitHub Check: Deploy API
  • GitHub Check: Deploy Web
  • GitHub Check: Deploy Marketing
  • GitHub Check: Deploy Admin
  • GitHub Check: Build
🔇 Additional comments (6)
apps/web/src/app/auth/desktop/success/page.tsx (4)

1-6: LGTM!

Imports are well-organized, using concrete file imports and the shared auth/db packages appropriately.


8-24: LGTM!

Good use of early return pattern for validation. The typed searchParams as Promise is correct for Next.js 15 server components.


80-88: LGTM!

URL construction is secure with proper encodeURIComponent usage on all parameters. The protocol switching between development and production environments is handled correctly.


30-40: Network error vs authentication failure handling remains unaddressed.

The catch block treats all errors uniformly as authentication failures. Per PR objectives, this regression was explicitly flagged: the previous implementation distinguished network errors from invalid tokens to preserve sessions during transient network failures.

This breaks offline-first functionality—users on unstable connections would be forced to re-authenticate repeatedly.

Likely an incorrect or invalid review comment.

apps/desktop/src/main/windows/main.ts (2)

5-5: LGTM!

The screen import is necessary for the new dynamic window sizing functionality.


166-175: LGTM!

The close handler properly tears down the notifications server, clears listeners, and detaches the IPC handler to prevent resource leaks and duplicate registrations on window reopen (macOS). The removal of window state saving is consistent with the broader changes to eliminate the window-state module.

export async function MainWindow() {
const savedWindowState = loadWindowState();
const initialBounds = getInitialWindowBounds(savedWindowState);
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Add error handling for screen API to prevent startup crashes.

screen.getPrimaryDisplay() can throw in edge cases (no display available, headless systems, display disconnection). Without error handling, this will crash the app on startup with no recovery path.

🔎 Proposed fix with fallback dimensions
+// Fallback dimensions if screen API fails
+const DEFAULT_WINDOW_WIDTH = 1200;
+const DEFAULT_WINDOW_HEIGHT = 800;
+
 export async function MainWindow() {
-	const { width, height } = screen.getPrimaryDisplay().workAreaSize;
+	let width = DEFAULT_WINDOW_WIDTH;
+	let height = DEFAULT_WINDOW_HEIGHT;
+	
+	try {
+		const workAreaSize = screen.getPrimaryDisplay().workAreaSize;
+		width = workAreaSize.width;
+		height = workAreaSize.height;
+	} catch (error) {
+		console.error("[window/main] Failed to get primary display, using defaults:", error);
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const { width, height } = screen.getPrimaryDisplay().workAreaSize;
// Fallback dimensions if screen API fails
const DEFAULT_WINDOW_WIDTH = 1200;
const DEFAULT_WINDOW_HEIGHT = 800;
export async function MainWindow() {
let width = DEFAULT_WINDOW_WIDTH;
let height = DEFAULT_WINDOW_HEIGHT;
try {
const workAreaSize = screen.getPrimaryDisplay().workAreaSize;
width = workAreaSize.width;
height = workAreaSize.height;
} catch (error) {
console.error("[window/main] Failed to get primary display, using defaults:", error);
}
🤖 Prompt for AI Agents
In @apps/desktop/src/main/windows/main.ts around line 32, Wrap the call to
screen.getPrimaryDisplay().workAreaSize in a try/catch inside main.ts so the app
doesn't crash if the screen API throws; on error log the exception (or use
processLogger) and fall back to safe default dimensions (e.g., width=1024,
height=768) and continue startup, replacing the direct destructuring of { width,
height } with values from the try block or the fallback.

Comment on lines +70 to +79
// Create a new session record in the database
await db.insert(sessions).values({
token,
userId: session.user.id,
expiresAt,
ipAddress,
userAgent,
activeOrganizationId: session.session.activeOrganizationId,
updatedAt: now,
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Wrap database insert in try-catch for graceful error handling.

If db.insert(sessions) fails (connection issues, constraint violations, etc.), the page will crash with a generic Next.js error instead of showing a user-friendly message. Based on learnings, errors should never be swallowed silently and should be logged with context.

🔎 Suggested fix
 	// Create a new session record in the database
+	try {
 		await db.insert(sessions).values({
 			token,
 			userId: session.user.id,
 			expiresAt,
 			ipAddress,
 			userAgent,
 			activeOrganizationId: session.session.activeOrganizationId,
 			updatedAt: now,
 		});
+	} catch (error) {
+		console.error("[auth/desktop] Failed to create desktop session:", error);
+		return (
+			<div className="flex min-h-screen flex-col items-center justify-center bg-background p-4">
+				<p className="text-xl text-muted-foreground">Failed to create session</p>
+				<p className="text-muted-foreground/70">
+					Please try signing in again from the desktop app.
+				</p>
+			</div>
+		);
+	}
🤖 Prompt for AI Agents
In @apps/web/src/app/auth/desktop/success/page.tsx around lines 70-79, Wrap the
db.insert(sessions).values(...) call in a try-catch to handle DB errors
gracefully: catch any error thrown by db.insert(sessions), log it with
contextual info (e.g., token, session.user.id,
session.session.activeOrganizationId) using the existing logger or
console.error, and then return or throw a controlled error/user-friendly
response instead of letting Next.js surface a generic crash; update the block
around the insert in page.tsx (the await db.insert(sessions).values({...})
statement) to perform this try-catch and logging.

@saddlepaddle saddlepaddle merged commit 9ee8911 into main Jan 6, 2026
12 of 13 checks passed
saddlepaddle added a commit that referenced this pull request Jan 6, 2026
This reverts commit 9ee8911.

Reason: The Better Auth migration had unintended side effects:
- Desktop app required database dependencies it doesn't need
- Many desktop UI files (sidebar, workspace views) were accidentally deleted
- Local-db migrations were removed that were already deployed to users

We're reverting to unblock users and will re-implement Better Auth properly
in a focused PR that only changes the auth implementation.

Additional changes:
- Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
saddlepaddle added a commit that referenced this pull request Jan 6, 2026
This reverts commit 9ee8911.

Reason: The Better Auth migration had unintended side effects:
- Desktop app required database dependencies it doesn't need
- Many desktop UI files (sidebar, workspace views) were accidentally deleted
- Local-db migrations were removed that were already deployed to users

We're reverting to unblock users and will re-implement Better Auth properly
in a focused PR that only changes the auth implementation.

Additional changes:
- Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
saddlepaddle added a commit that referenced this pull request Jan 6, 2026
This reverts commit 9ee8911.

Reason: The Better Auth migration had unintended side effects:
- Desktop app required database dependencies it doesn't need
- Many desktop UI files (sidebar, workspace views) were accidentally deleted
- Local-db migrations were removed that were already deployed to users

We're reverting to unblock users and will re-implement Better Auth properly
in a focused PR that only changes the auth implementation.

Additional changes:
- Reduced minimum desktop version from 0.0.44 to 0.0.39 to unblock existing users
@coderabbitai coderabbitai Bot mentioned this pull request Jan 6, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant