Skip to content
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e5213b4
add admin plan
Kitenite Oct 11, 2025
38e4658
add admin plan
Kitenite Oct 11, 2025
80ca857
hello world
Kitenite Oct 11, 2025
76dfb66
admin projects view
Kitenite Oct 11, 2025
1bb908f
fix typecheck
Kitenite Oct 11, 2025
512cc7e
project list
Kitenite Oct 11, 2025
594d79a
add preview
Kitenite Oct 11, 2025
2d8582c
add search
Kitenite Oct 11, 2025
4207f46
add search
Kitenite Oct 11, 2025
ab7a0f7
allow adding user
Kitenite Oct 12, 2025
e24570f
allow user detail
Kitenite Oct 12, 2025
ff524a3
allow add credit
Kitenite Oct 12, 2025
24104b9
fix some type imports in ui package
Kitenite Oct 12, 2025
b908a03
add subscription view
Kitenite Oct 12, 2025
8002f3d
disable auth for admin dashboard
Kitenite Oct 12, 2025
e3e5b12
price user count
Kitenite Oct 12, 2025
3148cc2
open stripe urls
Kitenite Oct 12, 2025
03e4ef8
deployments
Kitenite Oct 12, 2025
1493aa0
show deployments
Kitenite Oct 12, 2025
af29250
show deployments
Kitenite Oct 12, 2025
908ee8b
domain view
Kitenite Oct 12, 2025
ab3dc67
done
Kitenite Oct 12, 2025
3d82a0c
product view
Kitenite Oct 12, 2025
6861c71
add subscription view
Kitenite Oct 12, 2025
05c9405
add subscription to user
Kitenite Oct 12, 2025
10d7e3c
update subscription count
Kitenite Oct 12, 2025
4945724
mark rate limit inactive
Kitenite Oct 12, 2025
5626352
rate limit display
Kitenite Oct 12, 2025
e985436
Move admin dashboard to private submodule
Kitenite Oct 12, 2025
4d14eb4
docs: Add admin dashboard documentation to README
Kitenite Oct 12, 2025
e008e36
docs: Add admin dashboard self-hosting documentation
Kitenite Oct 12, 2025
4e7a416
add admin dashboard
Kitenite Oct 12, 2025
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
6 changes: 6 additions & 0 deletions apps/admin/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# ------------- Required Keys -------------
# Supabase - Database and authentication
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY="<Fill in from content after running supabase start>"
SUPABASE_DATABASE_URL=postgresql://postgres:[email protected]:54322/postgres
SUPABASE_SERVICE_ROLE_KEY="<Your Supabase service role key - REQUIRED for admin operations>"
46 changes: 46 additions & 0 deletions apps/admin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# database
/prisma/db.sqlite
/prisma/db.sqlite-journal
db.sqlite

# next.js
/.next/
/out/
next-env.d.ts

# production
/build

# misc
.DS_Store
*.pem

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

# local env files
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
.env
.env*.local

# vercel
.vercel

# typescript
*.tsbuildinfo

# idea files
.idea
199 changes: 199 additions & 0 deletions apps/admin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
# Onlook Admin App

A lightweight admin dashboard for Onlook, built with the same architecture as the main web client.

## Architecture

- **Next.js 15** (App Router)
- **tRPC** for type-safe API routes
- **Drizzle ORM** via shared `@onlook/db` package
- **Supabase Auth** for authentication
- **TailwindCSS** for styling
- **Bun** as package manager

## Planned Directory Structure

```
apps/admin/
├── src/
│ ├── app/
│ │ ├── layout.tsx # Root layout with providers
│ │ ├── page.tsx # Homepage/Dashboard
│ │ └── api/
│ │ └── trpc/
│ │ └── [trpc]/
│ │ └── route.ts # tRPC API handler
│ ├── server/
│ │ └── api/
│ │ ├── root.ts # Main tRPC router
│ │ ├── trpc.ts # tRPC context & procedures
│ │ └── routers/ # Feature-specific routers
│ │ └── admin.ts # Admin-specific endpoints
│ ├── trpc/
│ │ ├── react.tsx # Client-side tRPC provider
│ │ ├── helpers.ts # tRPC configuration helpers
│ │ └── query-client.ts # React Query client setup
│ ├── utils/
│ │ └── supabase/
│ │ ├── server.ts # Server-side Supabase client
│ │ └── client/
│ │ └── index.ts # Browser Supabase client
│ ├── components/ # Admin UI components
│ ├── env.ts # Environment validation (via @t3-oss/env-nextjs)
│ └── styles/
│ └── globals.css # Global styles + Tailwind imports
├── public/ # Static assets
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript config
├── next.config.ts # Next.js configuration
├── tailwind.config.ts # Tailwind configuration
├── postcss.config.js # PostCSS configuration
└── .env.local # Local environment variables (gitignored)
```

## Shared Packages

This app reuses existing monorepo packages:

- `@onlook/db` - Drizzle schema & database client
- `@onlook/ui` - Shared UI components
- `@onlook/utility` - Shared utilities
- `@onlook/constants` - Shared constants
- `@onlook/typescript` - Shared TypeScript config
- `@onlook/eslint` - Shared ESLint config

## Key Features

### tRPC Setup

Following the same pattern as `apps/web/client`:

- **Context**: Database connection, Supabase auth, user session
- **Procedures**:
- `publicProcedure` - No authentication required
- `protectedProcedure` - Requires authenticated user
- `adminProcedure` - Requires admin privileges (uses Supabase service role)
- **Validation**: Zod schemas for all inputs
- **Serialization**: SuperJSON for handling dates, etc.

### Environment Variables

Required environment variables (configure in `.env.local`):

#### Server-side
```env
# Database
SUPABASE_DATABASE_URL=postgresql://...
SUPABASE_SERVICE_ROLE_KEY=...
```

#### Client-side (NEXT_PUBLIC_*)
```env
NEXT_PUBLIC_SITE_URL=http://localhost:3001
NEXT_PUBLIC_SUPABASE_URL=...
NEXT_PUBLIC_SUPABASE_ANON_KEY=...
```

### TypeScript Path Aliases

- `@/*` → `src/*`
- `~/*` → `src/*`

## Scripts

```bash
# Development
bun dev # Start dev server (default: localhost:3001)

# Production
bun build # Build for production
bun start # Start production server

# Type checking & linting
bun typecheck # Run TypeScript type checking
bun lint # Run ESLint
bun format # Format code with ESLint --fix
```

## Development Workflow

1. **Install dependencies**: `bun install` (from repo root)
2. **Set up environment**: Copy `.env.local.example` to `.env.local` and fill in values
3. **Run dev server**: `bun --filter @onlook/admin dev`
4. **Access**: http://localhost:3001

## Authentication

- Uses Supabase Auth (same setup as main web client)
- Server-side auth: `createClient()` from `@/utils/supabase/server`
- Client-side auth: `createClient()` from `@/utils/supabase/client`
- Admin routes should use `adminProcedure` for service role access

## Deployment Considerations

### As Git Submodule (Separate Private Repo)

This admin app is designed to be extracted into a separate private repository and included as a git submodule:

```bash
# In a new private repo
git init
git remote add origin <private-repo-url>

# Copy apps/admin/* to the new repo
# Commit and push

# Back in main repo, add as submodule
git submodule add <private-repo-url> apps/admin
git commit -m "Add admin app as submodule"
```

### Environment Variables

- Use Vercel/deployment platform environment variables
- Ensure `SUPABASE_SERVICE_ROLE_KEY` is set (required for admin operations)
- Set `NEXT_PUBLIC_SITE_URL` to production URL

### Database Access

- Reuses the same Supabase database as main app via `@onlook/db`
- Admin routes have elevated permissions via service role key
- Consider implementing additional RBAC checks in tRPC procedures

## Security Considerations

- **Admin procedures**: Always verify user permissions before granting admin access
- **Service role key**: Never expose to client, only use in `adminProcedure`
- **RLS policies**: Admin client bypasses RLS, so implement checks in application layer
- **CORS**: Configure appropriately if admin is on separate domain

## Next Steps

1. ✅ Create directory structure
2. ✅ Add README with plan
3. ⬜ Set up package.json with dependencies
4. ⬜ Configure Next.js & TypeScript
5. ⬜ Set up tRPC router structure
6. ⬜ Create environment configuration
7. ⬜ Build basic layout & auth flow
8. ⬜ Add admin-specific routes/features
9. ⬜ Extract to separate private repo
10. ⬜ Configure as git submodule

## Contributing

This is an internal admin tool. Follow the same code standards as the main web client:

- Use Server Components by default
- Add `use client` only when needed (events, state, browser APIs)
- Validate all inputs with Zod
- Export new tRPC routers in `src/server/api/root.ts`
- Use path aliases (`@/*` or `~/*`)

## Questions?

Refer to the main web client (`apps/web/client`) for examples of:
- tRPC router patterns
- Supabase auth setup
- Next.js App Router usage
- Component patterns
14 changes: 14 additions & 0 deletions apps/admin/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import baseConfig from "@onlook/eslint/base";
import nextjsConfig, { restrictEnvAccess } from "@onlook/eslint/nextjs";
import reactConfig from "@onlook/eslint/react";

/** @type {import('typescript-eslint').Config} */
export default [
{
ignores: [".next/**"],
},
...baseConfig,
...reactConfig,
...nextjsConfig,
...restrictEnvAccess,
];
16 changes: 16 additions & 0 deletions apps/admin/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NextConfig } from 'next';
import path from 'node:path';
import './src/env';

const nextConfig: NextConfig = {
devIndicators: false,
eslint: {
ignoreDuringBuilds: true,
},
};

if (process.env.NODE_ENV === 'development') {
nextConfig.outputFileTracingRoot = path.join(__dirname, '../../..');
}

export default nextConfig;
44 changes: 44 additions & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"name": "@onlook/admin",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"dev": "next dev --port 3001",
"build": "next build",
"start": "next start --port 3001",
"typecheck": "tsc --noEmit --project tsconfig.json 2>&1 | grep -v 'web/client' || exit 0",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix the typecheck script filter.

The typecheck script filters out errors containing 'web/client', which appears to be copied from another package and doesn't apply to the admin app. This could inadvertently hide legitimate TypeScript errors.

Apply this diff to remove the incorrect filter:

-        "typecheck": "tsc --noEmit --project tsconfig.json 2>&1 | grep -v 'web/client' || exit 0",
+        "typecheck": "tsc --noEmit --project tsconfig.json",
📝 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
"typecheck": "tsc --noEmit --project tsconfig.json 2>&1 | grep -v 'web/client' || exit 0",
"typecheck": "tsc --noEmit --project tsconfig.json",
🤖 Prompt for AI Agents
In apps/admin/package.json around line 10, the "typecheck" script currently
pipes tsc output through a grep that excludes 'web/client' (copied from another
package) which can hide legitimate errors; remove the entire pipe filter and
trailing "|| exit 0" so the script simply runs tsc --noEmit --project
tsconfig.json and fails on real type errors.

"lint": "eslint . --max-warnings 0",
"format": "eslint --fix ."
},
"dependencies": {
"@onlook/db": "*",
"@onlook/ui": "*",
"@onlook/utility": "*",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.45.4",
"@t3-oss/env-nextjs": "^0.12.0",
"@tanstack/react-query": "^5.69.0",
"@trpc/client": "^11.0.0",
"@trpc/react-query": "^11.0.0",
"@trpc/server": "^11.0.0",
"next": ">=15.5.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^4.1.3"
},
"devDependencies": {
"@onlook/eslint": "*",
"@onlook/typescript": "*",
"@tailwindcss/postcss": "^4.0.15",
"@types/node": "^20.14.10",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"eslint": "^9.0.0",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.15",
"typescript": "^5.5.4"
}
}
5 changes: 5 additions & 0 deletions apps/admin/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
};
31 changes: 31 additions & 0 deletions apps/admin/src/app/api/trpc/[trpc]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { type NextRequest } from 'next/server';
import { env } from '~/env';
import { appRouter } from '~/server/api/root';
import { createTRPCContext } from '~/server/api/trpc';

/**
* This wraps the `createTRPCContext` helper and provides the required context for the tRPC API when
* handling a HTTP request (e.g. when you make requests from Client Components).
*/
const createContext = async (req: NextRequest) => {
return createTRPCContext({
headers: req.headers,
});
};

const handler = (req: NextRequest) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req,
router: appRouter,
createContext: () => createContext(req),
onError:
env.NODE_ENV === 'development'
? ({ path, error }) => {
console.error(`❌ tRPC failed on ${path ?? '<no-path>'}: ${error.message}`);
}
: undefined,
});

export { handler as GET, handler as POST };
19 changes: 19 additions & 0 deletions apps/admin/src/app/deployments/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { DeploymentsList } from '@/components/deployments/deployments-list';

export default function DeploymentsPage() {
return (
<div className="bg-background">
<div className="container mx-auto px-4 py-8 space-y-8">
<div>
<h1 className="text-4xl font-bold tracking-tight">
Deployments
</h1>
<p className="mt-2 text-muted-foreground">
View and manage all project deployments
</p>
</div>
<DeploymentsList />
</div>
</div>
);
}
Loading
Loading