Skip to content

feat(admin): introduce admin dashboard and related configurations - #1279

Merged
steebchen merged 9 commits into
mainfrom
feat/admin-dashboard
Dec 3, 2025
Merged

steebchen merged 9 commits into
mainfrom
feat/admin-dashboard

Conversation

@smakosh

@smakosh smakosh commented Dec 1, 2025

Copy link
Copy Markdown
Member
  • Added a new admin dashboard application with necessary components and configurations.
  • Updated environment variables to include ADMIN_PORT and ADMIN_URL.
  • Enhanced documentation to reflect the addition of the admin interface.
  • Adjusted API routes to accommodate admin functionalities.

Test Plan

  • Verify that the admin dashboard loads correctly on http://localhost:3006.
  • Ensure all new API routes for admin are functional.
  • Validate that environment variables are correctly set and utilized.

Summary by CodeRabbit

  • New Features

    • Adds an Admin dashboard at http://localhost:3006 with admin sign-in, protected routes, and an admin metrics page.
  • Documentation

    • Dev docs, READMEs and dev commands updated to include the Admin service and local URL.
  • Infrastructure

    • Local/dev Docker, compose, CI workflows, CORS/origins, and deploy configs updated to expose port 3006 and ADMIN_URL; build/test matrices include Admin.
  • Chores

    • Clean/ignore rules adjusted so previously excluded paths may now be included in build/context.

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

- Added a new admin dashboard application with necessary components and configurations.
- Updated environment variables to include ADMIN_PORT and ADMIN_URL.
- Enhanced documentation to reflect the addition of the admin interface.
- Adjusted API routes to accommodate admin functionalities.

## Test Plan
- [ ] Verify that the admin dashboard loads correctly on http://localhost:3006.
- [ ] Ensure all new API routes for admin are functional.
- [ ] Validate that environment variables are correctly set and utilized.
@smakosh
smakosh requested a review from steebchen December 1, 2025 16:50
@smakosh smakosh self-assigned this Dec 1, 2025
@coderabbitai

coderabbitai Bot commented Dec 1, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new Next.js Admin app served on port 3006 (UI, middleware, auth clients, hooks, components), a new admin-only API route (/admin/metrics), CI/infra/docker updates to build and expose Admin, and removes .vinxi from dockerignore/turbo/package clean outputs.

Changes

Cohort / File(s) Change Summary
Admin app scaffold & config
apps/admin/package.json, apps/admin/tsconfig.json, apps/admin/next.config.ts, apps/admin/postcss.config.mjs, apps/admin/eslint.config.mjs, apps/admin/components.json
New Next.js TypeScript project and build/dev tooling for the admin app.
Admin app metadata & CI scripts
apps/admin/.gitignore, apps/admin/.lintstagedrc.json, apps/admin/.prettierignore, apps/admin/README.md, apps/admin/public/favicon/site.webmanifest, .github/start.sh, .github/test-split-docker.sh, .github/test-unified-docker.sh, .github/workflows/images.yml
Ignore/format configs, README, manifest, and CI/start/test scripts updated to include admin.
Admin pages, layout & middleware
apps/admin/src/app/layout.tsx, apps/admin/src/app/page.tsx, apps/admin/src/app/login/page.tsx, apps/admin/middleware.ts
New layout, login and dashboard pages; middleware enforces admin-only access via session cookie validation against backend /user/me.
Admin UI components & primitives
apps/admin/src/components/ui/* (alert, avatar, badge, button, carousel, checkbox, collapsible, command, dialog, dropdown-menu, form, hover-card, input-group, input, label, logo, popover, progress, scroll-area, select, separator, sheet, sidebar, skeleton, sonner, tabs, textarea, tooltip)
Large set of Radix/Tailwind-styled UI primitives and composition components added.
Admin shell, providers & hooks
apps/admin/src/components/admin-shell.tsx, apps/admin/src/components/auth/user-provider.tsx, apps/admin/src/components/server-data-wrapper.tsx, apps/admin/src/components/landing/theme-toggle.tsx, apps/admin/src/hooks/*
Admin shell, user/provider wrappers, server-data seeding, theme toggle, mobile and user hooks (fetch/update/delete, redirect logic).
Admin libs & types
apps/admin/src/lib/*, apps/admin/src/lib/types.ts, apps/admin/src/types/next-themes.d.ts
New client/server helpers: config-server, config client/provider, fetch-client, server-api, getUser, auth-client, providers, stripe helper, admin-metrics, utils, and types.
API routes & user updates
apps/api/src/routes/admin.ts, apps/api/src/routes/user.ts, apps/api/src/routes/index.ts, apps/api/src/index.ts, apps/api/src/auth/config.ts
New /admin/metrics admin-only route with aggregations; user responses include isAdmin; CORS origins updated to allow admin origin; route registered.
Frontend/docs & config propagation
AGENTS.md, CLAUDE.md, README.md, apps/docs/app/api/proxy/route.ts, apps/docs/content/self-host.mdx, apps/ui/src/content/blog/*, apps/playground/src/lib/config-server.ts, apps/ui/src/lib/config-server.ts
Documentation and examples updated to list Admin and port 3006; proxy allowed origins updated; adminUrl added to frontend config.
Infrastructure & orchestration
infra/split.dockerfile, infra/unified.dockerfile, infra/docker-compose.split.yml, infra/docker-compose.split.local.yml, infra/docker-compose.unified.yml, infra/docker-compose.unified.local.yml, infra/supervisord.conf, infra/bunnyshell.yaml, infra/split.dockerfile
Added admin build/runtime stages, expose port 3006, ADMIN_URL env, compose/service entries, supervisord program, and bunnyshell application for admin.
Env examples
.env.example, .env.unified.example
Added ADMIN_PORT / ADMIN_URL example entries.
Build tooling & cleaning
.dockerignore, package.json, turbo.json
Removed .vinxi from .dockerignore and package.json clean script; removed .vinxi/** from turbo.json build outputs.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browser
    participant AdminApp as Admin App (Next.js)
    participant Middleware
    participant Backend as API Backend (/user/me)
    participant AdminAPI as API /admin/metrics
    participant DB as Database

    User->>Browser: Request admin dashboard
    Browser->>AdminApp: GET /
    AdminApp->>Middleware: middleware(req)
    Middleware->>Middleware: read session cookie(s)
    alt no session
        Middleware->>Browser: redirect /login
    else session present
        Middleware->>Backend: GET /user/me (with cookie)
        alt auth fails or not admin
            Backend-->>Middleware: 401/invalid or user.isAdmin=false
            Middleware->>Browser: redirect /login or 403
        else auth succeeds && isAdmin
            Middleware->>AdminApp: allow request
            AdminApp->>AdminAPI: GET /admin/metrics
            AdminAPI->>DB: run aggregation queries
            DB-->>AdminAPI: aggregates
            AdminAPI-->>AdminApp: metrics payload
            AdminApp->>Browser: render dashboard with metrics
        end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas requiring extra attention:
    • apps/admin/middleware.ts — cookie handling, backend call, redirects and 403 behavior.
    • apps/api/src/routes/admin.ts — authorization, aggregation queries, SQL/ORM correctness and performance.
    • apps/api/src/routes/user.tsisAdmin derivation and schema changes.
    • Infra & Docker files — multi-stage Dockerfile, compose entries, supervisord/bunnyshell, exposed ports/healthchecks.
    • Build/tooling changes — .dockerignore, package.json clean, and turbo.json outputs.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.48% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing an admin dashboard with related configurations, which is the primary focus across the changeset.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/admin-dashboard

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a8a616 and 726ad6b.

📒 Files selected for processing (1)
  • .github/workflows/images.yml (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.822Z
Learning: Applies to apps/ui/**/*.{ts,tsx} : Use Next.js App Router with React Server Components for frontend development
⏰ 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). (17)
  • GitHub Check: build-split (api, linux/amd64)
  • GitHub Check: build-split (docs, linux/amd64)
  • GitHub Check: build-split (admin, linux/amd64)
  • GitHub Check: build-split (ui, linux/amd64)
  • GitHub Check: build-split (worker, linux/amd64)
  • GitHub Check: build-split (playground, linux/amd64)
  • GitHub Check: build-split (gateway, linux/amd64)
  • GitHub Check: build-unified (linux/amd64)
  • GitHub Check: autofix
  • GitHub Check: test / run
  • GitHub Check: lint / run
  • GitHub Check: build / run
  • GitHub Check: e2e-shards (5)
  • GitHub Check: e2e-shards (2)
  • GitHub Check: e2e-shards (4)
  • GitHub Check: e2e-shards (3)
  • GitHub Check: e2e-shards (1)
🔇 Additional comments (3)
.github/workflows/images.yml (3)

190-206: Disk space cleanup is a welcome operational improvement.

The new "Free up disk space" step proactively removes unused tools and prunes Docker images, which helps prevent runner disk exhaustion during the test workflow.


230-234: Good: Per-iteration cleanup and observability in the build loop.

Adding docker image prune -f after each app build and reporting disk space helps prevent space-related failures when building multiple large images sequentially. This is particularly important for the admin build.


59-59: The workflow changes are properly supported by existing infrastructure. Both infra/split.dockerfile and .github/test-split-docker.sh already include the necessary configurations:

  • The Dockerfile defines admin-builder (line 109) and admin (line 238) build stages
  • The test script includes the admin service endpoint at http://localhost:3006/login (line 22)

No additional changes are needed to these files; the workflow additions to lines 59, 130, and 221 will function correctly as-is.

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 17

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

Caution

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

⚠️ Outside diff range comments (2)
.env.example (1)

29-35: Wire ADMIN_PORT into ORIGIN_URLS and optionally align key ordering.

  • Functionally, adding ADMIN_PORT=3006 here is fine, but with a browser-based Admin UI on http://localhost:3006 hitting the API, ORIGIN_URLS probably also needs to include that origin (and possibly the other first‑party UIs) to avoid CORS/trusted‑origin surprises. For example:
-ORIGIN_URLS=http://localhost:3002
+ORIGIN_URLS=http://localhost:3002,http://localhost:3003,http://localhost:3005,http://localhost:3006
  • dotenv-linter suggests ADMIN_PORT go before API_PORT; if you want to keep the env files linter‑clean, you can reorder the SERVICE PORTS block accordingly. This is purely stylistic, not functional.

Also applies to: 56-63

.env.unified.example (1)

18-25: Ensure ORIGIN_URLS covers the new ADMIN_URL origin (and tidy key order if desired).

  • Adding ADMIN_URL=http://localhost:3006 is good, but with the admin UI talking to the API from that origin, ORIGIN_URLS should probably be expanded to include it (and other first‑party UIs) to avoid CORS/trusted‑origin issues. For example:
-ORIGIN_URLS=http://localhost:3002
+ORIGIN_URLS=http://localhost:3002,http://localhost:3003,http://localhost:3005,http://localhost:3006
  • dotenv-linter suggests placing ADMIN_URL before DOCS_URL for consistent ordering. That’s cosmetic but keeps env linting clean.
🟡 Minor comments (4)
apps/admin/src/components/ui/carousel.tsx-80-91 (1)

80-91: Keyboard navigation doesn't adapt to vertical orientation.

Vertical carousels typically expect ArrowUp/ArrowDown for navigation, but this handler only responds to ArrowLeft/ArrowRight regardless of orientation.

 	const handleKeyDown = React.useCallback(
 		(event: React.KeyboardEvent<HTMLDivElement>) => {
-			if (event.key === "ArrowLeft") {
+			const prevKey = orientation === "horizontal" ? "ArrowLeft" : "ArrowUp";
+			const nextKey = orientation === "horizontal" ? "ArrowRight" : "ArrowDown";
+			if (event.key === prevKey) {
 				event.preventDefault();
 				scrollPrev();
-			} else if (event.key === "ArrowRight") {
+			} else if (event.key === nextKey) {
 				event.preventDefault();
 				scrollNext();
 			}
 		},
-		[scrollPrev, scrollNext],
+		[orientation, scrollPrev, scrollNext],
 	);
apps/admin/README.md-17-17 (1)

17-17: Correct the port number in the README.

The README mentions opening http://localhost:3000, but according to the PR objectives and configuration files, the admin dashboard runs on port 3006.

Apply this diff to correct the port:

-Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+Open [http://localhost:3006](http://localhost:3006) with your browser to see the result.
apps/gateway/src/app.ts-77-77 (1)

77-77: Add admin URL to ORIGIN_URLS in docker-compose files.

The environment variable defaults in docker-compose.*.yml files exclude http://localhost:3006 from ORIGIN_URLS, while the code fallback includes it. Update the ORIGIN_URLS defaults to include the admin URL for consistency:

ORIGIN_URLS=${ORIGIN_URLS:-http://localhost:3002,http://localhost:3003,http://localhost:3006,http://localhost:4002}

This ensures the configuration explicitly matches the intended origins and doesn't rely on hardcoded fallbacks.

apps/admin/src/app/login/page.tsx-160-167 (1)

160-167: Incomplete "Or" separator.

The "Or" divider suggests alternative login methods should follow, but nothing is rendered below it. Either add the intended alternative options (e.g., SSO, magic link) or remove this separator.

🧹 Nitpick comments (35)
apps/admin/src/components/ui/command.tsx (1)

23-39: cmdk-input-wrapper + ESLint disable are acceptable; consider centralizing lint config

Using cmdk-input-wrapper on the wrapper <div> is required by cmdk, so the inline // eslint-disable-next-line react/no-unknown-property is justified. If this pattern appears in multiple places, consider adjusting your ESLint configuration (or rule overrides for this file/directory) instead of repeating inline disables, but this is purely optional.

apps/admin/src/components/ui/alert.tsx (1)

22-66: Consider forwarding refs for better component reusability.

While the current implementation is functional, using React.forwardRef for Alert, AlertTitle, and AlertDescription would allow consumers to attach refs to these components when needed.

Example for Alert:

-function Alert({
+const Alert = React.forwardRef<
+	HTMLDivElement,
+	React.ComponentProps<"div"> & VariantProps<typeof alertVariants>
+>(function Alert({
 	className,
 	variant,
 	...props
-}: React.ComponentProps<"div"> & VariantProps<typeof alertVariants>) {
+}, ref) {
 	return (
 		<div
+			ref={ref}
 			data-slot="alert"
 			role="alert"
 			className={cn(alertVariants({ variant }), className)}
 			{...props}
 		/>
 	);
-}
+});
+Alert.displayName = "Alert";

Apply similar patterns to AlertTitle and AlertDescription.

apps/admin/src/components/ui/carousel.tsx (2)

114-127: Consider memoizing the context value.

The ESLint disable acknowledges the issue: a new context value object is created every render, which can trigger unnecessary re-renders in consumers. If performance becomes a concern, wrap with useMemo.

+	const contextValue = React.useMemo(
+		() => ({
+			carouselRef,
+			api,
+			opts,
+			orientation:
+				orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
+			scrollPrev,
+			scrollNext,
+			canScrollPrev,
+			canScrollNext,
+		}),
+		[carouselRef, api, opts, orientation, scrollPrev, scrollNext, canScrollPrev, canScrollNext],
+	);

 	return (
 		<CarouselContext.Provider
-			// eslint-disable-next-line react/jsx-no-constructed-context-values
-			value={{
-				carouselRef,
-				api: api,
-				opts,
-				orientation:
-					orientation || (opts?.axis === "y" ? "vertical" : "horizontal"),
-				scrollPrev,
-				scrollNext,
-				canScrollPrev,
-				canScrollNext,
-			}}
+			value={contextValue}
 		>

241-248: Consider exporting useCarousel for custom control scenarios.

The useCarousel hook is defined but not exported. If consumers need to build custom carousel controls (e.g., dot indicators, custom navigation), they'll need access to this hook.

 export {
 	type CarouselApi,
 	Carousel,
 	CarouselContent,
 	CarouselItem,
 	CarouselPrevious,
 	CarouselNext,
+	useCarousel,
 };
apps/docs/content/self-host.mdx (1)

28-30: Document what runs on port 3006 (Admin Dashboard).

You expose -p 3006:3006 but the “Accessing Your LLMGateway” section doesn’t mention this port, so it’s unclear to users what it’s for. Consider adding an entry like:

 After starting either option, you can access:

 - **Web Interface**: http://localhost:3002
 - **Documentation**: http://localhost:3005
 - **API Endpoint**: http://localhost:4002
 - **Gateway Endpoint**: http://localhost:4001
+ - **Admin Dashboard**: http://localhost:3006

Also applies to: 74-82

apps/ui/src/content/blog/2025-05-01-self-host-llm-gateway.md (1)

43-43: Consider formatting the URL as a link.

The static analysis tool flagged this as a bare URL. While this is functional, consider formatting it as a markdown link for consistency with documentation best practices.

Apply this diff to format as a link:

-- Admin: http://localhost:3006
+- Admin: [http://localhost:3006](http://localhost:3006)
apps/admin/src/hooks/use-mobile.ts (1)

10-18: Consider using mql.matches for consistency.

The hook correctly implements mobile detection with proper SSR handling and cleanup. However, the onChange callback rechecks window.innerWidth instead of using the mql.matches property, which is already tracking the same condition.

Apply this diff to use the MediaQueryList's matches property:

 React.useEffect(() => {
   const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
   const onChange = () => {
-    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
+    setIsMobile(mql.matches);
   };
   mql.addEventListener("change", onChange);
   setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
   return () => mql.removeEventListener("change", onChange);
 }, []);
apps/admin/src/components/admin-shell.tsx (2)

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

If signOut throws or fails silently, the user receives no feedback. Consider handling errors to inform the user or retry.

 const handleSignOut = async () => {
-	await signOut({
-		fetchOptions: {
-			onSuccess: () => {
-				queryClient.clear();
-				router.push("/login");
+	try {
+		await signOut({
+			fetchOptions: {
+				onSuccess: () => {
+					queryClient.clear();
+					router.push("/login");
+				},
 			},
-		},
-	});
+		});
+	} catch {
+		// Optionally show a toast or error message
+		console.error("Sign out failed");
+	}
 };

39-39: Minor: pathname === "" is redundant.

Next.js usePathname() returns the pathname starting with /, so the empty string check is unnecessary. This is a minor cleanup.

-const isDashboard = pathname === "/" || pathname === "";
+const isDashboard = pathname === "/";
apps/admin/src/components/server-data-wrapper.tsx (1)

20-25: Remove unnecessary comment.

Per coding guidelines, avoid unnecessary code comments. The code is self-explanatory.

 useEffect(() => {
-	// Set initial data for all queries
 	initialData.forEach(({ queryKey, data }) => {
 		queryClient.setQueryData(queryKey, data);
 	});
 }, [queryClient, initialData]);
apps/api/src/routes/admin.ts (1)

66-138: Consider parallelizing independent database queries for better performance.

These 6 database queries are independent and could be executed concurrently using Promise.all(), potentially reducing response time significantly.

-// Total credits issued (completed credit top-ups, including bonuses)
-const [creditsRow] = await db()
-	.select({...})
-	.from(tables.transaction)
-	.where(...);
-
-const totalCreditsIssued = Number(creditsRow?.value ?? 0);
-
-// Total revenue...
-const [revenueRow] = await db()...
-// ... more sequential queries
+const [
+	[creditsRow],
+	[revenueRow],
+	[usageCostRow],
+	[signupsRow],
+	[verifiedRow],
+	[payingRow],
+] = await Promise.all([
+	db()
+		.select({
+			value: sql<number>`COALESCE(SUM(CAST(${tables.transaction.creditAmount} AS NUMERIC)), 0)`.as("value"),
+		})
+		.from(tables.transaction)
+		.where(and(eq(tables.transaction.type, "credit_topup"), eq(tables.transaction.status, "completed"))),
+	db()
+		.select({
+			value: sql<number>`COALESCE(SUM(CAST(${tables.transaction.amount} AS NUMERIC)), 0)`.as("value"),
+		})
+		.from(tables.transaction)
+		.where(eq(tables.transaction.status, "completed")),
+	db()
+		.select({
+			value: sql<number>`COALESCE(SUM(${tables.log.cost}), 0)`.as("value"),
+		})
+		.from(tables.log),
+	db()
+		.select({ count: sql<number>`COUNT(*)`.as("count") })
+		.from(tables.user),
+	db()
+		.select({ count: sql<number>`COUNT(*)`.as("count") })
+		.from(tables.user)
+		.where(eq(tables.user.emailVerified, true)),
+	db()
+		.select({
+			count: sql<number>`COUNT(DISTINCT ${tables.transaction.organizationId})`.as("count"),
+		})
+		.from(tables.transaction)
+		.where(eq(tables.transaction.status, "completed")),
+]);
apps/admin/src/lib/server-api.ts (2)

61-61: Avoid any for error type.

Consider using unknown or the specific error type from the OpenAPI client.

-let response: { data?: T; error?: any };
+let response: { data?: T; error?: unknown };

89-91: Consider logging more details for debugging.

The catch block logs a generic error message without the actual error details. Consider including the error for easier debugging.

-} catch {
-	console.error(`Server API error for ${method} ${path}`);
+} catch (error) {
+	console.error(`Server API error for ${method} ${path}:`, error);
 	return null;
 }
apps/admin/src/components/ui/sidebar.tsx (1)

531-536: Consider using a local variable instead of mutating the parameter.

Mutating the tooltip parameter triggers the eslint rule and makes the data flow less clear. A local variable avoids the need for the disable comment.

-	if (typeof tooltip === "string") {
-		// eslint-disable-next-line no-param-reassign
-		tooltip = {
-			children: tooltip,
-		};
-	}
+	const tooltipProps =
+		typeof tooltip === "string" ? { children: tooltip } : tooltip;

 	return (
 		<Tooltip>
 			<TooltipTrigger asChild>{button}</TooltipTrigger>
 			<TooltipContent
 				side="right"
 				align="center"
 				hidden={state !== "collapsed" || isMobile}
-				{...tooltip}
+				{...tooltipProps}
 			/>
 		</Tooltip>
 	);
apps/admin/package.json (1)

11-11: Consider clarifying the public directory handling in the build script.

The build script uses test -d public && cp -r public ... which will fail the build if the public directory doesn't exist. If public is optional, this should use || true to prevent build failures:

-		"build": "pnpm generate && tsc && next build --turbopack && mkdir -p .next/static && cp -r .next/static .next/standalone/apps/admin/.next/ && test -d public && cp -r public .next/standalone/apps/admin/",
+		"build": "pnpm generate && tsc && next build --turbopack && mkdir -p .next/static && cp -r .next/static .next/standalone/apps/admin/.next/ && (test -d public && cp -r public .next/standalone/apps/admin/ || true)",
apps/admin/src/components/ui/skeleton.tsx (1)

1-13: Consider extracting duplicated UI components to a shared package.

The Skeleton component is identical to apps/playground/src/components/ui/skeleton.tsx. If multiple UI components are duplicated across apps, consider creating a shared UI package (e.g., packages/ui) to apply DRY principles.

apps/admin/src/lib/fetch-client.ts (1)

9-21: Consider removing redundant comments.

The comments on lines 9 and 21 are self-evident from the function names. Per coding guidelines: "No unnecessary code comments".

-// React hook to get the fetch client
 export function useFetchClient() {
-// React hook to get the API client
 export function useApi() {
apps/admin/src/lib/getUser.ts (2)

12-12: Remove unnecessary code comment.

Per the coding guidelines, unnecessary code comments should be avoided. This comment doesn't add value beyond what the code clearly expresses.

-	// Get session cookie for authentication
 	const sessionCookie = cookieStore.get(`${key}`);

16-29: Consider handling fetch network errors.

The fetch call can throw on network failures (DNS resolution, connection refused, etc.). Currently, any exception will propagate up and may cause unexpected server errors.

-	const data = await fetch(`${config.apiBackendUrl}/user/me`, {
-		method: "GET",
-		headers: {
-			Cookie: secureSessionCookie
-				? `__Secure-${key}=${secureSessionCookie.value}`
-				: sessionCookie
-					? `${key}=${sessionCookie.value}`
-					: "",
-		},
-	});
-
-	if (!data.ok) {
-		return null;
-	}
+	try {
+		const data = await fetch(`${config.apiBackendUrl}/user/me`, {
+			method: "GET",
+			headers: {
+				Cookie: secureSessionCookie
+					? `__Secure-${key}=${secureSessionCookie.value}`
+					: sessionCookie
+						? `${key}=${sessionCookie.value}`
+						: "",
+			},
+		});
+
+		if (!data.ok) {
+			return null;
+		}
+
+		const user: User = await data.json();
+		return user;
+	} catch {
+		return null;
+	}
apps/admin/src/lib/auth-client.ts (2)

6-6: Remove unnecessary code comments.

The comments at lines 6 and 17 are redundant as the function names are already self-explanatory.

As per coding guidelines, avoid unnecessary code comments.

Apply this diff to remove the comments:

-// React hook to get the auth client
 export function useAuthClient() {
-// React hook for auth methods
 export function useAuth() {

Also applies to: 17-17


10-14: Normalize apiUrl to prevent double slashes.

The baseURL concatenates config.apiUrl + "/auth", which could result in a double slash if config.apiUrl ends with /. Add URL normalization to remove trailing slashes from config.apiUrl before concatenation, or use a utility like new URL() to safely construct the endpoint URL.

apps/admin/next.config.ts (1)

13-14: Remove commented-out code.

The commented-out turbopack configuration options should either be enabled or removed to keep the codebase clean.

As per coding guidelines, avoid keeping commented-out code.

Apply this diff:

 	experimental: {
-		// turbopackFileSystemCacheForDev: true,
-		// turbopackFileSystemCacheForBuild: true,
 	},
apps/admin/src/app/page.tsx (2)

42-51: Missing default styling when accent is undefined.

When no accent prop is provided, the icon container only receives base styles without any color theme. Consider adding a default/neutral accent style.

 						className={cn(
 							"inline-flex h-9 w-9 items-center justify-center rounded-full border text-xs",
+							!accent &&
+								"border-border bg-muted/50 text-muted-foreground",
 							accent === "green" &&
 								"border-emerald-500/30 bg-emerald-500/10 text-emerald-400",

16-59: Consider extracting MetricCard to a separate file.

The MetricCard component is self-contained and could be reused across other admin pages. Extracting it to @/components/metric-card.tsx would improve modularity.

apps/admin/src/lib/utils.ts (1)

8-54: Consider using unknown instead of any for better type safety.

The error handling logic correctly handles multiple error formats including Zod-OpenAPI structures. However, using unknown instead of any would provide better type safety while still accepting any error type.

Apply this diff to improve type safety:

-export function getErrorMessage(error: any): string {
+export function getErrorMessage(error: unknown): string {

As per coding guidelines, avoid any unless absolutely necessary. The unknown type serves the same purpose here while enforcing type checks within the function body.

apps/admin/middleware.ts (2)

17-25: Path exclusion logic is duplicated between middleware body and matcher config.

The if block (lines 17-25) checks paths like /login, /signup, /_next, while the matcher (line 80) excludes _next/static, _next/image, favicon.ico. These serve different purposes but the overlap with /_next and /favicon is confusing.

The matcher controls which requests invoke the middleware; the if block provides early-exit logic. Consider consolidating or documenting the distinction.

Also applies to: 79-81


70-74: Catch block swallows error context.

Returning a generic 403 for all errors (network failures, JSON parse errors, etc.) makes debugging difficult. Consider logging the error server-side or distinguishing between auth failures and infrastructure errors.

-	} catch {
+	} catch (err) {
+		console.error("Admin middleware error:", err);
 		return new NextResponse("Forbidden: admin access required", {
 			status: 403,
 		});
 	}
apps/admin/src/hooks/useUser.ts (2)

39-53: First useEffect has no side effects - appears to be dead/scaffold code.

This effect only contains early-return conditions but performs no action. If this is placeholder for future onboarding logic, consider adding a TODO comment or removing it to reduce confusion.

-	// Check for onboarding completion for all authenticated users
-	useEffect(() => {
-		if (!data?.user || isLoading) {
-			return;
-		}
-
-		const currentPath = pathname;
-		const isAuthPage = ["/login", "/signup"].includes(currentPath);
-		const isLandingPage = currentPath === "/";
-
-		// Don't redirect if already on auth pages
-		if (isAuthPage || isLandingPage) {
-			return;
-		}
-	}, [data?.user, isLoading, router, pathname]);

73-81: Redundant entries in dependency array.

The array includes both the options object and its destructured properties (options?.redirectTo, options?.redirectWhen). Since options already captures reference changes, the individual property checks are redundant and can trigger unnecessary re-renders if options is recreated.

 	}, [
 		data?.user,
 		isLoading,
 		error,
 		router,
-		options?.redirectTo,
-		options?.redirectWhen,
 		options,
 	]);
apps/admin/src/components/ui/input-group.tsx (1)

131-145: Consider using forwardRef for consistency with InputGroupTextarea.

InputGroupTextarea uses forwardRef (lines 147-163), but InputGroupInput does not. For consistency and to allow parent components to access the underlying input element directly, consider wrapping InputGroupInput with forwardRef as well.

-function InputGroupInput({
-	className,
-	...props
-}: React.ComponentProps<"input">) {
-	return (
+const InputGroupInput = React.forwardRef<
+	HTMLInputElement,
+	React.ComponentProps<"input">
+>(({ className, ...props }, ref) => {
+	return (
		<Input
+			ref={ref}
			data-slot="input-group-control"
			className={cn(
				"flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
				className,
			)}
			{...props}
		/>
	);
-}
+});
+InputGroupInput.displayName = "InputGroupInput";
apps/admin/src/components/ui/dropdown-menu.tsx (1)

34-52: DropdownMenuContent includes Portal internally—document to avoid double-portal.

DropdownMenuContent already wraps content in DropdownMenuPrimitive.Portal (line 40), while DropdownMenuPortal is also exported separately. This is fine for flexibility, but consumers should be aware that using both together would create nested portals. Consider adding a brief JSDoc comment.

apps/admin/src/components/ui/dialog.tsx (1)

57-59: Minor: Redundant data-slot attribute.

DialogPortal (line 24) already sets data-slot="dialog-portal", so passing it again on line 58 is redundant. The second one will override the first, so no functional issue.

-		<DialogPortal data-slot="dialog-portal">
+		<DialogPortal>
apps/admin/src/components/ui/sheet.tsx (2)

75-78: Missing data-slot attribute on close button.

The close button inside SheetContent doesn't have a data-slot="sheet-close" attribute, unlike the exported SheetClose component (line 22). This inconsistency could affect targeting for testing or styling.

-				<SheetPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">
+				<SheetPrimitive.Close data-slot="sheet-close" className="ring-offset-background focus:ring-ring data-[state=open]:bg-secondary absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none">

47-82: Consider adding showCloseButton prop for consistency with Dialog.

DialogContent has a showCloseButton prop to conditionally render the close button, but SheetContent always renders it. For API consistency across modal-like components, consider adding the same prop here.

apps/admin/src/lib/stripe.ts (1)

25-35: Guard against state updates after unmount and normalize the error type

The current effect is fine functionally, but if the component unmounts before getStripePromise() resolves, React can warn about setting state on an unmounted component. Also, err may not always be an Error instance.

You can make the hook more robust with a simple mounted flag and error normalization:

-	useEffect(() => {
-		getStripePromise()
-			.then((stripeInstance) => {
-				setStripe(stripeInstance);
-				setIsLoading(false);
-			})
-			.catch((err) => {
-				setError(err);
-				setIsLoading(false);
-			});
-	}, []);
+	useEffect(() => {
+		let isMounted = true;
+
+		getStripePromise()
+			.then((stripeInstance) => {
+				if (!isMounted) return;
+				setStripe(stripeInstance);
+				setIsLoading(false);
+			})
+			.catch((err) => {
+				if (!isMounted) return;
+				const normalizedError =
+					err instanceof Error ? err : new Error("Failed to load Stripe");
+				setError(normalizedError);
+				setIsLoading(false);
+			});
+
+		return () => {
+			isMounted = false;
+		};
+	}, []);

This keeps the existing API ({ stripe, isLoading, error }) but avoids potential React warnings and ensures error is always an Error.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e30142 and 92fdd80.

⛔ Files ignored due to path filters (9)
  • apps/admin/public/favicon/android-chrome-192x192.png is excluded by !**/*.png
  • apps/admin/public/favicon/android-chrome-512x512.png is excluded by !**/*.png
  • apps/admin/public/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/public/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/public/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/public/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/public/opengraph.png is excluded by !**/*.png
  • apps/admin/src/lib/api/v1.d.ts is excluded by !**/v1.d.ts
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (91)
  • .dockerignore (0 hunks)
  • .env.example (1 hunks)
  • .env.unified.example (1 hunks)
  • .github/start.sh (2 hunks)
  • .github/test-split-docker.sh (1 hunks)
  • .github/test-unified-docker.sh (1 hunks)
  • AGENTS.md (2 hunks)
  • CLAUDE.md (3 hunks)
  • README.md (1 hunks)
  • apps/admin/.gitignore (1 hunks)
  • apps/admin/.lintstagedrc.json (1 hunks)
  • apps/admin/.prettierignore (1 hunks)
  • apps/admin/README.md (1 hunks)
  • apps/admin/components.json (1 hunks)
  • apps/admin/eslint.config.mjs (1 hunks)
  • apps/admin/middleware.ts (1 hunks)
  • apps/admin/next.config.ts (1 hunks)
  • apps/admin/package.json (1 hunks)
  • apps/admin/postcss.config.mjs (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (1 hunks)
  • apps/admin/src/app/globals.css (1 hunks)
  • apps/admin/src/app/layout.tsx (1 hunks)
  • apps/admin/src/app/login/page.tsx (1 hunks)
  • apps/admin/src/app/page.tsx (1 hunks)
  • apps/admin/src/components/admin-shell.tsx (1 hunks)
  • apps/admin/src/components/auth/user-provider.tsx (1 hunks)
  • apps/admin/src/components/landing/theme-toggle.tsx (1 hunks)
  • apps/admin/src/components/server-data-wrapper.tsx (1 hunks)
  • apps/admin/src/components/ui/alert.tsx (1 hunks)
  • apps/admin/src/components/ui/avatar.tsx (1 hunks)
  • apps/admin/src/components/ui/badge.tsx (1 hunks)
  • apps/admin/src/components/ui/button.tsx (1 hunks)
  • apps/admin/src/components/ui/carousel.tsx (1 hunks)
  • apps/admin/src/components/ui/checkbox.tsx (1 hunks)
  • apps/admin/src/components/ui/collapsible.tsx (1 hunks)
  • apps/admin/src/components/ui/command.tsx (1 hunks)
  • apps/admin/src/components/ui/dialog.tsx (1 hunks)
  • apps/admin/src/components/ui/dropdown-menu.tsx (1 hunks)
  • apps/admin/src/components/ui/form.tsx (1 hunks)
  • apps/admin/src/components/ui/hover-card.tsx (1 hunks)
  • apps/admin/src/components/ui/input-group.tsx (1 hunks)
  • apps/admin/src/components/ui/input.tsx (1 hunks)
  • apps/admin/src/components/ui/label.tsx (1 hunks)
  • apps/admin/src/components/ui/logo.tsx (1 hunks)
  • apps/admin/src/components/ui/popover.tsx (1 hunks)
  • apps/admin/src/components/ui/progress.tsx (1 hunks)
  • apps/admin/src/components/ui/scroll-area.tsx (1 hunks)
  • apps/admin/src/components/ui/select.tsx (1 hunks)
  • apps/admin/src/components/ui/separator.tsx (1 hunks)
  • apps/admin/src/components/ui/sheet.tsx (1 hunks)
  • apps/admin/src/components/ui/sidebar.tsx (1 hunks)
  • apps/admin/src/components/ui/skeleton.tsx (1 hunks)
  • apps/admin/src/components/ui/sonner.tsx (1 hunks)
  • apps/admin/src/components/ui/tabs.tsx (1 hunks)
  • apps/admin/src/components/ui/textarea.tsx (1 hunks)
  • apps/admin/src/components/ui/tooltip.tsx (1 hunks)
  • apps/admin/src/hooks/use-mobile.ts (1 hunks)
  • apps/admin/src/hooks/useUser.ts (1 hunks)
  • apps/admin/src/lib/admin-metrics.ts (1 hunks)
  • apps/admin/src/lib/auth-client.ts (1 hunks)
  • apps/admin/src/lib/config-server.ts (1 hunks)
  • apps/admin/src/lib/config.tsx (1 hunks)
  • apps/admin/src/lib/fetch-client.ts (1 hunks)
  • apps/admin/src/lib/getUser.ts (1 hunks)
  • apps/admin/src/lib/providers.tsx (1 hunks)
  • apps/admin/src/lib/server-api.ts (1 hunks)
  • apps/admin/src/lib/stripe.ts (1 hunks)
  • apps/admin/src/lib/types.ts (1 hunks)
  • apps/admin/src/lib/utils.ts (1 hunks)
  • apps/admin/src/types/next-themes.d.ts (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/api/src/auth/config.ts (1 hunks)
  • apps/api/src/index.ts (1 hunks)
  • apps/api/src/routes/admin.ts (1 hunks)
  • apps/api/src/routes/index.ts (2 hunks)
  • apps/api/src/routes/user.ts (4 hunks)
  • apps/docs/app/api/proxy/route.ts (1 hunks)
  • apps/docs/content/self-host.mdx (1 hunks)
  • apps/gateway/src/app.ts (1 hunks)
  • apps/playground/src/lib/config-server.ts (2 hunks)
  • apps/ui/src/content/blog/2025-05-01-self-host-llm-gateway.md (2 hunks)
  • apps/ui/src/lib/config-server.ts (2 hunks)
  • infra/bunnyshell.yaml (1 hunks)
  • infra/docker-compose.split.local.yml (3 hunks)
  • infra/docker-compose.split.yml (3 hunks)
  • infra/docker-compose.unified.local.yml (2 hunks)
  • infra/docker-compose.unified.yml (2 hunks)
  • infra/supervisord.conf (1 hunks)
  • infra/unified.dockerfile (1 hunks)
  • package.json (1 hunks)
  • turbo.json (0 hunks)
💤 Files with no reviewable changes (2)
  • .dockerignore
  • turbo.json
🧰 Additional context used
📓 Path-based instructions (10)
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,jsx,tsx}: Always use top-level import, never use require or dynamic imports
No unnecessary code comments

Files:

  • apps/admin/next.config.ts
  • apps/gateway/src/app.ts
  • apps/ui/src/lib/config-server.ts
  • apps/admin/src/lib/types.ts
  • apps/api/src/auth/config.ts
  • apps/admin/src/components/ui/separator.tsx
  • apps/admin/src/components/ui/checkbox.tsx
  • apps/admin/src/types/next-themes.d.ts
  • apps/admin/src/lib/stripe.ts
  • apps/admin/src/components/ui/progress.tsx
  • apps/api/src/index.ts
  • apps/admin/src/components/ui/logo.tsx
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/auth/user-provider.tsx
  • apps/admin/src/components/server-data-wrapper.tsx
  • apps/admin/src/components/ui/button.tsx
  • apps/admin/src/hooks/use-mobile.ts
  • apps/admin/src/app/page.tsx
  • apps/admin/src/components/ui/collapsible.tsx
  • apps/admin/src/lib/providers.tsx
  • apps/admin/src/components/landing/theme-toggle.tsx
  • apps/admin/src/lib/fetch-client.ts
  • apps/admin/src/components/ui/alert.tsx
  • apps/admin/src/components/ui/avatar.tsx
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/admin/src/components/admin-shell.tsx
  • apps/admin/src/lib/admin-metrics.ts
  • apps/admin/src/lib/utils.ts
  • apps/admin/src/components/ui/input-group.tsx
  • apps/admin/src/lib/server-api.ts
  • apps/api/src/routes/index.ts
  • apps/admin/src/components/ui/scroll-area.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/src/components/ui/tooltip.tsx
  • apps/admin/src/components/ui/badge.tsx
  • apps/admin/src/lib/auth-client.ts
  • apps/admin/src/components/ui/hover-card.tsx
  • apps/admin/src/app/login/page.tsx
  • apps/admin/src/lib/config-server.ts
  • apps/admin/src/components/ui/skeleton.tsx
  • apps/admin/src/hooks/useUser.ts
  • apps/admin/src/components/ui/dropdown-menu.tsx
  • apps/docs/app/api/proxy/route.ts
  • apps/admin/src/lib/config.tsx
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/src/lib/getUser.ts
  • apps/admin/src/components/ui/command.tsx
  • apps/admin/src/components/ui/sheet.tsx
  • apps/admin/src/components/ui/textarea.tsx
  • apps/admin/src/components/ui/select.tsx
  • apps/admin/src/components/ui/popover.tsx
  • apps/playground/src/lib/config-server.ts
  • apps/admin/middleware.ts
  • apps/admin/src/components/ui/sidebar.tsx
  • apps/admin/src/components/ui/form.tsx
  • apps/admin/src/components/ui/sonner.tsx
  • apps/admin/src/components/ui/dialog.tsx
  • apps/admin/src/components/ui/label.tsx
  • apps/admin/src/components/ui/input.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use cookies for user-settings which are not saved in the database to ensure SSR works

**/*.{ts,tsx,js,jsx}: Always use tabs for indentation
Always use top-level import, never use require or dynamic imports
Avoid unnecessary code comments

Files:

  • apps/admin/next.config.ts
  • apps/gateway/src/app.ts
  • apps/ui/src/lib/config-server.ts
  • apps/admin/src/lib/types.ts
  • apps/api/src/auth/config.ts
  • apps/admin/src/components/ui/separator.tsx
  • apps/admin/src/components/ui/checkbox.tsx
  • apps/admin/src/types/next-themes.d.ts
  • apps/admin/src/lib/stripe.ts
  • apps/admin/src/components/ui/progress.tsx
  • apps/api/src/index.ts
  • apps/admin/src/components/ui/logo.tsx
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/auth/user-provider.tsx
  • apps/admin/src/components/server-data-wrapper.tsx
  • apps/admin/src/components/ui/button.tsx
  • apps/admin/src/hooks/use-mobile.ts
  • apps/admin/src/app/page.tsx
  • apps/admin/src/components/ui/collapsible.tsx
  • apps/admin/src/lib/providers.tsx
  • apps/admin/src/components/landing/theme-toggle.tsx
  • apps/admin/src/lib/fetch-client.ts
  • apps/admin/src/components/ui/alert.tsx
  • apps/admin/src/components/ui/avatar.tsx
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/admin/src/components/admin-shell.tsx
  • apps/admin/src/lib/admin-metrics.ts
  • apps/admin/src/lib/utils.ts
  • apps/admin/src/components/ui/input-group.tsx
  • apps/admin/src/lib/server-api.ts
  • apps/api/src/routes/index.ts
  • apps/admin/src/components/ui/scroll-area.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/src/components/ui/tooltip.tsx
  • apps/admin/src/components/ui/badge.tsx
  • apps/admin/src/lib/auth-client.ts
  • apps/admin/src/components/ui/hover-card.tsx
  • apps/admin/src/app/login/page.tsx
  • apps/admin/src/lib/config-server.ts
  • apps/admin/src/components/ui/skeleton.tsx
  • apps/admin/src/hooks/useUser.ts
  • apps/admin/src/components/ui/dropdown-menu.tsx
  • apps/docs/app/api/proxy/route.ts
  • apps/admin/src/lib/config.tsx
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/src/lib/getUser.ts
  • apps/admin/src/components/ui/command.tsx
  • apps/admin/src/components/ui/sheet.tsx
  • apps/admin/src/components/ui/textarea.tsx
  • apps/admin/src/components/ui/select.tsx
  • apps/admin/src/components/ui/popover.tsx
  • apps/playground/src/lib/config-server.ts
  • apps/admin/middleware.ts
  • apps/admin/src/components/ui/sidebar.tsx
  • apps/admin/src/components/ui/form.tsx
  • apps/admin/src/components/ui/sonner.tsx
  • apps/admin/src/components/ui/dialog.tsx
  • apps/admin/src/components/ui/label.tsx
  • apps/admin/src/components/ui/input.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use Drizzle ORM with latest object syntax for database operations
For database reads: Use db().query.<table>.findMany() or db().query.<table>.findFirst()

Never use any or as any in TypeScript code unless absolutely necessary

Files:

  • apps/admin/next.config.ts
  • apps/gateway/src/app.ts
  • apps/ui/src/lib/config-server.ts
  • apps/admin/src/lib/types.ts
  • apps/api/src/auth/config.ts
  • apps/admin/src/components/ui/separator.tsx
  • apps/admin/src/components/ui/checkbox.tsx
  • apps/admin/src/types/next-themes.d.ts
  • apps/admin/src/lib/stripe.ts
  • apps/admin/src/components/ui/progress.tsx
  • apps/api/src/index.ts
  • apps/admin/src/components/ui/logo.tsx
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/auth/user-provider.tsx
  • apps/admin/src/components/server-data-wrapper.tsx
  • apps/admin/src/components/ui/button.tsx
  • apps/admin/src/hooks/use-mobile.ts
  • apps/admin/src/app/page.tsx
  • apps/admin/src/components/ui/collapsible.tsx
  • apps/admin/src/lib/providers.tsx
  • apps/admin/src/components/landing/theme-toggle.tsx
  • apps/admin/src/lib/fetch-client.ts
  • apps/admin/src/components/ui/alert.tsx
  • apps/admin/src/components/ui/avatar.tsx
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/admin/src/components/admin-shell.tsx
  • apps/admin/src/lib/admin-metrics.ts
  • apps/admin/src/lib/utils.ts
  • apps/admin/src/components/ui/input-group.tsx
  • apps/admin/src/lib/server-api.ts
  • apps/api/src/routes/index.ts
  • apps/admin/src/components/ui/scroll-area.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/src/components/ui/tooltip.tsx
  • apps/admin/src/components/ui/badge.tsx
  • apps/admin/src/lib/auth-client.ts
  • apps/admin/src/components/ui/hover-card.tsx
  • apps/admin/src/app/login/page.tsx
  • apps/admin/src/lib/config-server.ts
  • apps/admin/src/components/ui/skeleton.tsx
  • apps/admin/src/hooks/useUser.ts
  • apps/admin/src/components/ui/dropdown-menu.tsx
  • apps/docs/app/api/proxy/route.ts
  • apps/admin/src/lib/config.tsx
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/src/lib/getUser.ts
  • apps/admin/src/components/ui/command.tsx
  • apps/admin/src/components/ui/sheet.tsx
  • apps/admin/src/components/ui/textarea.tsx
  • apps/admin/src/components/ui/select.tsx
  • apps/admin/src/components/ui/popover.tsx
  • apps/playground/src/lib/config-server.ts
  • apps/admin/middleware.ts
  • apps/admin/src/components/ui/sidebar.tsx
  • apps/admin/src/components/ui/form.tsx
  • apps/admin/src/components/ui/sonner.tsx
  • apps/admin/src/components/ui/dialog.tsx
  • apps/admin/src/components/ui/label.tsx
  • apps/admin/src/components/ui/input.tsx
**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

For database reads, use db().query.<table>.findMany() or db().query.<table>.findFirst() with Drizzle ORM

Files:

  • apps/admin/next.config.ts
  • apps/gateway/src/app.ts
  • apps/ui/src/lib/config-server.ts
  • apps/admin/src/lib/types.ts
  • apps/api/src/auth/config.ts
  • apps/admin/src/types/next-themes.d.ts
  • apps/admin/src/lib/stripe.ts
  • apps/api/src/index.ts
  • apps/admin/src/hooks/use-mobile.ts
  • apps/admin/src/lib/fetch-client.ts
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/admin/src/lib/admin-metrics.ts
  • apps/admin/src/lib/utils.ts
  • apps/admin/src/lib/server-api.ts
  • apps/api/src/routes/index.ts
  • apps/admin/src/lib/auth-client.ts
  • apps/admin/src/lib/config-server.ts
  • apps/admin/src/hooks/useUser.ts
  • apps/docs/app/api/proxy/route.ts
  • apps/admin/src/lib/getUser.ts
  • apps/playground/src/lib/config-server.ts
  • apps/admin/middleware.ts
apps/{gateway,api}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Zod schemas for validation in Hono applications

Files:

  • apps/gateway/src/app.ts
  • apps/api/src/auth/config.ts
  • apps/api/src/index.ts
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/api/src/routes/index.ts
apps/{gateway,api}/src/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

apps/{gateway,api}/src/**/*.ts: Run pnpm build if API routes were modified
Use Zod schemas for validation in Hono applications

Files:

  • apps/gateway/src/app.ts
  • apps/api/src/auth/config.ts
  • apps/api/src/index.ts
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/api/src/routes/index.ts
apps/ui/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use Next.js App Router with React Server Components for frontend development

Files:

  • apps/ui/src/lib/config-server.ts
apps/{ui,playground}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use next/link for links and next/navigation's router for programmatic navigation

Files:

  • apps/ui/src/lib/config-server.ts
  • apps/playground/src/lib/config-server.ts
apps/{ui,playground}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use next/link for links and next/navigation router for programmatic navigation in Next.js applications

Files:

  • apps/ui/src/lib/config-server.ts
  • apps/playground/src/lib/config-server.ts
apps/{ui,playground,api}/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use cookies for user-settings that are not saved in the database to ensure SSR works

Files:

  • apps/ui/src/lib/config-server.ts
  • apps/api/src/auth/config.ts
  • apps/api/src/index.ts
  • apps/api/src/routes/admin.ts
  • apps/api/src/routes/user.ts
  • apps/api/src/routes/index.ts
  • apps/playground/src/lib/config-server.ts
🧠 Learnings (18)
📚 Learning: 2025-11-24T20:02:21.810Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.810Z
Learning: Applies to apps/ui/**/*.{ts,tsx} : Use Next.js App Router with React Server Components for frontend development

Applied to files:

  • apps/admin/next.config.ts
  • apps/admin/src/components/ui/separator.tsx
  • apps/admin/src/components/ui/checkbox.tsx
  • apps/admin/src/components/ui/progress.tsx
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/server-data-wrapper.tsx
  • apps/admin/src/components/ui/collapsible.tsx
  • apps/admin/src/components/admin-shell.tsx
  • apps/admin/src/components/ui/scroll-area.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/README.md
  • apps/admin/src/app/login/page.tsx
  • apps/admin/src/components/ui/skeleton.tsx
  • apps/admin/src/hooks/useUser.ts
  • apps/admin/src/components/ui/dropdown-menu.tsx
  • apps/admin/src/lib/config.tsx
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/src/components/ui/select.tsx
  • apps/admin/middleware.ts
  • apps/admin/src/components/ui/sidebar.tsx
📚 Learning: 2025-11-28T15:24:54.183Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.183Z
Learning: Applies to apps/{ui,playground}/src/**/*.{ts,tsx} : Use `next/link` for links and `next/navigation` router for programmatic navigation in Next.js applications

Applied to files:

  • apps/admin/next.config.ts
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/README.md
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/middleware.ts
📚 Learning: 2025-11-24T20:02:21.810Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.810Z
Learning: Applies to apps/{ui,playground}/**/*.{ts,tsx} : Use `next/link` for links and `next/navigation`'s router for programmatic navigation

Applied to files:

  • apps/admin/next.config.ts
  • apps/admin/src/app/layout.tsx
  • apps/admin/src/components/ui/tabs.tsx
  • apps/admin/src/components/ui/carousel.tsx
  • apps/admin/middleware.ts
📚 Learning: 2025-11-28T15:24:54.183Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.183Z
Learning: Applies to apps/{gateway,api}/src/**/*.ts : Run `pnpm build` if API routes were modified

Applied to files:

  • apps/gateway/src/app.ts
  • AGENTS.md
  • apps/admin/.lintstagedrc.json
  • CLAUDE.md
  • apps/api/src/routes/index.ts
📚 Learning: 2025-11-24T20:02:21.811Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.811Z
Learning: Run `pnpm build` to ensure production builds work

Applied to files:

  • AGENTS.md
  • apps/admin/.lintstagedrc.json
  • CLAUDE.md
📚 Learning: 2025-11-28T15:24:54.184Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.184Z
Learning: Run `pnpm build` after finishing work on a feature to ensure production builds work

Applied to files:

  • AGENTS.md
  • CLAUDE.md
📚 Learning: 2025-11-24T20:02:21.810Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.810Z
Learning: Always use pnpm for package management

Applied to files:

  • AGENTS.md
  • apps/admin/.lintstagedrc.json
  • CLAUDE.md
📚 Learning: 2025-11-24T20:02:21.811Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.811Z
Learning: Run `pnpm format` after code changes

Applied to files:

  • AGENTS.md
  • apps/admin/.lintstagedrc.json
  • CLAUDE.md
📚 Learning: 2025-11-28T15:24:54.184Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.184Z
Learning: Always run `pnpm format` before committing code to ensure consistent formatting and linting

Applied to files:

  • AGENTS.md
  • apps/admin/.lintstagedrc.json
  • CLAUDE.md
📚 Learning: 2025-11-24T20:02:21.811Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.811Z
Learning: Run `pnpm test:unit` and `pnpm test:e2e` after adding features

Applied to files:

  • AGENTS.md
  • CLAUDE.md
📚 Learning: 2025-11-24T20:02:21.811Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.811Z
Learning: For schema changes: Use `pnpm run setup` instead of writing migrations which will generate .sql files, and always sync schema with `pnpm run setup` after table/column changes

Applied to files:

  • AGENTS.md
  • CLAUDE.md
📚 Learning: 2025-11-28T15:24:54.183Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.183Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Always use tabs for indentation

Applied to files:

  • apps/admin/.lintstagedrc.json
📚 Learning: 2025-11-24T20:02:21.810Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.810Z
Learning: Applies to **/*.{js,ts,jsx,tsx} : No unnecessary code comments

Applied to files:

  • apps/admin/.lintstagedrc.json
📚 Learning: 2025-09-22T18:30:32.055Z
Learnt from: smakosh
Repo: theopenco/llmgateway PR: 911
File: apps/ui/src/lib/components/tweet-card.tsx:170-174
Timestamp: 2025-09-22T18:30:32.055Z
Learning: In the tweet-card component at apps/ui/src/lib/components/tweet-card.tsx, the use of dangerouslySetInnerHTML for rendering tweet entity text is acceptable. The react-tweet library content is considered safe to render as HTML in this context.

Applied to files:

  • apps/admin/src/components/ui/hover-card.tsx
📚 Learning: 2025-09-22T18:29:26.406Z
Learnt from: smakosh
Repo: theopenco/llmgateway PR: 911
File: apps/ui/src/lib/components/tweet-card.tsx:244-255
Timestamp: 2025-09-22T18:29:26.406Z
Learning: In the tweet-card component at apps/ui/src/lib/components/tweet-card.tsx, the TweetMedia component is intentionally not used in the MagicTweet component. This is a deliberate design decision to keep testimonials text-focused without rendering images or videos from tweets.

Applied to files:

  • apps/admin/src/components/ui/hover-card.tsx
📚 Learning: 2025-11-28T15:24:54.183Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.183Z
Learning: Applies to apps/{ui,playground,api}/src/**/*.{ts,tsx} : Use cookies for user-settings that are not saved in the database to ensure SSR works

Applied to files:

  • apps/admin/src/lib/getUser.ts
  • apps/admin/middleware.ts
📚 Learning: 2025-11-24T20:02:21.810Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.810Z
Learning: Applies to **/*.{ts,tsx,js,jsx} : Use cookies for user-settings which are not saved in the database to ensure SSR works

Applied to files:

  • apps/admin/src/lib/getUser.ts
  • apps/admin/middleware.ts
📚 Learning: 2025-11-28T15:24:54.183Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T15:24:54.183Z
Learning: Applies to packages/**/*.ts : Use DRY principles for code reuse across the monorepo with shared packages

Applied to files:

  • apps/admin/tsconfig.json
🧬 Code graph analysis (8)
apps/admin/src/lib/stripe.ts (1)
apps/api/src/routes/payments.ts (1)
  • stripe (13-18)
apps/admin/src/app/layout.tsx (1)
apps/admin/src/components/admin-shell.tsx (1)
  • AdminShell (33-116)
apps/admin/src/components/auth/user-provider.tsx (1)
apps/playground/src/components/auth/user-provider.tsx (1)
  • UserProvider (15-27)
apps/admin/src/app/page.tsx (1)
apps/admin/src/lib/admin-metrics.ts (1)
  • getAdminDashboardMetrics (14-25)
apps/api/src/routes/user.ts (1)
packages/db/src/schema.ts (1)
  • user (41-53)
apps/api/src/routes/index.ts (1)
apps/api/src/routes/admin.ts (1)
  • admin (9-9)
apps/admin/src/components/ui/skeleton.tsx (1)
apps/playground/src/components/ui/skeleton.tsx (1)
  • Skeleton (3-11)
apps/admin/middleware.ts (1)
packages/db/src/schema.ts (1)
  • session (55-73)
🪛 dotenv-linter (4.0.0)
.env.unified.example

[warning] 21-21: [UnorderedKey] The ADMIN_URL key should go before the DOCS_URL key

(UnorderedKey)

.env.example

[warning] 35-35: [UnorderedKey] The ADMIN_PORT key should go before the API_PORT key

(UnorderedKey)

🪛 GitHub Actions: ci
apps/admin/package.json

[error] 1-1: Lockfile mismatch detected: package.json specifiers do not match the lockfile. Run 'pnpm install' or 'pnpm install --no-frozen-lockfile' to update the lockfile.

package.json

[error] 1-1: Lockfile mismatch detected: package.json specifiers do not match the lockfile. Run 'pnpm install' or 'pnpm install --no-frozen-lockfile' to update the lockfile.

🪛 GitHub Actions: e2e
apps/admin/package.json

[error] 1-1: pnpm install failed with --frozen-lockfile: pnpm-lock.yaml is not up to date with apps/admin/package.json. Run 'pnpm install' to update the lockfile. 1 dependencies were removed: @llmgateway/db@workspace:*

package.json

[error] 1-1: pnpm install failed with --frozen-lockfile: pnpm-lock.yaml is not up to date with apps/admin/package.json. Run 'pnpm install' to update the lockfile. 1 dependencies were removed: @llmgateway/db@workspace:*

🪛 markdownlint-cli2 (0.18.1)
AGENTS.md

138-138: Bare URL used

(MD034, no-bare-urls)

apps/ui/src/content/blog/2025-05-01-self-host-llm-gateway.md

43-43: Bare URL used

(MD034, no-bare-urls)

CLAUDE.md

148-148: Bare URL used

(MD034, no-bare-urls)

⏰ 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). (1)
  • GitHub Check: autofix

Comment thread apps/admin/next.config.ts
Comment thread apps/admin/package.json
Comment thread apps/admin/public/favicon/site.webmanifest
Comment on lines +42 to +53
export default function Login() {
const queryClient = useQueryClient();
const router = useRouter();
const searchParams = useSearchParams();
const [isLoading, setIsLoading] = useState(false);
const { signIn } = useAuth();
const returnUrl = getSafeRedirectUrl(searchParams.get("returnUrl"));

useUser({
redirectTo: returnUrl,
redirectWhen: "authenticated",
});

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

🧩 Analysis chain

🏁 Script executed:

head -50 apps/admin/src/app/login/page.tsx

Repository: theopenco/llmgateway

Length of output: 1407


🌐 Web query:

Next.js App Router useSearchParams Suspense boundary requirement 2025

💡 Result:

Short answer: As of 2025 you must wrap any Client Component that calls useSearchParams() in a React Suspense boundary when the route is statically prerendered — otherwise the build will fail with the "Missing Suspense boundary with useSearchParams" error. If you don't want that behavior you can make the route dynamic (e.g., use connection in a Server Component) or pass the page's searchParams prop into the Client Component instead. [1][2][3]

Key sources:

  • Next.js useSearchParams docs (behavior + recommendation to wrap in ). [1]
  • Next.js error doc "Missing Suspense boundary with useSearchParams" (explains build failure and fixes). [2]
  • Active Next.js issue/discussion showing the requirement and user reports in 2024–2025. [3]

If you want, I can show a minimal example (Suspense wrapper, or passing searchParams from the Page) or help update your code to fix the build error.


Wrap useSearchParams with a Suspense boundary to prevent build failure.

In Next.js App Router, Client Components that call useSearchParams() must be wrapped in a Suspense boundary when the route is statically prerendered. Without it, the build will fail with "Missing Suspense boundary with useSearchParams" error. Either wrap the component using <Suspense>, make the route dynamic, or pass searchParams from the parent Server Component.

🤖 Prompt for AI Agents
In apps/admin/src/app/login/page.tsx around lines 42 to 53, the call to
useSearchParams() in this Client Component will break static prerendering; wrap
the component (or the part that calls useSearchParams) in a React.Suspense
boundary so Next.js can handle the client-side params, or alternatively obtain
searchParams in a parent Server Component and pass them down (or make the route
dynamic). Concretely: move useSearchParams usage into a child component wrapped
with <Suspense fallback={...}> (or pass searchParams from the server) and keep
the rest of Login as-is to avoid the "Missing Suspense boundary with
useSearchParams" build error.

Comment thread apps/admin/src/app/login/page.tsx
Comment on lines +53 to +57
export async function fetchServerData<T>(
method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
path: keyof paths,
options?: any,
): Promise<T | null> {

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.

🛠️ Refactor suggestion | 🟠 Major

Avoid any type for options parameter.

As per coding guidelines, any should not be used. Consider using the proper type from openapi-fetch or a more specific type.

+import type { FetchOptions } from "openapi-fetch";
+
 export async function fetchServerData<T>(
 	method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH",
 	path: keyof paths,
-	options?: any,
+	options?: FetchOptions<unknown>,
 ): Promise<T | null> {

If FetchOptions doesn't fit your needs, consider at minimum using unknown or defining a specific interface for the expected options shape.

🤖 Prompt for AI Agents
In apps/admin/src/lib/server-api.ts around lines 53 to 57, the options parameter
is typed as any which violates the guideline; replace any with a concrete type
(preferably the FetchOptions type from openapi-fetch) by importing that type and
updating the function signature to options?: FetchOptions; if FetchOptions is
unsuitable, type options as unknown or define a small interface describing the
exact fields used (e.g., body, headers, signal) and update all call sites to
pass the correctly shaped object or to cast where necessary; ensure imports and
any downstream usages are adjusted to the new type so the code compiles without
using any.

Comment thread apps/admin/src/lib/stripe.ts
Comment thread apps/api/src/routes/user.ts
Comment thread infra/docker-compose.split.local.yml
Comment thread infra/docker-compose.split.yml

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0384cc4 and c27710a.

📒 Files selected for processing (1)
  • .github/workflows/images.yml (3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.822Z
Learning: Applies to apps/ui/**/*.{ts,tsx} : Use Next.js App Router with React Server Components for frontend development
⏰ 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). (16)
  • GitHub Check: e2e-shards (1)
  • GitHub Check: e2e-shards (5)
  • GitHub Check: e2e-shards (4)
  • GitHub Check: e2e-shards (3)
  • GitHub Check: e2e-shards (2)
  • GitHub Check: build-split (docs, linux/amd64)
  • GitHub Check: build-split (playground, linux/amd64)
  • GitHub Check: build-split (ui, linux/amd64)
  • GitHub Check: build-split (worker, linux/amd64)
  • GitHub Check: build-split (gateway, linux/amd64)
  • GitHub Check: build-split (api, linux/amd64)
  • GitHub Check: build-unified (linux/amd64)
  • GitHub Check: build / run
  • GitHub Check: lint / run
  • GitHub Check: test / run
  • GitHub Check: autofix

Comment thread .github/workflows/images.yml
Added API_URL and API_BACKEND_URL to the environment
variables in both docker-compose files for improved
configuration flexibility.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c27710a and cb13c6c.

📒 Files selected for processing (3)
  • infra/docker-compose.split.local.yml (3 hunks)
  • infra/docker-compose.split.yml (3 hunks)
  • infra/split.dockerfile (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • infra/docker-compose.split.yml
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.822Z
Learning: Applies to apps/ui/**/*.{ts,tsx} : Use Next.js App Router with React Server Components for frontend development
📚 Learning: 2025-11-24T20:02:21.822Z
Learnt from: CR
Repo: theopenco/llmgateway PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-11-24T20:02:21.822Z
Learning: Run `pnpm build` to ensure production builds work

Applied to files:

  • infra/split.dockerfile
🪛 Hadolint (2.14.0)
infra/split.dockerfile

[error] 111-111: COPY with more than 2 arguments requires the last argument to end with /

(DL3021)


[error] 112-112: COPY with more than 2 arguments requires the last argument to end with /

(DL3021)

⏰ 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). (17)
  • GitHub Check: build-split (admin, linux/amd64)
  • GitHub Check: build-split (ui, linux/amd64)
  • GitHub Check: build-split (playground, linux/amd64)
  • GitHub Check: build-split (api, linux/amd64)
  • GitHub Check: build-split (docs, linux/amd64)
  • GitHub Check: build-split (gateway, linux/amd64)
  • GitHub Check: build-unified (linux/amd64)
  • GitHub Check: test / run
  • GitHub Check: build / run
  • GitHub Check: lint / run
  • GitHub Check: generate / run
  • GitHub Check: e2e-shards (3)
  • GitHub Check: e2e-shards (5)
  • GitHub Check: e2e-shards (4)
  • GitHub Check: e2e-shards (1)
  • GitHub Check: e2e-shards (2)
  • GitHub Check: autofix
🔇 Additional comments (4)
infra/docker-compose.split.local.yml (2)

127-127: LGTM! ADMIN_URL propagated correctly to all services.

The ADMIN_URL environment variable has been consistently added to the ui (line 127), playground (line 162), and docs (line 226) services with the correct default value. This enables these services to reference the admin dashboard URL as needed.

Also applies to: 162-162, 226-226


168-198: LGTM! Past issue about missing API_URL and API_BACKEND_URL has been resolved.

The admin service now includes the required API_URL and API_BACKEND_URL environment variables (lines 194–195). The service configuration is complete with proper build context, port mapping, healthcheck, and network settings, following the established pattern used by other frontend services.

infra/split.dockerfile (2)

108-115: Admin-builder stage structure is consistent and well-integrated.

The admin-builder follows the established multi-stage build pattern used by other services (api, gateway, ui, playground, worker, docs). Cache mounts, pnpm filter isolation, and build invocation are all correct.


237-252: Admin runtime stage correctly mirrors Next.js app patterns.

The admin runtime stage properly replicates the structure of other Next.js applications (ui, playground, docs): standalone output copy, environment configuration, and working directory setup. Port configuration (PORT=80, EXPOSE 80) is consistent with other Next.js runtimes; external port mapping (3006) should be handled via docker-compose or orchestration layer.

Comment thread infra/split.dockerfile
steebchen and others added 3 commits December 3, 2025 01:35
Add docker image prune after each build to prevent disk space
exhaustion when building multiple images sequentially.

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

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove unused system tools (.NET, Android, GHC, CodeQL)
- Clean all Docker resources before building
- Add disk space monitoring after each build
- This should resolve ENOSPC errors in CI

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

Co-Authored-By: Claude <noreply@anthropic.com>
@steebchen
steebchen merged commit b4896c7 into main Dec 3, 2025
28 checks passed
@steebchen
steebchen deleted the feat/admin-dashboard branch December 3, 2025 12:41
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.

2 participants