698 lines
21 KiB
Markdown
698 lines
21 KiB
Markdown
# Primeira Liga Stats
|
||||
|
|
|
|||
|
|
A self-hosted, real-time football statistics dashboard for the **Portuguese Primeira Liga** (and optionally UEFA Euro). Built with Next.js 15, PostgreSQL 17, and the [API-Football](https://www.api-football.com/) data provider. The entire stack runs as Docker containers — one command to deploy.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Table of Contents
|
|||
|
|
|
|||
|
|
1. [Overview](#overview)
|
|||
|
|
2. [Tech Stack](#tech-stack)
|
|||
|
|
3. [Architecture](#architecture)
|
|||
|
|
4. [Project Structure](#project-structure)
|
|||
|
|
5. [Prerequisites](#prerequisites)
|
|||
|
|
6. [Quick Start](#quick-start)
|
|||
|
|
7. [Configuration](#configuration)
|
|||
|
|
8. [Environment Variables](#environment-variables)
|
|||
|
|
9. [Features](#features)
|
|||
|
|
10. [Pages & Routes](#pages--routes)
|
|||
|
|
11. [API Endpoints](#api-endpoints)
|
|||
|
|
12. [Database](#database)
|
|||
|
|
13. [Admin Panel](#admin-panel)
|
|||
|
|
14. [Performance & Caching](#performance--caching)
|
|||
|
|
15. [Docker Reference](#docker-reference)
|
|||
|
|
16. [Data Persistence & Backups](#data-persistence--backups)
|
|||
|
|
17. [Changing the Port](#changing-the-port)
|
|||
|
|
18. [API Usage & Rate Limits](#api-usage--rate-limits)
|
|||
|
|
19. [Troubleshooting](#troubleshooting)
|
|||
|
|
20. [Development (Local, without Docker)](#development-local-without-docker)
|
|||
|
|
21. [System Requirements](#system-requirements)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
Primeira Liga Stats is a production-ready web dashboard that displays live and historical football data. It polls [API-Football v3](https://www.api-football.com/documentation-v3) for match data and presents it in a clean, auto-refreshing UI. All configuration — including the API key, color theme, visible sections, and refresh interval — can be changed at runtime through the built-in Admin Panel without restarting the application.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Tech Stack
|
|||
|
|
|
|||
|
|
| Layer | Technology | Version |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Framework | Next.js | 15.1.0 |
|
|||
|
|
| Language | TypeScript | 5.7 |
|
|||
|
|
| Runtime | Node.js | 22 LTS (Alpine) |
|
|||
|
|
| Database | PostgreSQL | 17 Alpine |
|
|||
|
|
| ORM | Prisma | 5.22 |
|
|||
|
|
| UI Components | Radix UI | Latest |
|
|||
|
|
| Styling | Tailwind CSS | 3.4 |
|
|||
|
|
| Icons | Lucide React | 0.460 |
|
|||
|
|
| Container Runtime | Docker / Docker Compose | — |
|
|||
|
|
| Data Source | API-Football v3 | — |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Architecture
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────┐
|
|||
|
|
│ Docker Network │
|
|||
|
|
│ │
|
|||
|
|
│ ┌──────────────────┐ ┌─────────────┐ │
|
|||
|
|
│ │ app container │ │ db container│ │
|
|||
|
|
│ │ (Next.js 15) │◄─┤ (Postgres17)│ │
|
|||
|
|
│ │ Node 22 Alpine │ │ │ │
|
|||
|
|
│ │ Port 3000 │ │ Port 5432 │ │
|
|||
|
|
│ └────────┬─────────┘ └─────────────┘ │
|
|||
|
|
│ │ │
|
|||
|
|
└───────────┼─────────────────────────────┘
|
|||
|
|
│
|
|||
|
|
▼ HTTP
|
|||
|
|
http://localhost:3000
|
|||
|
|
|
|||
|
|
▲ HTTPS
|
|||
|
|
│
|
|||
|
|
API-Football v3 (external)
|
|||
|
|
v3.football.api-sports.io
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The Next.js app acts as a **backend-for-frontend**: all API-Football requests are proxied through Next.js API routes. The browser never sees the API key. Settings and request logs are stored in PostgreSQL via Prisma.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Project Structure
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
primeira_liga_stats/
|
|||
|
|
├── Dockerfile # Multi-stage Docker build (Node 22)
|
|||
|
|
├── docker-compose.yml # Orchestrates app + db services
|
|||
|
|
├── .dockerignore # Excludes node_modules, .next, etc.
|
|||
|
|
├── start.sh # Container entrypoint: migrate + start
|
|||
|
|
├── init-db/
|
|||
|
|
│ └── 01-init.sql # Auto-runs on first DB container start
|
|||
|
|
└── nextjs_space/
|
|||
|
|
├── package.json.docker # Dependencies (used by Dockerfile)
|
|||
|
|
├── next.config.js # Next.js config (standalone, compression)
|
|||
|
|
├── tailwind.config.ts # Tailwind theme + dark mode config
|
|||
|
|
├── tsconfig.json # TypeScript compiler config
|
|||
|
|
├── postcss.config.js # PostCSS config
|
|||
|
|
├── components.json # shadcn/ui component registry
|
|||
|
|
├── prisma/
|
|||
|
|
│ └── schema.prisma # Data models: Settings, ApiRequestLog
|
|||
|
|
├── lib/
|
|||
|
|
│ ├── db.ts # Prisma client singleton (with dev logging)
|
|||
|
|
│ ├── settings.ts # Settings fetch + 30s in-memory cache
|
|||
|
|
│ ├── settings-context.tsx # React context for settings
|
|||
|
|
│ ├── api-logger.ts # Non-blocking request logger + pruning
|
|||
|
|
│ ├── types.ts # Shared TypeScript types
|
|||
|
|
│ └── utils.ts # Utility helpers (cn, etc.)
|
|||
|
|
├── app/
|
|||
|
|
│ ├── layout.tsx # Root layout (Inter font, metadata)
|
|||
|
|
│ ├── page.tsx # Main dashboard page (client component)
|
|||
|
|
│ ├── globals.css # Global CSS + Tailwind directives
|
|||
|
|
│ ├── theme-provider.tsx # next-themes provider
|
|||
|
|
│ ├── _components/ # Page-level UI components
|
|||
|
|
│ │ ├── live-matches.tsx # Live score cards with auto-refresh
|
|||
|
|
│ │ ├── standings.tsx # Full league table
|
|||
|
|
│ │ ├── top-scorers.tsx # Top scorers leaderboard
|
|||
|
|
│ │ ├── recent-results.tsx # Last 10 match results
|
|||
|
|
│ │ ├── upcoming-fixtures.tsx # Next 10 fixtures
|
|||
|
|
│ │ └── countdown-timer.tsx # Refresh countdown in header
|
|||
|
|
│ ├── admin/
|
|||
|
|
│ │ ├── layout.tsx # Admin layout wrapper
|
|||
|
|
│ │ ├── page.tsx # Admin dashboard
|
|||
|
|
│ │ └── _components/
|
|||
|
|
│ │ └── api-stats-chart.tsx # Request volume chart
|
|||
|
|
│ └── api/
|
|||
|
|
│ ├── fixtures/route.ts # GET /api/fixtures?type=live|last|next
|
|||
|
|
│ ├── standings/route.ts # GET /api/standings
|
|||
|
|
│ ├── topscorers/route.ts # GET /api/topscorers
|
|||
|
|
│ ├── settings/route.ts # GET /api/settings (public, no API key)
|
|||
|
|
│ └── admin/
|
|||
|
|
│ ├── settings/route.ts # GET|PUT /api/admin/settings
|
|||
|
|
│ └── stats/route.ts # GET /api/admin/stats
|
|||
|
|
├── components/
|
|||
|
|
│ ├── theme-provider.tsx
|
|||
|
|
│ └── ui/ # Radix-based shadcn/ui components
|
|||
|
|
└── public/
|
|||
|
|
├── favicon.svg
|
|||
|
|
└── og-image.png
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Prerequisites
|
|||
|
|
|
|||
|
|
- **Docker Engine** 20.10 or later
|
|||
|
|
- **Docker Compose** v2.0 or later (`docker compose` not `docker-compose`)
|
|||
|
|
- At least **1 GB RAM** available to Docker
|
|||
|
|
- At least **2 GB free disk space**
|
|||
|
|
- An **API-Football API key** (free tier available at [api-football.com](https://www.api-football.com/))
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Quick Start
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. Clone or unzip the project
|
|||
|
|
cd primeira_liga_stats
|
|||
|
|
|
|||
|
|
# 2. Start everything
|
|||
|
|
docker compose up -d
|
|||
|
|
|
|||
|
|
# 3. Open the app (give it ~30 seconds on first run for the build)
|
|||
|
|
open http://localhost:3000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The Admin Panel is at `http://localhost:3000/admin`.
|
|||
|
|
|
|||
|
|
That's it. No other setup is needed. The API key is pre-configured in `docker-compose.yml`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Configuration
|
|||
|
|
|
|||
|
|
All configuration lives in two places:
|
|||
|
|
|
|||
|
|
### 1. `docker-compose.yml` — environment variables (startup time)
|
|||
|
|
|
|||
|
|
Edit this file to change the API key, port, or secrets before starting the containers. Requires a container restart to take effect.
|
|||
|
|
|
|||
|
|
### 2. Admin Panel at `/admin` — runtime settings
|
|||
|
|
|
|||
|
|
Change site name, colors, visible sections, refresh interval, API key, and league without restarting. Settings are persisted to the database and take effect immediately.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Environment Variables
|
|||
|
|
|
|||
|
|
These are set in the `app` service of `docker-compose.yml`:
|
|||
|
|
|
|||
|
|
| Variable | Default | Description |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `DATABASE_URL` | `postgresql://primeiraliga:primeiraliga2024@db:5432/primeiraliga` | Full Postgres connection string. Includes pool settings. |
|
|||
|
|
| `API_FOOTBALL_API_KEY` | *(pre-configured)* | Your API-Football v3 key. Can also be set via Admin Panel. |
|
|||
|
|
| `NEXTAUTH_URL` | `http://localhost:3000` | Public URL of the app. Change this when deploying to a domain. |
|
|||
|
|
| `NEXTAUTH_SECRET` | *(pre-configured)* | Secret used to sign sessions. Change this in production. |
|
|||
|
|
| `NODE_ENV` | `production` | Set automatically. Do not change. |
|
|||
|
|
|
|||
|
|
> **Security note:** Before deploying publicly, generate a new `NEXTAUTH_SECRET` with `openssl rand -base64 32` and update `docker-compose.yml`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Features
|
|||
|
|
|
|||
|
|
### Main Dashboard (`/`)
|
|||
|
|
|
|||
|
|
- **Live Matches** — real-time scores with elapsed time indicator; auto-refreshes every 30 seconds when there are live games
|
|||
|
|
- **League Standings** — full table with position, team logo, played/won/drawn/lost, goal difference, and points
|
|||
|
|
- **Top Scorers** — ranked list of top goal scorers with player photo, team, and statistics
|
|||
|
|
- **Recent Results** — last 10 completed match results
|
|||
|
|
- **Upcoming Fixtures** — next 10 scheduled matches with date and time
|
|||
|
|
- **Countdown Timer** — header widget showing time until next data refresh
|
|||
|
|
- **Configurable theme** — primary and secondary colors applied across gradients and accents
|
|||
|
|
|
|||
|
|
### Admin Panel (`/admin`)
|
|||
|
|
|
|||
|
|
- Toggle each dashboard section on/off
|
|||
|
|
- Change site name and brand colors
|
|||
|
|
- Switch between **Primeira Liga** (League ID 94, season 2024) and **UEFA Euro 2024** (League ID 4)
|
|||
|
|
- Update the API-Football API key at runtime
|
|||
|
|
- View API request statistics: total requests, today's requests, average response time, success rate, and an hourly request volume chart
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Pages & Routes
|
|||
|
|
|
|||
|
|
| Route | Type | Description |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `/` | Client page | Main stats dashboard |
|
|||
|
|
| `/admin` | Client page | Admin configuration panel |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API Endpoints
|
|||
|
|
|
|||
|
|
All routes are internal — they proxy requests to API-Football and return JSON. The browser never sends the API key directly.
|
|||
|
|
|
|||
|
|
### `GET /api/fixtures?type=<live|last|next>`
|
|||
|
|
|
|||
|
|
Fetches fixtures from API-Football.
|
|||
|
|
|
|||
|
|
| `type` param | Data returned | Revalidation |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `live` | Currently live matches | 30 seconds |
|
|||
|
|
| `last` | Last 10 completed results | 5 minutes |
|
|||
|
|
| `next` | Next 10 upcoming fixtures | 5 minutes |
|
|||
|
|
|
|||
|
|
Responses include `Cache-Control: public, s-maxage=N, stale-while-revalidate=2N` headers.
|
|||
|
|
|
|||
|
|
### `GET /api/standings`
|
|||
|
|
|
|||
|
|
Returns the full league standings table. Cached for 5 minutes.
|
|||
|
|
|
|||
|
|
### `GET /api/topscorers`
|
|||
|
|
|
|||
|
|
Returns the top scorers list. Cached for 5 minutes.
|
|||
|
|
|
|||
|
|
### `GET /api/settings`
|
|||
|
|
|
|||
|
|
Returns public settings (no API key). Used by the frontend to load theme, visibility flags, and refresh interval.
|
|||
|
|
|
|||
|
|
### `GET /api/admin/settings`
|
|||
|
|
|
|||
|
|
Returns full settings including the API key. Admin use only.
|
|||
|
|
|
|||
|
|
### `PUT /api/admin/settings`
|
|||
|
|
|
|||
|
|
Updates settings. Accepts a JSON body with any subset of the settings fields. Automatically busts the server-side settings cache.
|
|||
|
|
|
|||
|
|
### `GET /api/admin/stats`
|
|||
|
|
|
|||
|
|
Returns API request analytics: total count, today's count, per-endpoint breakdown, hourly chart data (last 24h), average response time, and success rate. All database queries run in parallel via `Promise.all`.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Database
|
|||
|
|
|
|||
|
|
### Engine
|
|||
|
|
|
|||
|
|
PostgreSQL 17 (Alpine), running in its own container (`primeira_liga_db`).
|
|||
|
|
|
|||
|
|
### Connection
|
|||
|
|
|
|||
|
|
| Property | Value |
|
|||
|
|
|---|---|
|
|||
|
|
| Host (from app container) | `db` |
|
|||
|
|
| Port | `5432` |
|
|||
|
|
| Database | `primeiraliga` |
|
|||
|
|
| User | `primeiraliga` |
|
|||
|
|
| Password | `primeiraliga2024` |
|
|||
|
|
|
|||
|
|
### Schema
|
|||
|
|
|
|||
|
|
Managed by Prisma. There are two tables:
|
|||
|
|
|
|||
|
|
**`Settings`** — singleton row (`id = 'main'`) storing all runtime configuration.
|
|||
|
|
|
|||
|
|
| Column | Type | Default |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `id` | TEXT (PK) | `'main'` |
|
|||
|
|
| `apiKey` | TEXT? | `null` |
|
|||
|
|
| `selectedLeague` | TEXT | `'primeira_liga'` |
|
|||
|
|
| `leagueId` | INT | `94` |
|
|||
|
|
| `season` | INT | `2024` |
|
|||
|
|
| `siteName` | TEXT | `'Primeira Liga Stats'` |
|
|||
|
|
| `primaryColor` | TEXT | `'#E42518'` |
|
|||
|
|
| `secondaryColor` | TEXT | `'#006600'` |
|
|||
|
|
| `showLiveMatches` | BOOL | `true` |
|
|||
|
|
| `showStandings` | BOOL | `true` |
|
|||
|
|
| `showTopScorers` | BOOL | `true` |
|
|||
|
|
| `showRecentResults` | BOOL | `true` |
|
|||
|
|
| `showUpcoming` | BOOL | `true` |
|
|||
|
|
| `refreshInterval` | INT | `300` (seconds) |
|
|||
|
|
| `createdAt` | TIMESTAMP | auto |
|
|||
|
|
| `updatedAt` | TIMESTAMP | auto |
|
|||
|
|
|
|||
|
|
**`ApiRequestLog`** — append-only log of every outbound API-Football call.
|
|||
|
|
|
|||
|
|
| Column | Type | Description |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `id` | TEXT (PK) | cuid() |
|
|||
|
|
| `endpoint` | TEXT | e.g. `/fixtures?type=live` |
|
|||
|
|
| `status` | INT | HTTP status code |
|
|||
|
|
| `duration` | INT | Milliseconds |
|
|||
|
|
| `createdAt` | TIMESTAMP | Indexed for fast range queries |
|
|||
|
|
|
|||
|
|
Logs older than 7 days are automatically pruned in the background (at most once per hour).
|
|||
|
|
|
|||
|
|
### Initialization
|
|||
|
|
|
|||
|
|
On the very first `docker compose up`, PostgreSQL runs `init-db/01-init.sql` which creates both tables and inserts a default `Settings` row. Subsequent starts skip this file. Prisma's `db push` in `start.sh` ensures the schema stays in sync.
|
|||
|
|
|
|||
|
|
### Connecting directly
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker exec -it primeira_liga_db psql -U primeiraliga -d primeiraliga
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Useful SQL:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
-- View current settings
|
|||
|
|
SELECT * FROM "Settings";
|
|||
|
|
|
|||
|
|
-- View last 20 API requests
|
|||
|
|
SELECT endpoint, status, duration, "createdAt"
|
|||
|
|
FROM "ApiRequestLog"
|
|||
|
|
ORDER BY "createdAt" DESC
|
|||
|
|
LIMIT 20;
|
|||
|
|
|
|||
|
|
-- Count requests today
|
|||
|
|
SELECT COUNT(*) FROM "ApiRequestLog"
|
|||
|
|
WHERE "createdAt" >= CURRENT_DATE;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Admin Panel
|
|||
|
|
|
|||
|
|
Navigate to `http://localhost:3000/admin`.
|
|||
|
|
|
|||
|
|
### Layout Settings tab
|
|||
|
|
|
|||
|
|
- **Site Name** — displayed in the browser tab and page header
|
|||
|
|
- **Primary Color** — used for live indicators, headings, and gradient left side
|
|||
|
|
- **Secondary Color** — used for gradient right side and accent elements
|
|||
|
|
- **Section toggles** — individually enable/disable Live Matches, Standings, Top Scorers, Recent Results, Upcoming Fixtures
|
|||
|
|
- **Refresh Interval** — how often the dashboard auto-refreshes (in seconds; default 300)
|
|||
|
|
|
|||
|
|
### API Configuration tab
|
|||
|
|
|
|||
|
|
- **API Key** — view the currently configured key (masked) or enter a new one. Saved to the database immediately; no restart needed.
|
|||
|
|
- **League Switcher** — toggle between Primeira Liga 2024/25 (League ID 94) and UEFA Euro 2024 (League ID 4)
|
|||
|
|
|
|||
|
|
### Statistics tab
|
|||
|
|
|
|||
|
|
- Total API requests logged
|
|||
|
|
- Requests made today
|
|||
|
|
- Average response time (ms)
|
|||
|
|
- Success rate (%)
|
|||
|
|
- Hourly bar chart of the last 24 hours
|
|||
|
|
- Per-endpoint request breakdown
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Performance & Caching
|
|||
|
|
|
|||
|
|
### Server-side settings cache
|
|||
|
|
|
|||
|
|
`lib/settings.ts` caches the Settings row in process memory for 30 seconds. This means API routes don't make a database round-trip on every request. The cache is immediately invalidated whenever settings are updated via `PUT /api/admin/settings`.
|
|||
|
|
|
|||
|
|
### Next.js `fetch` cache
|
|||
|
|
|
|||
|
|
All outbound calls to API-Football use Next.js's extended `fetch` with `next: { revalidate: N }`:
|
|||
|
|
|
|||
|
|
- Live fixtures: revalidate every **30 seconds**
|
|||
|
|
- Standings, top scorers, last/next fixtures: revalidate every **5 minutes**
|
|||
|
|
|
|||
|
|
### HTTP Cache-Control headers
|
|||
|
|
|
|||
|
|
API route responses include `Cache-Control: public, s-maxage=N, stale-while-revalidate=2N`, making them compatible with CDN edge caching if you front the app with Cloudflare or a similar proxy.
|
|||
|
|
|
|||
|
|
### Non-blocking logging
|
|||
|
|
|
|||
|
|
`logApiRequest()` in `lib/api-logger.ts` is fire-and-forget — it returns immediately and never adds latency to the response. DB write failures are caught silently.
|
|||
|
|
|
|||
|
|
### Parallel database queries
|
|||
|
|
|
|||
|
|
The admin stats endpoint runs all 6 database queries simultaneously with `Promise.all`, reducing response time from ~6× (sequential) to ~1× (parallel) query latency.
|
|||
|
|
|
|||
|
|
### Docker build layer caching
|
|||
|
|
|
|||
|
|
`package.json` is copied and `npm ci` is run before any source code is copied, so dependency installation is only re-run when `package.json` changes (not on every code change).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Docker Reference
|
|||
|
|
|
|||
|
|
### Start
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up -d
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Stop (keep data)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose down
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Stop and delete all data
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose down -v
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### View all logs (live)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose logs -f
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### View app logs only
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose logs -f app
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### View database logs only
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose logs -f db
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Rebuild after code changes
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up -d --build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Restart only the app (not the DB)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose restart app
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Check container status
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose ps
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Container names
|
|||
|
|
|
|||
|
|
| Container | Service |
|
|||
|
|
|---|---|
|
|||
|
|
| `primeira_liga_app` | Next.js application |
|
|||
|
|
| `primeira_liga_db` | PostgreSQL 17 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Data Persistence & Backups
|
|||
|
|
|
|||
|
|
Database data lives in the `postgres_data` Docker volume and persists across `docker compose down` (without `-v`).
|
|||
|
|
|
|||
|
|
### Backup
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker exec primeira_liga_db \
|
|||
|
|
pg_dump -U primeiraliga primeiraliga > backup_$(date +%Y%m%d).sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Restore
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker exec -i primeira_liga_db \
|
|||
|
|
psql -U primeiraliga primeiraliga < backup_20260101.sql
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Full reset (wipe and start fresh)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose down -v
|
|||
|
|
docker compose up -d
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Changing the Port
|
|||
|
|
|
|||
|
|
Edit `docker-compose.yml` under the `app` service:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
ports:
|
|||
|
|
- "8080:3000" # exposes on host port 8080 instead of 3000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Also update `NEXTAUTH_URL` to match:
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
environment:
|
|||
|
|
- NEXTAUTH_URL=http://localhost:8080
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Then restart:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up -d
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## API Usage & Rate Limits
|
|||
|
|
|
|||
|
|
The app uses [API-Football v3](https://www.api-football.com/documentation-v3).
|
|||
|
|
|
|||
|
|
### Free tier limits
|
|||
|
|
|
|||
|
|
- **100 requests per day**
|
|||
|
|
- Data updates available every ~15 minutes on the provider side
|
|||
|
|
|
|||
|
|
### Default request rate in this app
|
|||
|
|
|
|||
|
|
With the default 5-minute refresh interval and 4 data sections (standings, top scorers, live, last/next fixtures), the app makes roughly **4–5 requests per refresh cycle** = ~58–72 requests per 24 hours. This fits within the free tier.
|
|||
|
|
|
|||
|
|
### Conserving requests
|
|||
|
|
|
|||
|
|
- Increase the refresh interval in the Admin Panel (e.g. 600 seconds = 10 minutes)
|
|||
|
|
- Disable sections you don't need (e.g. Top Scorers, Upcoming Fixtures)
|
|||
|
|
- The server-side Next.js `fetch` cache means multiple browser clients hitting the same route share one upstream request
|
|||
|
|
|
|||
|
|
### Monitoring usage
|
|||
|
|
|
|||
|
|
The Admin Panel Statistics tab shows how many requests have been made today and in total.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Troubleshooting
|
|||
|
|
|
|||
|
|
### App won't start
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose logs -f app
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Common causes: Prisma migration failed (DB not ready yet — `start.sh` waits 5 seconds and the healthcheck retries up to 10 times), or a port conflict.
|
|||
|
|
|
|||
|
|
### Database connection error
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose logs -f db
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Wait 10–15 seconds for the DB to initialise on first run. The app container will retry. If it keeps failing, run `docker compose down -v && docker compose up -d` to start fresh.
|
|||
|
|
|
|||
|
|
### Port 3000 already in use
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Find what's using it
|
|||
|
|
lsof -i :3000
|
|||
|
|
|
|||
|
|
# Or just change the port in docker-compose.yml
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### API data not loading / blank sections
|
|||
|
|
|
|||
|
|
1. Verify your API key is valid at [api-football.com](https://www.api-football.com/)
|
|||
|
|
2. Check your daily request limit in the API-Football dashboard
|
|||
|
|
3. Check app logs: `docker compose logs -f app`
|
|||
|
|
4. Open browser DevTools → Network tab and look for failed requests to `/api/*`
|
|||
|
|
|
|||
|
|
### Changes in `docker-compose.yml` not taking effect
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up -d --force-recreate
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Code changes not reflected
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker compose up -d --build
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Prisma client out of sync after schema change
|
|||
|
|
|
|||
|
|
The `start.sh` script runs `prisma db push --skip-generate` on every container start, so schema changes are automatically applied. If you update `schema.prisma`, rebuild the image.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## Development (Local, without Docker)
|
|||
|
|
|
|||
|
|
You can run the Next.js app locally for development while pointing at a local or remote Postgres instance.
|
|||
|
|
|
|||
|
|
### 1. Install dependencies
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd nextjs_space
|
|||
|
|
|
|||
|
|
# Copy the Docker package.json as the local one
|
|||
|
|
cp package.json.docker package.json
|
|||
|
|
|
|||
|
|
npm install
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Set up environment
|
|||
|
|
|
|||
|
|
Create `nextjs_space/.env.local`:
|
|||
|
|
|
|||
|
|
```env
|
|||
|
|
DATABASE_URL="postgresql://primeiraliga:primeiraliga2024@localhost:5432/primeiraliga"
|
|||
|
|
API_FOOTBALL_API_KEY="your_key_here"
|
|||
|
|
NEXTAUTH_URL="http://localhost:3000"
|
|||
|
|
NEXTAUTH_SECRET="dev-secret-change-in-production"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Start a local Postgres (optional — if not using Docker DB)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker run -d \
|
|||
|
|
--name pg-dev \
|
|||
|
|
-e POSTGRES_USER=primeiraliga \
|
|||
|
|
-e POSTGRES_PASSWORD=primeiraliga2024 \
|
|||
|
|
-e POSTGRES_DB=primeiraliga \
|
|||
|
|
-p 5432:5432 \
|
|||
|
|
postgres:17-alpine
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. Push the schema
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd nextjs_space
|
|||
|
|
npx prisma db push
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. Start the dev server
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
npm run dev
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
App available at `http://localhost:3000`.
|
|||
|
|
|
|||
|
|
### Available scripts
|
|||
|
|
|
|||
|
|
| Script | Description |
|
|||
|
|
|---|---|
|
|||
|
|
| `npm run dev` | Start Next.js dev server with hot reload |
|
|||
|
|
| `npm run build` | Generates Prisma client and builds for production |
|
|||
|
|
| `npm run start` | Start the production build |
|
|||
|
|
| `npm run lint` | Run ESLint |
|
|||
|
|
| `npm run db:push` | Sync Prisma schema to DB (no migration files) |
|
|||
|
|
| `npm run db:migrate` | Apply pending Prisma migrations |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## System Requirements
|
|||
|
|
|
|||
|
|
| Requirement | Minimum | Recommended |
|
|||
|
|
|---|---|---|
|
|||
|
|
| Docker Engine | 20.10 | Latest stable |
|
|||
|
|
| Docker Compose | v2.0 | Latest stable |
|
|||
|
|
| RAM (for Docker) | 1 GB | 2 GB |
|
|||
|
|
| Disk space | 2 GB | 4 GB |
|
|||
|
|
| CPU | 1 core | 2 cores |
|
|||
|
|
| OS | Linux, macOS, Windows (WSL2) | Linux |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## External Links
|
|||
|
|
|
|||
|
|
- [API-Football Documentation](https://www.api-football.com/documentation-v3)
|
|||
|
|
- [API-Football Dashboard (check usage)](https://dashboard.api-football.com/)
|
|||
|
|
- [Next.js 15 Docs](https://nextjs.org/docs)
|
|||
|
|
- [Prisma Docs](https://www.prisma.io/docs)
|
|||
|
|
- [Docker Docs](https://docs.docker.com/)
|
|||
|
|
- [Tailwind CSS Docs](https://tailwindcss.com/docs)
|