# SportPulse Widget API - Project 1 Implementation Plan

## Overview

**Project 1**: Read-only API for sports widgets with X-API-Key authentication.

**Stack**: Node.js + TypeScript + Express + MongoDB

**Status**: Planning Complete

---

## Implementation Phases

### Phase 1: Project Setup
- [x] Initialize npm project with TypeScript
- [x] Configure tsconfig.json
- [x] Set up Docker Compose for MongoDB
- [x] Create .env.example and environment config
- [x] Set up folder structure

### Phase 2: Types & Validation
- [x] Define TypeScript interfaces in `src/types/index.ts`
- [x] Create Zod validation schemas in `src/validation/schemas.ts`

### Phase 3: Database Models
- [x] Create Mongoose models for all entities
- [x] Set up database connection
- [x] Implement seed data loading from JSON

### Phase 4: Middleware
- [x] Implement X-API-Key authentication middleware
- [x] Create error handling middleware
- [x] Add request logging middleware

### Phase 5: API Routes & Controllers
- [x] `GET /sports` - List all sports
- [x] `GET /sports/:sportId/leagues` - Leagues for a sport
- [x] `GET /leagues/:leagueId/participants` - Participants in a league
- [x] `GET /leagues/:leagueId/widget-types` - Widget types for a league
- [x] `GET /templates` - Templates with embed codes

### Phase 6: Services & Business Logic
- [x] Implement service layer for each entity
- [x] Add embed code generation logic

### Phase 7: Scripts & Utilities
- [x] Create API key generation script

### Phase 8: Documentation & Testing
- [x] Write README with setup instructions
- [x] Test all endpoints

---

## Project Structure

```
/sportpulse-internal-apis/
├── package.json
├── tsconfig.json
├── docker-compose.yml              # MongoDB
├── .env.example
├── README.md
├── src/
│   ├── index.ts                    # Entry point
│   ├── app.ts                      # Express app setup
│   ├── config/
│   │   ├── database.ts
│   │   └── env.ts
│   ├── middleware/
│   │   ├── auth.middleware.ts      # X-API-Key validation
│   │   ├── error.middleware.ts
│   │   └── request-logger.middleware.ts
│   ├── models/
│   │   ├── index.ts
│   │   ├── sport.model.ts
│   │   ├── league.model.ts
│   │   ├── participant.model.ts
│   │   ├── widget-type.model.ts
│   │   ├── template.model.ts
│   │   └── api-key.model.ts
│   ├── routes/
│   │   ├── index.ts
│   │   ├── sports.routes.ts
│   │   ├── leagues.routes.ts        # includes /widget-types nested route
│   │   └── templates.routes.ts
│   ├── controllers/
│   │   └── [mirrors routes]
│   ├── services/
│   │   └── [mirrors routes]
│   ├── types/
│   │   └── index.ts                # All TypeScript interfaces
│   └── validation/
│       └── schemas.ts              # Zod schemas
├── scripts/
│   └── generate-api-key.ts         # Generate API keys for clients
└── sportpulse_widgets_seed.json    # Seed data (provided)
```

---

## Entities (MongoDB Collections)

**Naming Convention**: All `...Name` fields use slug format (lowercase, hyphenated, no prefixes).

| Entity | Key Fields |
|--------|-----------|
| **Sport** | id, sportName, iconUrl, isActive |
| **League** | id, sportId, sportName, leagueName, leagueLogoUrl, isActive |
| **Participant** | id, participantType (TEAM/DRIVER/PLAYER), sportId, sportName, leagueIds[], leagueNames[], teamName, fullName, shortName, abbreviation, teamLogoUrl, isActive |
| **WidgetType** | id, sportId, sportName, leagueId?, leagueName?, widgetName, isActive |
| **Template** | id, sportId, sportName, leagueId?, leagueName?, participantId?, teamName?, widgetTypeId, widgetName, templateName, darkThemeUrl, lightThemeUrl, thumbnailLightUrl, thumbnailDarkUrl, featured, isActive |
| **ApiKey** | id, name, keyHash, keyPrefix, isActive, expiresAt, lastUsedAt |

**Seed Data**: `sportpulse_widgets_seed.json` (6 sports, 14 leagues, 77 widget types, 113 templates)

---

## API Endpoints

Base: `/api/v1` | Auth: `X-API-Key` header

| Endpoint | Description |
|----------|-------------|
| `GET /sports` | List all sports (filter: isActive) |
| `GET /sports/:sportId/leagues` | Leagues for a sport (filter: isActive) |
| `GET /leagues/:leagueId/participants` | Participants in a league (filter: type, isActive) |
| `GET /leagues/:leagueId/widget-types` | Widget types for a league (filter: isActive) |
| `GET /templates` | Templates with embed codes (filter: sportId, leagueId, widgetTypeId, participantId, featured, isActive) |

**Embed Generation**: Templates return `embedCodeLight` and `embedCodeDark` as iframe snippets. Returns `null` if the corresponding theme URL is not available.

---

## Entity Schemas (TypeScript Interfaces)

```typescript
interface Sport {
  id: number;
  sportName: string;        // e.g., "soccer", "basketball"
  iconUrl: string | null;
  isActive: boolean;
}

interface League {
  id: number;
  sportId: number;
  sportName: string;
  leagueName: string;       // e.g., "premier-league", "nba"
  leagueLogoUrl: string | null;
  isActive: boolean;
}

interface Participant {
  id: number;
  participantType: 'TEAM' | 'DRIVER' | 'PLAYER';
  sportId: number;
  sportName: string;
  leagueIds: number[];
  leagueNames: string[];
  teamName: string | null;  // e.g., "manchester-united"
  fullName: string | null;
  shortName: string | null;
  abbreviation: string | null;
  teamLogoUrl: string | null;
  isActive: boolean;
}

interface WidgetType {
  id: number;
  sportId: number;
  sportName: string;
  leagueId: number | null;
  leagueName: string | null;
  widgetName: string;       // e.g., "standings", "schedule"
  isActive: boolean;
}

interface Template {
  id: number;
  sportId: number;
  sportName: string;
  leagueId: number | null;
  leagueName: string | null;
  participantId: number | null;
  teamName: string | null;
  widgetTypeId: number;
  widgetName: string;
  templateName: string;
  darkThemeUrl: string | null;    // null if only light theme available
  lightThemeUrl: string | null;   // null if only dark theme available
  thumbnailLightUrl: string | null;
  thumbnailDarkUrl: string | null;
  featured: boolean;
  isActive: boolean;
}
```

---

## Seed Data Structure

**File**: `sportpulse_widgets_seed.json` (already provided)

```json
{
  "version": "1.0",
  "sports": [
    { "id": 1, "sportName": "american-football", "iconUrl": null, "isActive": true }
  ],
  "leagues": [
    { "id": 1, "sportId": 6, "sportName": "soccer", "leagueName": "bundesliga", "iconUrl": null, "isActive": true }
  ],
  "participants": [],
  "widgetTypes": [
    { "id": 1, "sportId": 1, "leagueId": 8, "sportName": "american-football", "leagueName": "ncaa-fbs", "widgetName": "live-scores", "isActive": true }
  ],
  "templates": [
    { "id": 1, "sportId": 1, "leagueId": 8, "participantId": null, "widgetTypeId": 1, "templateName": "live", "darkThemeUrl": "https://...", "lightThemeUrl": "https://...", "thumbnailLightUrl": null, "thumbnailDarkUrl": null, "featured": false, "isActive": true, "sportName": "american-football", "leagueName": "ncaa-fbs", "widgetName": "live-scores", "teamName": null }
  ]
}
```

**Note**: Seed file uses `iconUrl` for leagues; the API will map this to `leagueLogoUrl` during import.

---

## Key Files to Create

| File | Purpose |
|------|---------|
| `src/types/index.ts` | All TypeScript interfaces |
| `src/validation/schemas.ts` | Zod validation schemas |
| `src/models/*.ts` | Mongoose models (5 entities + ApiKey) |
| `src/middleware/auth.middleware.ts` | X-API-Key validation |
| `src/routes/*.ts` | All route definitions |
| `src/controllers/*.ts` | Request handlers |
| `src/services/*.ts` | Business logic |
| `scripts/generate-api-key.ts` | Generate API keys |
| `docker-compose.yml` | MongoDB container |

---

## Environment Variables

```bash
# .env.example
MONGODB_URI=mongodb://localhost:27017/sportpulse
API_PORT=3001
NODE_ENV=development
```

---

## Deliverables (Project 1)

- Complete TypeScript codebase
- Zod validation schemas for all entities
- API key generation script
- README with setup instructions
- Docker Compose for local MongoDB

---

## Changelog

| Date | Phase | Status | Notes |
|------|-------|--------|-------|
| 2025-12-22 | Planning | Complete | Initial plan created with 8 phases |
| 2025-12-22 | Phase 1 | Complete | Project initialized, TypeScript configured, Docker Compose created |
| 2025-12-22 | Phase 2 | Complete | Types and Zod validation schemas defined |
| 2025-12-22 | Phase 3 | Complete | Mongoose models created, seed script implemented |
| 2025-12-22 | Phase 4 | Complete | Auth, error, and logging middleware implemented |
| 2025-12-22 | Phase 5 | Complete | All 5 API endpoints implemented with controllers |
| 2025-12-22 | Phase 6 | Complete | Services layer with embed code generation |
| 2025-12-22 | Phase 7 | Complete | API key generation script created |
| 2025-12-30 | Phase 8 | Complete | README, Jest testing framework, unit & integration tests |
| 2025-12-30 | Refactor | Complete | Renamed project to sportpulse-internal-apis |
| 2025-12-30 | Refactor | Complete | Changed `/widget-types` to nested `/leagues/:leagueId/widget-types` |
| 2025-12-30 | Refactor | Complete | Theme URLs now nullable; embed codes return null when URL unavailable |
