feat: Implement Phase 1 critical features and fix API integration

This commit completes the first phase of feature parity implementation and
resolves all API integration issues to match the backend API format.

## API Integration Fixes

- Remove all hardcoded default values from transformers (tick_rate, kast, player_count, steam_updated)
- Update TypeScript types to make fields optional where backend doesn't guarantee them
- Update Zod schemas to validate optional fields correctly
- Fix mock data to match real API response format (plain arrays, not wrapped objects)
- Update UI components to handle undefined values with proper fallbacks
- Add comprehensive API documentation for Match and Player endpoints

## Phase 1 Features Implemented (3/6)

### 1. Player Tracking System 
- Created TrackPlayerModal.svelte with auth code input
- Integrated track/untrack player API endpoints
- Added UI for providing optional share code
- Displays tracked status on player profiles
- Full validation and error handling

### 2. Share Code Parsing 
- Created ShareCodeInput.svelte component
- Added to matches page for easy match submission
- Real-time validation of share code format
- Parse status feedback with loading states
- Auto-redirect to match page on success

### 3. VAC/Game Ban Status 
- Added VAC and game ban count/date fields to Player type
- Display status badges on player profile pages
- Show ban count and date when available
- Visual indicators using DaisyUI badge components

## Component Improvements

- Modal.svelte: Added Svelte 5 Snippet types, actions slot support
- ThemeToggle.svelte: Removed deprecated svelte:component usage
- Tooltip.svelte: Fixed type safety with Snippet type
- All new components follow Svelte 5 runes pattern ($state, $derived, $bindable)

## Type Safety & Linting

- Fixed all ESLint errors (any types → proper types)
- Fixed form label accessibility issues
- Replaced error: any with error: unknown + proper type guards
- Added Snippet type imports where needed
- Updated all catch blocks to use instanceof Error checks

## Static Assets

- Migrated all files from public/ to static/ directory per SvelteKit best practices
- Moved 200+ map icons, screenshots, and other assets
- Updated all import paths to use /images/ (served from static/)

## Documentation

- Created IMPLEMENTATION_STATUS.md tracking all 15 missing features
- Updated API.md with optional field annotations
- Created MATCHES_API.md with comprehensive endpoint documentation
- Added inline comments marking optional vs required fields

## Testing

- Updated mock fixtures to remove default values
- Fixed mock handlers to return plain arrays like real API
- Ensured all components handle undefined gracefully

## Remaining Phase 1 Tasks

- [ ] Add VAC status column to match scoreboard
- [ ] Create weapons statistics tab for matches
- [ ] Implement recently visited players on home page

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-12 19:31:18 +01:00
parent a861b1c1b6
commit 8f3b652740
422 changed files with 106174 additions and 102193 deletions

View File

@@ -1,234 +1,393 @@
# CORS Proxy Configuration
# API Proxying with SvelteKit Server Routes
This document explains how the CORS proxy works in the CS2.WTF frontend.
This document explains how API requests are proxied to the backend using SvelteKit server routes.
## Problem: CORS in Development
## Why Use Server Routes?
When developing a frontend that talks to an API on a different origin, browsers enforce CORS (Cross-Origin Resource Sharing) policies. This causes errors like:
The CS2.WTF frontend uses **SvelteKit server routes** to proxy API requests to the backend. This approach provides several benefits:
-**Works in all environments**: Development, preview, and production
-**No CORS issues**: Requests are server-side
-**Single code path**: Same behavior everywhere
-**Flexible backend switching**: Change one environment variable
-**Future-proof**: Can add caching, rate limiting, auth later
-**Better security**: Backend URL not exposed to client
## Architecture
### Request Flow
```
Access to fetch at 'https://api.csgow.tf/matches' from origin 'http://localhost:5173'
has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present
on the requested resource.
Browser → /api/matches → SvelteKit Server Route → Backend → Response
```
## Solution: Vite Development Proxy
**Detailed Flow**:
The Vite dev server includes a built-in proxy that solves this problem by making all API requests appear same-origin.
### Configuration
**File**: `vite.config.ts`
```typescript
import { loadEnv } from 'vite';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8000';
return {
server: {
proxy: {
'/api': {
target: apiBaseUrl,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
secure: false,
ws: true
}
}
}
};
});
```
1. Browser: GET http://localhost:5173/api/matches?limit=20
2. SvelteKit: Routes to src/routes/api/[...path]/+server.ts
3. Server Handler: Reads VITE_API_BASE_URL environment variable
4. Backend Call: GET https://api.csgow.tf/matches?limit=20
5. Backend: Returns JSON response
6. Server Handler: Forwards response to browser
7. Browser: Receives response (no CORS issues!)
```
### How It Works
**SSR (Server-Side Rendering) Flow**:
1. **API Client** (in development) makes requests to `/api/*`:
```typescript
// src/lib/api/client.ts
const API_BASE_URL = import.meta.env.DEV ? '/api' : VITE_API_BASE_URL;
```
```
1. Page Load: +page.ts calls api.matches.getMatches()
2. API Client: Detects import.meta.env.SSR === true
3. Direct Call: GET https://api.csgow.tf/matches?limit=20
4. Backend: Returns JSON response
5. SSR: Renders page with data
```
2. **Vite Proxy** intercepts requests to `/api/*` and forwards them:
```
Browser Request: GET http://localhost:5173/api/matches?limit=6
Vite Proxy: Intercepts /api/* requests
Backend Request: GET https://api.csgow.tf/matches?limit=6
Response: ← Returns data through proxy
Browser: ← Receives response (appears same-origin)
```
**Note**: SSR bypasses the SvelteKit route and calls the backend directly because relative URLs (`/api`) don't work during server-side rendering.
3. **Browser sees same-origin request** - no CORS error!
### Key Components
### Configuration Options
**1. SvelteKit Server Route** (`src/routes/api/[...path]/+server.ts`)
| Option | Value | Purpose |
|--------|-------|---------|
| `target` | `env.VITE_API_BASE_URL` | Where to forward requests |
| `changeOrigin` | `true` | Updates `Origin` header to match target |
| `rewrite` | Remove `/api` prefix | Maps `/api/matches` → `/matches` |
| `secure` | `false` | Allow self-signed certificates |
| `ws` | `true` | Enable WebSocket proxying |
- Catch-all route that matches `/api/*`
- Forwards requests to backend
- Supports GET, POST, DELETE methods
- Handles errors gracefully
**2. API Client** (`src/lib/api/client.ts`)
- Browser: Uses `/api` base URL (routes to SvelteKit)
- SSR: Uses `VITE_API_BASE_URL` directly (bypasses SvelteKit route)
- Automatically detects environment with `import.meta.env.SSR`
**3. Environment Variable** (`.env`)
- `VITE_API_BASE_URL` controls which backend to use
- Switch between local and production easily
## Configuration
### Environment Variables
**`.env`**:
```env
# Proxy will forward /api/* to this URL
# Production API (default)
VITE_API_BASE_URL=https://api.csgow.tf
# Or use local backend
# Local backend (for development)
# VITE_API_BASE_URL=http://localhost:8000
```
### Logging
The proxy logs all requests for debugging:
**Switching Backends**:
```bash
[Vite Config] API Proxy target: https://api.csgow.tf
[Proxy] GET /api/matches?limit=6 -> https://api.csgow.tf/matches?limit=6
[Proxy ✓] GET /api/matches?limit=6 -> 200
[Proxy] GET /api/match/123 -> https://api.csgow.tf/match/123
[Proxy ✓] GET /api/match/123 -> 200
# Use production API
echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env
npm run dev
# Use local backend
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
npm run dev
```
Error logging:
```bash
[Proxy Error] ECONNREFUSED
[Proxy Error] Make sure backend is running at: http://localhost:8000
### Server Route Implementation
**File**: `src/routes/api/[...path]/+server.ts`
```typescript
import { error, json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://api.csgow.tf';
export const GET: RequestHandler = async ({ params, url }) => {
const path = params.path; // e.g., "matches"
const queryString = url.search; // e.g., "?limit=20"
const backendUrl = `${API_BASE_URL}/${path}${queryString}`;
try {
const response = await fetch(backendUrl);
const data = await response.json();
return json(data);
} catch (err) {
throw error(503, 'Unable to connect to backend');
}
};
```
## API Client Configuration
### API Client Configuration
**File**: `src/lib/api/client.ts`
```typescript
const getAPIBaseURL = (): string => {
// In production builds, use the configured URL directly
if (import.meta.env.PROD) {
return import.meta.env?.VITE_API_BASE_URL || 'https://api.csgow.tf';
}
// Simple, single configuration
const API_BASE_URL = '/api';
// In development mode, ALWAYS use the Vite proxy to avoid CORS issues
// The proxy will forward /api requests to VITE_API_BASE_URL
return '/api';
};
// Always routes to SvelteKit server routes
// No environment detection needed
```
This ensures:
- ✅ **Development**: Always uses `/api` (proxy handles CORS)
- ✅ **Production**: Uses direct URL (backend has CORS enabled)
## Testing the Setup
## Testing the Proxy
### 1. Check Environment Variable
### 1. Check Vite Config Loads Environment
```bash
cat .env
# Should show:
VITE_API_BASE_URL=https://api.csgow.tf
# or
VITE_API_BASE_URL=http://localhost:8000
```
### 2. Start Development Server
Start dev server and look for:
```bash
npm run dev
# Should show:
[Vite Config] API Proxy target: https://api.csgow.tf
```
### 2. Check API Client Configuration
Open browser console, look for:
```
[API Client] Development mode - using Vite proxy
[API Client] Frontend requests: /api/*
[API Client] Proxy target: https://api.csgow.tf
# Server starts on http://localhost:5173
```
### 3. Check Network Requests
Open DevTools → Network tab:
- ✅ Requests should go to `/api/*` (not full URL)
- ✅ Response should be `200 OK`
- ✅ Requests go to `/api/matches`, `/api/player/123`, etc.
- ✅ Status should be `200 OK`
- ✅ No CORS errors in console
### 4. Check Proxy Logs
### 4. Test Both Backends
Terminal should show:
**Test Production API**:
```bash
# Set production API
echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env
# Start dev server
npm run dev
# Visit http://localhost:5173/matches
# Should load matches from production API
```
[Proxy] GET /api/matches -> https://api.csgow.tf/matches
[Proxy ✓] GET /api/matches -> 200
**Test Local Backend**:
```bash
# Start local backend first
cd ../csgowtfd
go run main.go
# In another terminal, set local API
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
# Start dev server
npm run dev
# Visit http://localhost:5173/matches
# Should load matches from local backend
```
## Common Issues
### Issue 1: Proxy Not Loading .env
### Issue 1: 503 Service Unavailable
**Symptom**: Proxy uses default `http://localhost:8000` instead of `.env` value
**Symptom**: API requests return 503 error
**Cause**: `vite.config.ts` not loading environment variables
**Possible Causes**:
**Fix**: Use `loadEnv()` in config:
```typescript
import { loadEnv } from 'vite';
1. Backend is not running
2. Wrong `VITE_API_BASE_URL` in `.env`
3. Network connectivity issues
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8000';
// ...
});
**Fix**:
```bash
# Check .env file
cat .env
# If using local backend, make sure it's running
curl http://localhost:8000/matches
# If using production API, check connectivity
curl https://api.csgow.tf/matches
# Restart dev server after changing .env
npm run dev
```
### Issue 2: Still Getting CORS Errors
### Issue 2: 404 Not Found
**Symptom**: `/api/*` routes return 404
**Cause**: SvelteKit server route file missing or not loaded
**Fix**:
```bash
# Check file exists
ls src/routes/api/[...path]/+server.ts
# If missing, create it
mkdir -p src/routes/api/'[...path]'
# Then create +server.ts file
# Restart dev server
npm run dev
```
### Issue 3: Environment Variable Not Loading
**Symptom**: Server route uses wrong backend URL
**Cause**: Changes to `.env` require server restart
**Fix**:
```bash
# Stop dev server (Ctrl+C)
# Update .env
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
# Start dev server again
npm run dev
```
### Issue 4: CORS Errors Still Appearing
**Symptom**: Browser console shows CORS errors
**Possible Causes**:
1. API client not using `/api` prefix in development
2. Request bypassing proxy somehow
3. Running production build instead of dev server
**Cause**: API client is not using `/api` prefix
**Fix**:
1. Check API client logs show: `Development mode - using Vite proxy`
2. Verify Network tab shows requests to `/api/*`
3. Run `npm run dev` (not `npm run preview`)
### Issue 3: Connection Refused
**Symptom**: `[Proxy Error] ECONNREFUSED`
**Cause**: Backend is not running at the configured URL
**Fix**:
- If using local backend: Start `csgowtfd` on port 8000
- If using production API: Check `VITE_API_BASE_URL=https://api.csgow.tf`
## Production Build
In production, the proxy is **not used**. The frontend makes direct requests to the backend:
Check `src/lib/api/client.ts`:
```typescript
// Production build
const API_BASE_URL = 'https://api.csgow.tf';
// Should be:
const API_BASE_URL = '/api';
// Direct request (no proxy)
fetch('https://api.csgow.tf/matches');
// Not:
const API_BASE_URL = 'https://api.csgow.tf'; // ❌ Wrong
```
The production API must have CORS enabled:
## How It Works Compared to Vite Proxy
### Old Approach (Vite Proxy)
```
Access-Control-Allow-Origin: https://cs2.wtf
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
Development:
Browser → /api → Vite Proxy → Backend
Production:
Browser → Backend (direct, different code path)
```
**Problems**:
- Two different code paths (dev vs prod)
- Proxy only works in development
- SSR has to bypass proxy
- Complex configuration
### New Approach (SvelteKit Server Routes)
```
All Environments:
Browser → /api → SvelteKit Route → Backend
```
**Benefits**:
- Single code path
- Works in dev, preview, and production
- Consistent behavior everywhere
- Simpler configuration
## Adding Features
### Add Request Caching
**File**: `src/routes/api/[...path]/+server.ts`
```typescript
const cache = new Map<string, { data: any; expires: number }>();
export const GET: RequestHandler = async ({ params, url }) => {
const cacheKey = `${params.path}${url.search}`;
// Check cache
const cached = cache.get(cacheKey);
if (cached && Date.now() < cached.expires) {
return json(cached.data);
}
// Fetch from backend
const data = await fetch(`${API_BASE_URL}/${params.path}${url.search}`).then((r) => r.json());
// Cache for 5 minutes
cache.set(cacheKey, {
data,
expires: Date.now() + 5 * 60 * 1000
});
return json(data);
};
```
### Add Rate Limiting
```typescript
import { rateLimit } from '$lib/server/rateLimit';
export const GET: RequestHandler = async ({ request, params, url }) => {
// Check rate limit
await rateLimit(request);
// Continue with normal flow...
};
```
### Add Authentication
```typescript
export const GET: RequestHandler = async ({ request, params, url }) => {
// Get auth token from cookie
const token = request.headers.get('cookie')?.includes('auth_token');
// Forward to backend with auth
const response = await fetch(backendUrl, {
headers: {
Authorization: `Bearer ${token}`
}
});
// ...
};
```
## Summary
| Environment | Frontend URL | API Requests | CORS |
|-------------|-------------|--------------|------|
| **Development** | `http://localhost:5173` | `/api/*` → Proxy → Backend | ✅ Proxy handles |
| **Production** | `https://cs2.wtf` | Direct to backend | ✅ Backend CORS |
| Feature | Vite Proxy | SvelteKit Routes |
| --------------------- | ---------- | ---------------- |
| Works in dev | ✅ | ✅ |
| Works in production | ❌ | ✅ |
| Single code path | ❌ | ✅ |
| Can add caching | ❌ | ✅ |
| Can add rate limiting | ❌ | ✅ |
| Can add auth | ❌ | ✅ |
| SSR compatible | ❌ | ✅ |
The proxy is a **development-only** feature that makes local development smooth and eliminates CORS headaches.
**SvelteKit server routes provide a production-ready, maintainable solution for API proxying that works in all environments.**