Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
**/.pnpm-store
**/.idea
**/openapi.json
**/.vinxi
**/.next
**/.next-dev
**/.vercel
Expand Down
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ API_PORT=4002
UI_PORT=3002
PLAYGROUND_PORT=3003
DOCS_PORT=3005
ADMIN_PORT=3006

# Health check timeout in milliseconds (defaults to 5000ms)
TIMEOUT_MS=5000
Expand Down
1 change: 1 addition & 0 deletions .env.unified.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ AUTH_SECRET=your-secret-key-here
UI_URL=http://localhost:3002
PLAYGROUND_URL=http://localhost:3003
DOCS_URL=http://localhost:3005
ADMIN_URL=http://localhost:3006
API_URL=http://localhost:4002
API_BACKEND_URL=http://localhost:4002
ORIGIN_URLS=http://localhost:3002
Expand Down
3 changes: 2 additions & 1 deletion .github/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ YELLOW='\033[0;33m'
NC='\033[0m' # No Color

# Array of apps to test
APPS=("api" "gateway" "playground" "ui" "docs")
APPS=("api" "gateway" "playground" "ui" "docs" "admin")

# Ports for each app
declare -A APP_PORTS
Expand All @@ -17,6 +17,7 @@ APP_PORTS["gateway"]=4001
APP_PORTS["ui"]=3002
APP_PORTS["playground"]=3003
APP_PORTS["docs"]=3005
APP_PORTS["admin"]=3006

# Health check routes for each app (optional)
declare -A HEALTH_ROUTES
Expand Down
1 change: 1 addition & 0 deletions .github/test-split-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ APP_ENDPOINTS["gateway"]="http://localhost:4001"
APP_ENDPOINTS["ui"]="http://localhost:3002"
APP_ENDPOINTS["playground"]="http://localhost:3003"
APP_ENDPOINTS["docs"]="http://localhost:3005"
APP_ENDPOINTS["admin"]="http://localhost:3006/login"

# Health check routes for each app (optional)
declare -A HEALTH_ROUTES
Expand Down
1 change: 1 addition & 0 deletions .github/test-unified-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ APP_ENDPOINTS["gateway"]="http://localhost:4001"
APP_ENDPOINTS["ui"]="http://localhost:3002"
APP_ENDPOINTS["playground"]="http://localhost:3003"
APP_ENDPOINTS["docs"]="http://localhost:3005"
APP_ENDPOINTS["admin"]="http://localhost:3006/login"

# Health check routes for each app (optional)
declare -A HEALTH_ROUTES
Expand Down
29 changes: 26 additions & 3 deletions .github/workflows/images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
strategy:
fail-fast: false
matrix:
app: [api, gateway, playground, ui, docs, worker]
app: [api, gateway, playground, ui, docs, worker, admin]
Comment thread
steebchen marked this conversation as resolved.
platform: ${{ github.event_name == 'pull_request' && fromJSON('["linux/amd64"]') || fromJSON('["linux/amd64", "linux/arm64"]') }}
permissions:
contents: read
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
if: needs.setup.result == 'success' && github.event_name != 'pull_request'
strategy:
matrix:
app: [api, gateway, playground, ui, docs, worker]
app: [api, gateway, playground, ui, docs, worker, admin]
permissions:
contents: read
packages: write
Expand Down Expand Up @@ -187,6 +187,23 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

- name: Free up disk space
run: |
echo "Disk space before cleanup:"
df -h

# Remove unnecessary tools and files
sudo rm -rf /usr/share/dotnet
sudo rm -rf /usr/local/lib/android
sudo rm -rf /opt/ghc
sudo rm -rf /opt/hostedtoolcache/CodeQL

# Clean up docker
docker system prune -af --volumes

echo "Disk space after cleanup:"
df -h

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

Expand All @@ -201,14 +218,20 @@ jobs:
- name: Build all split Docker images for testing
run: |
# Build all split images sequentially
for app in api gateway playground ui docs worker; do
for app in api gateway playground ui docs worker admin; do
echo "Building $app image..."
docker build \
-f infra/split.dockerfile \
--target $app \
--build-arg APP_VERSION=${{ github.event_name == 'pull_request' && 'pr-test' || needs.setup.outputs.image_tag }} \
-t ghcr.io/${{ github.repository }}-$app:${{ github.event_name == 'pull_request' && 'pr-test' || needs.setup.outputs.image_tag }} \
.

# Clean up intermediate build layers to free space
docker image prune -f

echo "Disk space after building $app:"
df -h / | tail -1
done

- name: Test split Docker images
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This file provides guidance to AI agents when working with code in this reposito

NOTE: these commands can only be run in the root directory of the repository, not in individual app directories.

- `pnpm dev` - Start all development servers (UI on :3002, Playground on :3003, API on :4002, Gateway on :4001, Docs on :3005)
- `pnpm dev` - Start all development servers (UI on :3002, Playground on :3003, API on :4002, Gateway on :4001, Docs on :3005, Admin on :3006)
- `pnpm build` - Build all applications for production
- `pnpm clean` - Clean build artifacts and cache directories

Expand Down Expand Up @@ -135,6 +135,7 @@ NOTE: these commands can only be run in the root directory of the repository, no
- UI: http://localhost:3002
- Playground: http://localhost:3003
- Docs: http://localhost:3005
- Admin: http://localhost:3006
- API: http://localhost:4002
- Gateway: http://localhost:4001
- PostgreSQL: localhost:5432
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ This file provides guidance to AI agents when working with code in this reposito

NOTE: these commands can only be run in the root directory of the repository, not in individual app directories.

- `pnpm dev` - Start all development servers (UI on :3002, Playground on :3003, API on :4002, Gateway on :4001, Docs on :3005)
- `pnpm dev` - Start all development servers (UI on :3002, Playground on :3003, API on :4002, Gateway on :4001, Docs on :3005, Admin on :3006)
- `pnpm build` - Build all applications for production. Always run this after finishing work on a feature.
- `pnpm clean` - Clean build artifacts and cache directories

Expand Down Expand Up @@ -145,6 +145,7 @@ NOTE: these commands can only be run in the root directory of the repository, no
- API: http://localhost:4002
- Gateway: http://localhost:4001
- Docs: http://localhost:3005
- Admin: http://localhost:3006
- PostgreSQL: localhost:5432
- Redis: localhost:6379

Expand All @@ -155,6 +156,7 @@ NOTE: these commands can only be run in the root directory of the repository, no
- `apps/api`: Hono backend
- `apps/gateway`: API gateway for routing LLM requests
- `apps/docs`: Documentation site
- `apps/admin`: Internal Admin Dashboard
- `packages/db`: Drizzle ORM schema and migrations
- `packages/models`: Model and provider definitions
- `packages/shared`: Shared types and utilities
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ curl -X POST https://api.llmgateway.io/v1/chat/completions \
- `apps/api`: Hono backend
- `apps/gateway`: API gateway for routing LLM requests
- `apps/docs`: Documentation site
- `apps/admin`: Internal admin dashboard
- `packages/db`: Drizzle ORM schema and migrations
- `packages/models`: Model and provider definitions
- `packages/shared`: Shared types and utilities
Expand Down
42 changes: 42 additions & 0 deletions apps/admin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions

# testing
/coverage

# next.js
/.next-dev/
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# env files (can opt-in for committing if needed)
.env*

# vercel
.vercel

# typescript
*.tsbuildinfo
next-env.d.ts
5 changes: 5 additions & 0 deletions apps/admin/.lintstagedrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"package.json": "pnpm sort-package-json",
"**/*.{js,jsx,ts,tsx}": ["pnpm eslint --fix", "pnpm prettier --write"],
"**/*.{md,mdx,json,yml,yaml}": ["pnpm prettier --write"]
}
3 changes: 3 additions & 0 deletions apps/admin/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.content-collections
.next
v1.d.ts
36 changes: 36 additions & 0 deletions apps/admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).

## Getting Started

First, run the development server:

```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```

Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.

This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.

## Learn More

To learn more about Next.js, take a look at the following resources:

- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.

You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!

## Deploy on Vercel

The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
22 changes: 22 additions & 0 deletions apps/admin/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"registries": {}
}
17 changes: 17 additions & 0 deletions apps/admin/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import lint from "../../eslint.config.mjs";
import react from "@abinnovision/eslint-config-react";

/** @type {import("eslint").Linter.Config[]} */
export default [
...lint,
...react,
{
rules: {
"no-console": "off",
"react/no-unescaped-entities": "off",
},
},
{
ignores: ["**/v1.d.ts"],
},
];
81 changes: 81 additions & 0 deletions apps/admin/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { NextResponse } from "next/server";

import type { NextRequest } from "next/server";

interface MeResponse {
user: {
id: string;
email: string;
isAdmin?: boolean;
};
}

export async function middleware(req: NextRequest) {
const { pathname } = req.nextUrl;

// Allow auth and static routes without admin check
if (
pathname.startsWith("/login") ||
pathname.startsWith("/signup") ||
pathname.startsWith("/_next") ||
pathname.startsWith("/favicon") ||
pathname.startsWith("/api")
) {
return NextResponse.next();
}

const apiUrl =
process.env.API_BACKEND_URL ||
process.env.API_URL ||
"http://localhost:4002";

const key = "better-auth.session_token";
const cookies = req.cookies;
const secureSession = cookies.get(`__Secure-${key}`)?.value;
const session = cookies.get(key)?.value;

const cookieHeader = secureSession
? `__Secure-${key}=${secureSession}`
: session
? `${key}=${session}`
: "";

if (!cookieHeader) {
const loginUrl = new URL("/login", req.url);
loginUrl.searchParams.set("returnUrl", pathname || "/");
return NextResponse.redirect(loginUrl);
}

try {
const res = await fetch(`${apiUrl}/user/me`, {
method: "GET",
headers: {
Cookie: cookieHeader,
},
});

if (!res.ok) {
const loginUrl = new URL("/login", req.url);
loginUrl.searchParams.set("returnUrl", pathname || "/");
return NextResponse.redirect(loginUrl);
}

const data = (await res.json()) as MeResponse;

if (!data.user?.isAdmin) {
return new NextResponse("Forbidden: admin access required", {
status: 403,
});
}
} catch {
return new NextResponse("Forbidden: admin access required", {
status: 403,
});
}

return NextResponse.next();
}

export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
};
Loading
Loading