Files
csgowtf/docs/CORS_PROXY.md
vikingowl 8f3b652740 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>
2025-11-12 19:31:18 +01:00

8.7 KiB

API Proxying with SvelteKit Server Routes

This document explains how API requests are proxied to the backend using SvelteKit server routes.

Why Use Server Routes?

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

Browser → /api/matches → SvelteKit Server Route → Backend → Response

Detailed Flow:

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!)

SSR (Server-Side Rendering) Flow:

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

Note: SSR bypasses the SvelteKit route and calls the backend directly because relative URLs (/api) don't work during server-side rendering.

Key Components

1. SvelteKit Server Route (src/routes/api/[...path]/+server.ts)

  • 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:

# Production API (default)
VITE_API_BASE_URL=https://api.csgow.tf

# Local backend (for development)
# VITE_API_BASE_URL=http://localhost:8000

Switching Backends:

# 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

Server Route Implementation

File: src/routes/api/[...path]/+server.ts

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

File: src/lib/api/client.ts

// Simple, single configuration
const API_BASE_URL = '/api';

// Always routes to SvelteKit server routes
// No environment detection needed

Testing the Setup

1. Check Environment Variable

cat .env

# Should show:
VITE_API_BASE_URL=https://api.csgow.tf
# or
VITE_API_BASE_URL=http://localhost:8000

2. Start Development Server

npm run dev

# Server starts on http://localhost:5173

3. Check Network Requests

Open DevTools → Network tab:

  • Requests go to /api/matches, /api/player/123, etc.
  • Status should be 200 OK
  • No CORS errors in console

4. Test Both Backends

Test Production API:

# 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

Test Local Backend:

# 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: 503 Service Unavailable

Symptom: API requests return 503 error

Possible Causes:

  1. Backend is not running
  2. Wrong VITE_API_BASE_URL in .env
  3. Network connectivity issues

Fix:

# 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: 404 Not Found

Symptom: /api/* routes return 404

Cause: SvelteKit server route file missing or not loaded

Fix:

# 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:

# 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

Cause: API client is not using /api prefix

Fix: Check src/lib/api/client.ts:

// Should be:
const API_BASE_URL = '/api';

// Not:
const API_BASE_URL = 'https://api.csgow.tf'; // ❌ Wrong

How It Works Compared to Vite Proxy

Old Approach (Vite Proxy)

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

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

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

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

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

SvelteKit server routes provide a production-ready, maintainable solution for API proxying that works in all environments.