# Build Instructions

## Development

### Setup
```bash
# Install dependencies
pnpm install

# Copy environment file
cp .env.example .env

# Edit .env as needed
# For local development with parent backend:
# PUBLIC_API_URL=http://localhost:3000
# PUBLIC_SEASON=s1
# PUBLIC_ENABLE_TURNSTILE=false
```

### Run Dev Server
```bash
pnpm dev
```

App runs on http://localhost:5173

**Note**: Dev server proxies `/api/*` to `http://localhost:3000` (see [vite.config.ts](vite.config.ts))

---

## Production Build

### Build for Parent Backend

The build is configured to output directly to the parent backend's `frontend/` folder:

```bash
pnpm build
```

**Output location**: `../frontend/` (replaces existing compiled frontend)

### What Gets Built

```
../frontend/
├── _app/                    # SvelteKit app chunks
│   ├── immutable/
│   │   ├── assets/         # CSS files
│   │   ├── chunks/         # JS chunks
│   │   ├── nodes/          # Route components
│   │   └── entry/          # App entry points
│   └── version.json
├── index.html              # Main page
├── 404.html               # 404 fallback
├── admin/
│   └── index.html
├── moderation/
│   └── index.html
├── join/
│   └── index.html
└── ... (other static files)
```

### Backend Integration

The parent backend (in `../src/index.ts`) serves the built frontend:

```typescript
// Serve frontend static files
app.use('/', sirv('./frontend', {
  maxAge: 31536000, // 1 year for immutable files
  immutable: true
}));

// SPA fallback - serve 404.html for unmatched routes
app.use((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.sendFile(path.join(__dirname, '../frontend/404.html'));
});
```

**All API routes** (`/me`, `/auth/*`, `/alliance/*`, etc.) are handled by the backend first, then unmatched routes serve the frontend.

---

## Environment Variables

### Build Time (PUBLIC_*)

These are embedded into the built JavaScript:

- `PUBLIC_API_URL` - API base URL (default: `''` = same origin)
- `PUBLIC_SEASON` - Current season (default: `'s1'`)
- `PUBLIC_ENABLE_TURNSTILE` - Enable Cloudflare Turnstile (default: `'false'`)
- `PUBLIC_TURNSTILE_SITE_KEY` - Turnstile site key

### Development vs Production

**Development** (`.env`):
```env
PUBLIC_API_URL=http://localhost:3000
PUBLIC_SEASON=s1
PUBLIC_ENABLE_TURNSTILE=false
```

**Production** (backend serves frontend from same origin):
```env
PUBLIC_API_URL=
PUBLIC_SEASON=s1
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=0x4AAAAAABpqJe8FO0N84q0F
```

---

## Turnstile Configuration

### Disable Turnstile (Development)

```env
PUBLIC_ENABLE_TURNSTILE=false
```

- No Turnstile widget rendered
- Login buttons work immediately
- OAuth URLs don't include `token` parameter
- Backend should handle auth without Turnstile

### Enable Turnstile (Production)

```env
PUBLIC_ENABLE_TURNSTILE=true
PUBLIC_TURNSTILE_SITE_KEY=your_site_key_here
```

- Turnstile widget loads on login page
- Login buttons disabled until captcha completes
- OAuth URLs include `?token=...` parameter
- Backend validates Turnstile token

---

## Build Process

### Clean Build

```bash
# Remove old build
rm -rf ../frontend/_app ../frontend/index.html

# Fresh build
pnpm build
```

### Verify Build

After building, check the parent backend:

```bash
cd ..
pnpm start

# Open browser to http://localhost:3000
# Should see the new frontend
```

---

## Backend Requirements

The parent backend must:

1. **Serve static files** from `./frontend` directory
2. **Handle API routes** first (before static file serving)
3. **Serve 404.html** for SPA fallback on unmatched routes
4. **Set appropriate headers** (CORS, CSP, etc.)

Example setup in `../src/index.ts`:

```typescript
import sirv from 'sirv';
import path from 'path';

// API routes first
app.get('/me', authMiddleware, ...);
app.post('/auth/logout', ...);
// ... all other API routes

// Serve frontend static files
app.use('/', sirv('./frontend', {
  maxAge: 31536000,
  immutable: true
}));

// SPA fallback
app.use((req, res) => {
  res.setHeader('Content-Type', 'text/html');
  res.sendFile(path.join(__dirname, '../frontend/404.html'));
});
```

---

## Troubleshooting

### Build fails with "Cannot find module"
```bash
rm -rf node_modules
pnpm install
```

### Changes not appearing
```bash
# Clear browser cache
# Or use incognito mode
```

### API calls return 404
- Check `PUBLIC_API_URL` in `.env`
- For production, set to empty string (same origin)
- Backend API routes must be registered before static file serving

### Turnstile not working
- Check `PUBLIC_ENABLE_TURNSTILE` is `'true'` (string, not boolean)
- Verify `PUBLIC_TURNSTILE_SITE_KEY` is correct
- Check browser console for errors

---

## CI/CD Integration

### Example GitHub Actions

```yaml
name: Build Frontend

on:
  push:
    branches: [main]
    paths:
      - 'frontend-src/**'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: pnpm/action-setup@v2
        with:
          version: 10
      - uses: actions/setup-node@v3
        with:
          node-version: 24
          cache: 'pnpm'
          cache-dependency-path: frontend-src/pnpm-lock.yaml

      - name: Install dependencies
        run: cd frontend-src && pnpm install

      - name: Build
        run: cd frontend-src && pnpm build
        env:
          PUBLIC_API_URL: ''
          PUBLIC_SEASON: 's1'
          PUBLIC_ENABLE_TURNSTILE: 'true'
          PUBLIC_TURNSTILE_SITE_KEY: ${{ secrets.TURNSTILE_SITE_KEY }}

      - name: Upload artifact
        uses: actions/upload-artifact@v3
        with:
          name: frontend-build
          path: frontend/
```

---

## File Structure Reference

```
openplace/
├── frontend/               # ← Build output (served by backend)
│   ├── _app/
│   ├── index.html
│   └── ...
├── frontend-src/           # ← Source code (this directory)
│   ├── src/
│   ├── static/
│   ├── package.json
│   ├── svelte.config.js   # Output to ../frontend
│   └── BUILD.md           # This file
└── src/                   # Backend source
    └── index.ts           # Serves ./frontend
```
