Skip to content

Commit 8432c37

Browse files
davila7claude
andcommitted
fix: Resolve SSR fetch for components.json + update CLAUDE.md
- Use absolute URL (https://www.aitmpl.com) for server-side fetch of components.json - Set site config in astro.config.mjs as fallback - Update CLAUDE.md to reflect unified single Vercel project architecture Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 571986c commit 8432c37

2 files changed

Lines changed: 66 additions & 84 deletions

File tree

CLAUDE.md

Lines changed: 62 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ vercel --prod
179179

180180
### Critical Endpoints
181181

182-
The `/api` directory contains Vercel Serverless Functions:
182+
API endpoints live as Astro API routes in `dashboard/src/pages/api/`:
183183

184184
**`/api/track-download-supabase`** (CRITICAL)
185185
- Tracks component downloads for analytics
@@ -195,39 +195,12 @@ The `/api` directory contains Vercel Serverless Functions:
195195
- Vercel Cron: every 30 minutes
196196
- Database: Neon (claude_code_versions, claude_code_changes, discord_notifications_log, monitoring_metadata tables)
197197

198-
### Deployment Workflow
198+
### Shared API Libraries
199199

200-
**ALWAYS test before deploying:**
201-
202-
```bash
203-
# 1. Run API tests
204-
cd api
205-
npm test
206-
207-
# 2. If tests pass, deploy
208-
cd ..
209-
vercel --prod
210-
211-
# 3. Monitor logs
212-
vercel logs aitmpl.com --follow
213-
```
214-
215-
### Environment Variables (Vercel)
216-
217-
```bash
218-
# Supabase
219-
SUPABASE_URL=https://xxx.supabase.co
220-
SUPABASE_SERVICE_ROLE_KEY=xxx
221-
222-
# Neon Database
223-
NEON_DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
224-
225-
# Discord
226-
DISCORD_APP_ID=xxx
227-
DISCORD_BOT_TOKEN=xxx
228-
DISCORD_PUBLIC_KEY=xxx
229-
DISCORD_WEBHOOK_URL_CHANGELOG=https://discord.com/api/webhooks/xxx
230-
```
200+
- `dashboard/src/lib/api/cors.ts` — CORS headers, `corsResponse()`, `jsonResponse()`
201+
- `dashboard/src/lib/api/neon.ts` — Neon client factory
202+
- `dashboard/src/lib/api/auth.ts` — Clerk JWT verification
203+
- `dashboard/src/lib/api/changelog-parser.ts` — Claude Code changelog parser
231204

232205
### Emergency Rollback
233206

@@ -294,54 +267,70 @@ GA_SERVICE_ACCOUNT_JSON # Base64 service account (optional)
294267

295268
**Graceful degradation:** Each source catches its own errors. Missing secrets or API failures show `⚠️ Unavailable` instead of crashing the report.
296269

297-
## Dashboard (app.aitmpl.com)
270+
## Dashboard (www.aitmpl.com)
298271

299-
Astro + React + Tailwind dashboard deployed at `https://app.aitmpl.com`. Clerk auth for user collections. Source lives in `dashboard/`.
272+
Astro + React + Tailwind dashboard serving both `www.aitmpl.com` and `app.aitmpl.com`. Clerk auth for user collections. Source lives in `dashboard/`. All API endpoints are Astro API routes in the same project.
300273

301274
### Architecture
302275

303276
- **Framework**: Astro 5 with React islands, Tailwind v4, `output: 'server'`
304277
- **Auth**: Clerk (`window.Clerk` global, no ClerkProvider per island)
305-
- **Data**: Fetches from `https://www.aitmpl.com/components.json` at runtime (NOT bundled)
306-
- **API proxy**: `dashboard/src/pages/api/[...path].ts` proxies to `localhost:3000` in dev, `https://www.aitmpl.com` in prod
278+
- **Data**: `components.json` and `trending-data.json` served from `dashboard/public/` (same-origin)
279+
- **APIs**: All endpoints in `dashboard/src/pages/api/` (Astro API routes, no separate serverless project)
307280

308281
### Vercel Project Setup
309282

310-
Two separate Vercel projects deploy from the same repo:
283+
Single Vercel project serves all domains:
311284

312-
| Project | Domain | Root Directory |
313-
|---------|--------|----------------|
314-
| `aitmpl` | `www.aitmpl.com` | `/` (root) |
315-
| `aitmpl-dashboard` | `app.aitmpl.com` | `dashboard` |
285+
| Project | Domains | Root Directory |
286+
|---------|---------|----------------|
287+
| `aitmpl-dashboard` | `www.aitmpl.com`, `aitmpl.com` (redirect), `app.aitmpl.com` | `dashboard` |
316288

317-
Each directory has its own `.vercel/project.json` with the correct project ID. Do NOT mix them up.
289+
The legacy root project (`aitmpl`) is archived — only its `.vercel.app` subdomain remains.
318290

319291
### Deployment
320292

321293
**ALWAYS use the deployer agent (`.claude/agents/deployer.md`) for all deployments.** It runs pre-deploy checks (auth, git status, API tests) and handles the full pipeline safely. Never deploy manually.
322294

323295
```bash
324-
npm run deploy:site # Deploy www.aitmpl.com (main site + API)
325-
npm run deploy:dashboard # Deploy app.aitmpl.com (Astro dashboard)
326-
npm run deploy:all # Deploy both
296+
npm run deploy # Deploy www + app.aitmpl.com
297+
npm run deploy:dashboard # Same as above
327298
```
328299

329300
**CI/CD**: Pushes to `main` auto-deploy via GitHub Actions (`.github/workflows/deploy.yml`):
330-
- Changes in `docs/`, `api/`, or `vercel.json` trigger site deploy
331-
- Changes in `dashboard/` trigger dashboard deploy
301+
- Changes in `dashboard/**` trigger deploy
332302

333303
**Required GitHub Secrets** (Settings > Secrets > Actions):
334304
- `VERCEL_TOKEN` — Vercel personal access token
335305
- `VERCEL_ORG_ID` — Vercel org/team ID
336-
- `VERCEL_SITE_PROJECT_ID` — Project ID for www.aitmpl.com
337-
- `VERCEL_DASHBOARD_PROJECT_ID` — Project ID for app.aitmpl.com
306+
- `VERCEL_DASHBOARD_PROJECT_ID` — Project ID for aitmpl-dashboard
338307

339-
### Dashboard Environment Variables (Vercel)
308+
### Environment Variables (Vercel)
340309

341310
```bash
342-
PUBLIC_CLERK_PUBLISHABLE_KEY=xxx # Clerk public key
343-
CLERK_SECRET_KEY=xxx # Clerk secret key
344-
PUBLIC_COMPONENTS_JSON_URL=https://www.aitmpl.com/components.json # MUST use www
311+
# Clerk
312+
PUBLIC_CLERK_PUBLISHABLE_KEY=xxx
313+
CLERK_SECRET_KEY=xxx
314+
315+
# Data
316+
PUBLIC_COMPONENTS_JSON_URL=/components.json
317+
318+
# GitHub OAuth
319+
PUBLIC_GITHUB_CLIENT_ID=xxx
320+
GITHUB_CLIENT_SECRET=xxx
321+
322+
# Supabase (download tracking)
323+
SUPABASE_URL=https://xxx.supabase.co
324+
SUPABASE_SERVICE_ROLE_KEY=xxx
325+
326+
# Neon Database
327+
NEON_DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
328+
329+
# Discord
330+
DISCORD_APP_ID=xxx
331+
DISCORD_BOT_TOKEN=xxx
332+
DISCORD_PUBLIC_KEY=xxx
333+
DISCORD_WEBHOOK_URL_CHANGELOG=https://discord.com/api/webhooks/xxx
345334
```
346335

347336
### Known Issues & Solutions
@@ -350,40 +339,36 @@ PUBLIC_COMPONENTS_JSON_URL=https://www.aitmpl.com/components.json # MUST use ww
350339
- Node v24 has a bug with `writeFileSync` in Vercel's build environment
351340
- Solution: Dashboard project is pinned to Node 22.x (set via Vercel API/dashboard)
352341

353-
**CORS: Always use `www.aitmpl.com` for cross-origin fetches**
354-
- `aitmpl.com` (bare domain) 307-redirects to `www.aitmpl.com`
355-
- The redirect response has NO CORS headers, which blocks browser fetches from `app.aitmpl.com`
356-
- `www.aitmpl.com` serves the actual response WITH `Access-Control-Allow-Origin: *`
357-
- The `PUBLIC_COMPONENTS_JSON_URL` env var MUST point to `https://www.aitmpl.com/...` (not `https://aitmpl.com/...`)
358-
- CORS headers are configured in the root `vercel.json` under `headers`
342+
**Vercel CLI ignores local `.vercel/project.json`**
343+
- The CLI often resolves to the parent directory's project. Use `VERCEL_ORG_ID` and `VERCEL_PROJECT_ID` env vars to force the correct project.
359344

360345
### Local Development
361346

362347
```bash
363348
cd dashboard
364349
npm install
365-
npx astro dev --port 4321 # Dashboard at http://localhost:4321
366-
# In another terminal, for API proxy:
367-
node api/dev-server.js # API at http://localhost:3000
350+
npx astro dev --port 4321 # Dashboard + APIs at http://localhost:4321
368351
```
369352

370-
## Website Architecture (docs/)
371-
372-
Static website at https://aitmpl.com for browsing components.
353+
## Data Files
373354

374-
### Key Files
355+
### Component Catalog
375356

376-
- `docs/components.json` - Component catalog (generated, ~2MB)
377-
- `docs/index.html` - Main component browser
378-
- `docs/blog/` - Blog articles
379-
- `docs/js/` - Vanilla JavaScript (data-loader, search, cart)
357+
- `docs/components.json` — Generated catalog (source of truth)
358+
- `dashboard/public/components.json` — Copy served by the dashboard
359+
- `dashboard/public/trending-data.json` — Trending/download stats
380360

381361
### Data Flow
382362

383363
1. `scripts/generate_components_json.py` scans `cli-tool/components/`
384364
2. Generates `docs/components.json` with embedded content
385-
3. Website loads JSON and renders component cards
386-
4. Download tracking via `/api/track-download-supabase`
365+
3. Copy to `dashboard/public/components.json` for the dashboard to serve
366+
4. Dashboard loads JSON and renders component cards
367+
5. Download tracking via `/api/track-download-supabase`
368+
369+
### Legacy Static Site (docs/)
370+
371+
The `docs/` directory contains the old static HTML site (no longer deployed to www). Blog articles in `docs/blog/` are still referenced externally.
387372

388373
### Blog Article Creation
389374

@@ -423,16 +408,15 @@ This automatically:
423408
npm test # Run all tests
424409
npm run test:watch # Watch mode
425410
npm run test:coverage # Coverage report
426-
cd api && npm test # Test API endpoints
427411
```
428412

429413
Aim for 70%+ test coverage. Test critical paths and error handling.
430414

431415
## Common Issues
432416

433417
**API endpoint returns 404 after deploy**
434-
- Serverless functions must be in `/api/` directory
435-
- Use format: `/api/function-name.js` or `/api/folder/index.js`
418+
- API routes must be in `dashboard/src/pages/api/` as Astro API routes
419+
- Export named HTTP methods: `export const POST: APIRoute`, `export const GET: APIRoute`
436420

437421
**Download tracking not working**
438422
- Check Vercel logs: `vercel logs aitmpl.com --follow`
@@ -441,8 +425,8 @@ Aim for 70%+ test coverage. Test critical paths and error handling.
441425

442426
**Components not updating on website**
443427
- Run `python scripts/generate_components_json.py`
444-
- Clear browser cache
445-
- Check `docs/components.json` file size
428+
- Copy `docs/components.json` to `dashboard/public/components.json`
429+
- Deploy and clear browser cache
446430

447431
## Important Notes
448432

dashboard/src/lib/data.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,11 @@ export async function fetchComponents(): Promise<ComponentsData> {
1111
return cachedData;
1212
}
1313

14-
// Server-side: resolve relative URLs using deployment URL
14+
// Server-side: resolve relative URLs to absolute (fetch() needs full URL on Node)
1515
let url = COMPONENTS_JSON_URL;
16-
if (import.meta.env.SSR && url.startsWith('/')) {
17-
const base = import.meta.env.SITE
18-
?? (process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : null)
19-
?? `http://localhost:${import.meta.env.PORT ?? 4321}`;
20-
url = new URL(url, base).href;
16+
if (typeof window === 'undefined' && url.startsWith('/')) {
17+
const base = import.meta.env.SITE || 'https://www.aitmpl.com';
18+
url = `${base}${url}`;
2119
}
2220

2321
const controller = new AbortController();

0 commit comments

Comments
 (0)