# Kompletní Technická Dokumentace - Systém pro Správu Fotbalového Klubu

**Autor:** Tomáš Dvořák  
**Verze:** 1.0  
**Datum:** Říjen 2025  
**Účel:** Maturitní projekt - Prezentace komplexního webového systému pro správu fotbalového klubu

---

## 📋 Obsah

1. [Úvod a Motivace](#1-úvod-a-motivace)
2. [Přehled Projektu](#2-přehled-projektu)
3. [Technologický Stack](#3-technologický-stack)
4. [Architektura Systému](#4-architektura-systému)
5. [Struktura Projektu](#5-struktura-projektu)
6. [Klíčové Funkce](#6-klíčové-funkce)
7. [API a Integrace](#7-api-a-integrace)
8. [Bezpečnost](#8-bezpečnost)
9. [Minimální Požadavky](#9-minimální-požadavky)
10. [Instalace a Konfigurace](#10-instalace-a-konfigurace)
11. [Příklady Kódu](#11-příklady-kódu)
12. [Závěr](#12-závěr)

---

## 1. Úvod a Motivace

### 1.1 Problematika

Moderní sportovní kluby, zejména fotbalové, čelí výzvě efektivní správy obsahu, komunikace s fanoušky a prezentace klubových aktivit. Mnoho klubů, zejména na amatérské úrovni, používá zastaralé nebo rozdrobené systémy, které neumožňují komplexní správu všech aspektů klubového života.

### 1.2 Cíl Projektu

Vytvořit **moderní, plně funkční webový systém** pro správu fotbalového klubu, který:

- **Centralizuje** všechny informace o klubu na jednom místě
- **Automatizuje** získávání výsledků a tabulek z oficiálních zdrojů (FAČR)
- **Usnadňuje** komunikaci s fanoušky prostřednictvím newsletteru a kontaktních formulářů
- **Poskytuje** intuitivní administrační rozhraní pro správu obsahu
- **Zajišťuje** bezpečnost a škálovatelnost pomocí moderních technologií
- **Respektuje** legislativu (GDPR, cookies consent)

### 1.3 Klíčové Požadavky

- **Responsivní design** - plná funkčnost na mobilních zařízeních
- **Real-time aktualizace** - živé skóre zápasů, okamžitá synchronizace dat
- **Multimédia** - galerie, videa z YouTube, klubové loga
- **Analytics** - sledování návštěvnosti a chování uživatelů
- **Personalizace** - přizpůsobení barev a vzhledu dle klubové identity
- **Modulární architektura** - snadná rozšiřitelnost o nové funkce

---

## 2. Přehled Projektu

### 2.1 Popis Systému

**Fotbal Club** je full-stack webová aplikace určená pro kompletní správu fotbalového klubu. Systém kombinuje veřejnou část pro fanoušky a návštěvníky s pokročilým administrátorským rozhraním pro správu obsahu.

### 2.2 Hlavní Komponenty

```
┌─────────────────────────────────────────────────────────────┐
│                    FRONTEND (React SPA)                      │
│  • Veřejný web        • Admin dashboard    • Setup wizard   │
└──────────────────────────┬──────────────────────────────────┘
                           │ REST API (JSON)
┌──────────────────────────┴──────────────────────────────────┐
│                    BACKEND (Go + Gin)                        │
│  • API Controllers   • Auth/JWT   • Business Logic          │
└──────────────────────────┬──────────────────────────────────┘
                           │ GORM
┌──────────────────────────┴──────────────────────────────────┐
│                  DATABASE (PostgreSQL)                       │
│  • Články   • Týmy   • Zápasy   • Uživatelé   • Settings   │
└─────────────────────────────────────────────────────────────┘
```

### 2.3 Funkční Oblasti

| Oblast | Funkce |
|--------|--------|
| **Obsah** | Blog/články, o klubu, galerie, videa |
| **Sport** | Zápasy, tabulky, hráči, týmy, výsledky |
| **Komunikace** | Kontaktní formulář, newsletter, notifikace |
| **Marketing** | Sponzoři, bannery, SEO optimalizace |
| **Správa** | Uživatelé, role, nastavení, analytika |
| **Integrace** | FAČR API, YouTube, Zonerama, Umami Analytics |

---

## 3. Technologický Stack

### 3.1 Backend Technologie

#### **Programovací Jazyk: Go (Golang) 1.23+**

**Odůvodnění volby:**
- **Vysoký výkon** - kompilovaný jazyk s nízkou latencí
- **Concurrency** - nativní podpora paralelního zpracování (goroutines)
- **Typ bezpečnost** - silně typovaný jazyk eliminuje mnoho chyb
- **Jednoduchost** - čistá syntax, snadná údržba
- **Ekosystém** - bohaté knihovny pro web development

#### **Web Framework: Gin**

```go
// Příklad Gin routeru
func main() {
    r := gin.Default()
    
    // Middleware
    r.Use(securityHeaders())
    r.Use(corsMiddleware())
    
    // Routy
    api := r.Group("/api/v1")
    {
        api.GET("/articles", controllers.GetArticles)
        api.POST("/articles", middleware.JWTAuth(), controllers.CreateArticle)
    }
    
    r.Run(":8080")
}
```

**Vlastnosti:**
- Rychlost (40x rychlejší než Martini)
- HTTP/2 podpora
- Middleware systém
- JSON validace
- Error management

#### **ORM: GORM**

```go
// Definice modelu
type Article struct {
    ID          uint           `gorm:"primarykey" json:"id"`
    Title       string         `gorm:"size:255;not null" json:"title"`
    Content     string         `gorm:"type:text" json:"content"`
    Published   bool           `gorm:"default:false" json:"published"`
    PublishedAt *time.Time     `json:"published_at"`
    CategoryID  uint           `json:"category_id"`
    Category    Category       `gorm:"foreignKey:CategoryID" json:"category"`
    CreatedAt   time.Time      `json:"created_at"`
    UpdatedAt   time.Time      `json:"updated_at"`
}
```

**Výhody:**
- Auto-migrace databázových schémat
- Vztahy mezi tabulkami (has-one, has-many, many-to-many)
- Hooks (before/after create, update, delete)
- Transaction management
- Query builder s bezpečnými parametry

#### **Databáze: PostgreSQL 15**

**Klíčové vlastnosti:**
- ACID compliance
- Pokročilé indexování
- Full-text search
- JSON/JSONB podpora
- Replikace a high availability

#### **Další Backend Knihovny**

| Knihovna | Účel |
|----------|------|
| `golang-jwt/jwt` | JWT token generování a validace |
| `golang.org/x/crypto` | Bcrypt hashing hesel |
| `gopkg.in/mail.v2` | SMTP odesílání emailů |
| `joho/godotenv` | Správa environment variables |
| `PuerkitoBio/goquery` | HTML parsing (web scraping FAČR) |
| `vanng822/go-premailer` | Inline CSS pro HTML emaily |

### 3.2 Frontend Technologie

#### **Framework: React 18+**

**Architektura:**
- **Single Page Application (SPA)** - rychlé přechody bez reload
- **Funkcionální komponenty** - hooks-based přístup
- **TypeScript** - typová bezpečnost na frontendu

#### **UI Knihovna: Chakra UI**

```tsx
// Příklad Chakra UI komponenty
import { Box, Button, Heading, Stack } from '@chakra-ui/react';

function ArticleCard({ article }) {
  return (
    <Box 
      bg="white" 
      p={6} 
      rounded="lg" 
      shadow="md"
      _hover={{ shadow: 'lg' }}
    >
      <Heading size="md" mb={4}>{article.title}</Heading>
      <Button colorScheme="brand">Číst více</Button>
    </Box>
  );
}
```

**Výhody:**
- Přístupnost (a11y) out-of-the-box
- Responzivní props systém
- Theming a customizace
- Dark mode podpora
- Komponenty již vyzkoušené v produkci

#### **State Management**

| Nástroj | Použití |
|---------|---------|
| **React Query** | Server state, cache, synchronizace |
| **React Context** | Globální state (auth, theme) |
| **Local State** | Lokální stav komponent |

```tsx
// React Query příklad
import { useQuery } from '@tanstack/react-query';

function ArticlesList() {
  const { data, isLoading, error } = useQuery({
    queryKey: ['articles'],
    queryFn: () => api.get('/articles'),
    staleTime: 5 * 60 * 1000, // 5 minut cache
  });
  
  if (isLoading) return <Spinner />;
  if (error) return <Alert status="error">{error.message}</Alert>;
  
  return <ArticlesGrid articles={data} />;
}
```

#### **Routing: React Router v6**

- Deklarativní routing
- Protected routes s autorizací
- Lazy loading stránek
- Nested routes

### 3.3 DevOps a Nástroje

#### **Kontejnerizace: Docker**

```dockerfile
# Multi-stage build pro optimalizaci
FROM golang:1.23-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o main .

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/main .
EXPOSE 8080
CMD ["./main"]
```

**Výhody:**
- Izolované prostředí
- Reprodukovatelné buildy
- Snadné nasazení
- Konzistentní development/production

#### **Orchestrace: Docker Compose**

- Současné spuštění backend + frontend + databáze
- Automatické volume mapování
- Health checks
- Network izolace

### 3.4 Externí Služby a API

| Služba | Účel | Integrace | Poznámka |
|--------|------|-----------|----------|
| **FAČR API*** | Oficiální výsledky a tabulky českého fotbalu | REST API scraping | *Custom wrapper vytvořen pro tento projekt |
| **YouTube Data API v3** | Klubová videa | iframe embed + data API | [Docs](https://developers.google.com/youtube/v3) |
| **Zonerama*** | Fotogalerie | HTML scraping | *Custom scraper vytvořen (Zonerama nemá veřejné API) |
| **Umami Analytics** | Web analytics (privacy-first) | JavaScript tracking SDK + REST API | Self-hosted, [Docs](https://umami.is/docs) |
| **OpenRouter AI** | AI generování článků | REST API (GPT modely) | [Docs](https://openrouter.ai/docs) |
| **Google Maps Embed** | Lokace klubu | Maps Embed API | [Docs](https://developers.google.com/maps/documentation/embed) |

**Poznámka:** API označená hvězdičkou (*) byly vytvořeny specificky pro tento projekt jako custom wrappery/scrapery, protože oficiální veřejné API není dostupné nebo je omezené.

---

## 4. Architektura Systému

### 4.1 Celková Architektura

Aplikace využívá **třívrstvou architekturu** s jasným oddělením zodpovědností:

```
┌─────────────────────────────────────────────────────────────────┐
│                        PREZENTAČNÍ VRSTVA                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │   Veřejný    │  │    Admin     │  │    Setup     │          │
│  │     Web      │  │  Dashboard   │  │    Wizard    │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
│           React Components + Chakra UI + React Router           │
└────────────────────────────┬────────────────────────────────────┘
                             │
                      REST API (JSON)
                      JWT Authentication
                             │
┌────────────────────────────┴────────────────────────────────────┐
│                      APLIKAČNÍ VRSTVA                            │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │ Controllers  │  │  Middleware  │  │   Services   │          │
│  │  (Handlers)  │  │  Auth/CORS   │  │  (Business)  │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
│            Gin Framework + GORM + Custom Logic                  │
└────────────────────────────┬────────────────────────────────────┘
                             │
                      ORM (GORM)
                      SQL Queries
                             │
┌────────────────────────────┴────────────────────────────────────┐
│                       DATOVÁ VRSTVA                              │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐          │
│  │  PostgreSQL  │  │   Cache      │  │   Uploads    │          │
│  │   Database   │  │   (JSON)     │  │   (Files)    │          │
│  └──────────────┘  └──────────────┘  └──────────────┘          │
└─────────────────────────────────────────────────────────────────┘
```

### 4.2 Backend Architektura (Go)

#### **Vrstvová struktura:**

1. **Controllers (Ovladače)**
   - Zpracování HTTP požadavků
   - Validace vstupních dat
   - Volání business logiky
   - Formátování odpovědí

2. **Middleware**
   - Autentizace (JWT)
   - Autorizace (role-based)
   - Rate limiting
   - CORS
   - Security headers

3. **Services (Služby)**
   - Business logika
   - Komplexní operace
   - Integrace s externími API
   - Background joby

4. **Models (Modely)**
   - Datové struktury
   - Databázové schéma
   - Vztahy mezi tabulkami

#### **Příklad Request Flow:**

```
1. HTTP Request → Gin Router
2. Router → Middleware Stack
   ├─ CORS Middleware
   ├─ Security Headers
   ├─ JWT Authentication
   └─ Rate Limiting
3. Middleware → Controller
4. Controller → Service (Business Logic)
5. Service → GORM (Database)
6. GORM → PostgreSQL
7. Response: PostgreSQL → GORM → Service → Controller → JSON
```

### 4.3 Frontend Architektura (React)

#### **Komponentová architektura:**

```
src/
├── components/          # Znovupoužitelné komponenty
│   ├── common/         # Obecné UI prvky (Button, Card, etc.)
│   ├── admin/          # Admin-specifické komponenty
│   ├── layout/         # Layout komponenty (Header, Footer)
│   └── widgets/        # Složitější widgety
├── pages/              # Stránky (route komponenty)
│   ├── admin/          # Admin stránky
│   └── legal/          # Právní stránky
├── contexts/           # React Context (global state)
├── hooks/              # Custom React hooks
├── services/           # API klienti
└── layouts/            # Layout wrappery
```

#### **State Management Pattern:**

```tsx
// 1. Server State (React Query)
const { data, isLoading } = useQuery({
  queryKey: ['articles'],
  queryFn: fetchArticles,
});

// 2. Global State (React Context)
const { user, isAuthenticated } = useAuth();

// 3. Local State (useState)
const [isOpen, setIsOpen] = useState(false);
```

### 4.4 Datový Model

#### **Základní Entity:**

```sql
-- Uživatelé a autentizace
users (id, email, password_hash, role, created_at)
password_resets (id, user_id, token, expires_at)

-- Obsah
articles (id, title, content, published, category_id, created_at)
categories (id, name, slug, color)

-- Sport
teams (id, name, age_category, division)
players (id, name, position, number, team_id)
matches (external_id, home_team, away_team, score, date)
match_overrides (external_match_id, home_name, away_name, home_logo, away_logo)

-- Galerie
gallery_albums (id, title, cover_url, photo_count, zonerama_id)

-- Komunikace
contact_messages (id, name, email, subject, message, status, read_at)
newsletter_subscriptions (id, email, status, preferences, subscribed_at)

-- Konfigurace
settings (id, club_name, club_colors, smtp_config, newsletter_enabled)
competition_aliases (code, display_name, priority, hidden)

-- Analytics
visitor_events (id, event_type, page_url, user_agent, ip_hash, created_at)
```

#### **Vztahy:**

```
Article N:1 Category
Article N:1 User (author)
Player N:1 Team
Match N:1 Competition
NewsletterSubscription 1:N EmailLog
ContactMessage 1:1 ContactCategory
```

### 4.5 API Design Pattern

Aplikace využívá **RESTful API** s konzistentním názvoslovím:

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/api/v1/articles` | Seznam článků | Ne |
| GET | `/api/v1/articles/:id` | Detail článku | Ne |
| POST | `/api/v1/articles` | Vytvoření článku | Ano (JWT) |
| PUT | `/api/v1/articles/:id` | Aktualizace článku | Ano (JWT) |
| DELETE | `/api/v1/articles/:id` | Smazání článku | Ano (JWT + Admin) |

**Response formát:**

```json
// Úspěch
{
  "success": true,
  "data": {
    "id": 1,
    "title": "Název článku"
  },
  "message": "Article created successfully"
}

// Chyba
{
  "success": false,
  "error": "Validation failed",
  "details": {
    "title": "Title is required"
  }
}
```

---

## 5. Struktura Projektu

### 5.1 Backend Struktura

```
fotbal-club/
├── main.go                    # Vstupní bod aplikace
├── go.mod                     # Go dependencies
├── go.sum                     # Dependency checksums
├── .env                       # Environment variables
├── Dockerfile                 # Production build
├── Dockerfile.dev             # Development build
├── docker-compose.yml         # Multi-container orchestrace
├── Makefile                   # Build & run skripty
│
├── internal/                  # Privátní aplikační kód
│   ├── config/               # Konfigurace
│   │   └── config.go         # Načítání ENV variables
│   │
│   ├── controllers/          # HTTP handlery
│   │   ├── base_controller.go       # CRUD operace
│   │   ├── auth_controller.go       # Autentizace
│   │   ├── contact_controller.go    # Kontaktní formulář
│   │   ├── facr_controller.go       # FAČR integrace
│   │   ├── gallery_controller.go    # Galerie
│   │   ├── ai_controller.go         # AI generování
│   │   ├── analytics_controller.go  # Analytika
│   │   └── ...
│   │
│   ├── middleware/           # Middleware funkce
│   │   ├── auth.go          # JWT ověření
│   │   ├── admin.go         # Role kontrola
│   │   └── ratelimit.go     # Rate limiting
│   │
│   ├── models/              # Databázové modely
│   │   ├── models.go        # Společné modely (User, Article, etc.)
│   │   ├── contact.go       # Kontaktní modely
│   │   ├── scoreboard.go    # Scoreboard state
│   │   └── ...
│   │
│   ├── routes/              # Route definice
│   │   ├── routes.go        # Hlavní router
│   │   ├── analytics_routes.go
│   │   └── ...
│   │
│   └── services/            # Business logika
│       ├── prefetch.go      # Background cache refresh
│       ├── newsletter_automation.go  # Auto newslettery
│       └── gallery_service.go
│
├── pkg/                     # Znovupoužitelné balíčky
│   ├── database/           # DB utilities
│   │   ├── db.go          # Inicializace
│   │   ├── migrate.go     # Migrace
│   │   └── seed.go        # Seed data
│   ├── email/             # Email service
│   │   └── email.go
│   ├── logger/            # Logging
│   └── utils/             # Helper funkce
│
├── database/              # SQL migrace
│   └── migrations/
│
├── templates/             # HTML šablony pro emaily
│   └── emails/
│       ├── newsletter.html
│       ├── contact_confirm.html
│       └── password_reset.html
│
├── static/                # Statické soubory
│   └── dist/
│
├── uploads/               # Nahrané soubory
│   ├── articles/
│   ├── sponsors/
│   └── players/
│
└── cache/                 # JSON cache soubory
    ├── articles.json
    ├── matches.json
    └── standings.json
```

### 5.2 Frontend Struktura

```
frontend/
├── public/                    # Statické public soubory
│   ├── index.html
│   ├── favicon.ico
│   └── manifest.json
│
├── src/
│   ├── index.tsx             # Vstupní bod React app
│   ├── App.tsx               # Hlavní komponenta s routingem
│   ├── config.ts             # Frontend konfigurace
│   │
│   ├── components/           # React komponenty
│   │   ├── common/          # Sdílené komponenty
│   │   │   ├── Button.tsx
│   │   │   ├── Card.tsx
│   │   │   └── Modal.tsx
│   │   ├── admin/           # Admin komponenty
│   │   │   ├── ArticleForm.tsx
│   │   │   ├── UserManager.tsx
│   │   │   └── ...
│   │   ├── layout/          # Layout komponenty
│   │   │   ├── Navbar.tsx
│   │   │   ├── Footer.tsx
│   │   │   └── Sidebar.tsx
│   │   ├── seo/             # SEO komponenty
│   │   │   └── DefaultSEO.tsx
│   │   └── widgets/         # Specializované widgety
│   │       ├── MatchCard.tsx
│   │       ├── ArticleGrid.tsx
│   │       └── ...
│   │
│   ├── pages/               # Stránkové komponenty
│   │   ├── HomePage.tsx
│   │   ├── BlogPage.tsx
│   │   ├── ContactPage.tsx
│   │   ├── admin/
│   │   │   ├── AdminDashboardPage.tsx
│   │   │   ├── ArticlesAdminPage.tsx
│   │   │   └── ...
│   │   └── legal/
│   │       ├── PrivacyPolicyPage.tsx
│   │       └── TermsPage.tsx
│   │
│   ├── contexts/            # React Context providers
│   │   ├── AuthContext.tsx       # Autentizace state
│   │   └── ClubThemeContext.tsx  # Klubové barvy
│   │
│   ├── hooks/               # Custom React hooks
│   │   ├── useAuth.ts
│   │   ├── useFacrApi.ts
│   │   ├── useSettings.ts
│   │   └── useUmami.ts      # Analytics tracking
│   │
│   ├── services/            # API klienti
│   │   ├── api.ts           # Axios instance
│   │   ├── auth.service.ts
│   │   ├── articles.service.ts
│   │   └── ...
│   │
│   ├── layouts/             # Layout wrappery
│   │   ├── AdminLayout.tsx
│   │   └── PublicLayout.tsx
│   │
│   ├── assets/              # Obrázky, fonty, styly
│   │   ├── images/
│   │   ├── fonts/
│   │   └── styles/
│   │
│   └── types/               # TypeScript typy
│       └── index.d.ts
│
├── package.json             # NPM dependencies
├── tsconfig.json            # TypeScript konfigurace
└── .env                     # Environment variables
```

### 5.3 Klíčové Konfigurační Soubory

#### **Backend: `.env`**
```bash
# Application
APP_ENV=development
PORT=8080
DEBUG=true

# Database
DATABASE_URL=postgres://user:pass@localhost:5432/fotbal_club

# JWT
JWT_SECRET=your_secret_key_here
JWT_EXPIRATION_HOURS=24

# Email (SMTP)
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your_email@example.com
SMTP_PASSWORD=password

# File Uploads
UPLOAD_DIR=./uploads
MAX_UPLOAD_SIZE=10485760  # 10MB

# External APIs
OPENROUTER_API_KEY=sk-or-v1-...
UMAMI_URL=https://analytics.example.com
```

#### **Frontend: `.env`**
```bash
REACT_APP_API_URL=http://localhost:8080/api/v1
REACT_APP_FACR_API_BASE_URL=http://localhost:8080/api/v1/facr
REACT_APP_HOMEPAGE_LAYOUT=classic
```

---

## 6. Klíčové Funkce

### 6.1 Systém Článků a Blogu

**Popis:** Komplexní systém pro správu klubového obsahu s podporou kategorií, náhledů, publikování a SEO.

#### **Hlavní Vlastnosti:**

- ✅ Rich text editor s podporou obrázků a YouTube videí
- ✅ Kategorizace článků (zápasy, události, novinky)
- ✅ Plánované publikování (draft → published)
- ✅ Featured články na homepage
- ✅ Automatické generování slugů pro SEO-friendly URL
- ✅ Sledování počtu přečtení
- ✅ Propojení článku s konkrétním zápasem

#### **Technická Implementace:**

```go
// Backend Model
type Article struct {
    ID           uint           `gorm:"primarykey" json:"id"`
    Title        string         `gorm:"size:255;not null" json:"title"`
    Slug         string         `gorm:"size:255;uniqueIndex" json:"slug"`
    Excerpt      string         `gorm:"type:text" json:"excerpt"`
    Content      string         `gorm:"type:text" json:"content"`
    FeaturedImage string        `json:"featured_image"`
    Published    bool           `gorm:"default:false" json:"published"`
    PublishedAt  *time.Time     `json:"published_at"`
    ViewCount    int            `gorm:"default:0" json:"view_count"`
    CategoryID   uint           `json:"category_id"`
    Category     Category       `gorm:"foreignKey:CategoryID" json:"category"`
    AuthorID     uint           `json:"author_id"`
    Author       User           `gorm:"foreignKey:AuthorID" json:"author"`
    CreatedAt    time.Time      `json:"created_at"`
    UpdatedAt    time.Time      `json:"updated_at"`
}
```

**Frontend Flow:**
1. Admin vytvoří článek přes formulář s WYSIWYG editorem
2. Nahraje featured obrázek přes drag-and-drop
3. Vybere kategorii a nastaví publikování
4. Článek se zobrazí na blogu a homepage (pokud je featured)

### 6.2 FAČR Integrace - Automatické Výsledky

**Popis:** Systém automaticky stahuje a zobrazuje výsledky, rozpisy a tabulky z oficiálního webu Fotbalové asociace České republiky.

#### **Jak to funguje:**

```
┌─────────────┐
│  Frontend   │ 
│   Request   │ ───────┐
└─────────────┘        │
                       ▼
              ┌─────────────────┐
              │  Backend Proxy  │
              │  (FACR Ctrl)    │
              └─────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │   Web Scraping  │
              │  (goquery HTML) │
              └─────────────────┘
                       │
                       ▼
              ┌─────────────────┐
              │  facr.fotbal.cz │
              │  (Official API) │
              └─────────────────┘
```

#### **Klíčové Komponenty:**

**1. Club Search**
```go
// Vyhledání klubu podle názvu
GET /api/v1/facr/club/search?q=Sokol
Response: [
  { "id": "123", "name": "TJ Sokol Praha", "type": "klub" }
]
```

**2. Match Schedule**
```go
// Získání rozpisů zápasů
GET /api/v1/facr/club/klub/123
Response: {
  "matches": [
    {
      "date": "2025-10-15T15:00:00Z",
      "home_team": "TJ Sokol Praha",
      "away_team": "FC Sparta B",
      "competition": "III. třída - sk. A"
    }
  ]
}
```

**3. Competition Tables**
```go
// Tabulky soutěží
GET /api/v1/facr/club/klub/123/table
Response: {
  "tables": [
    {
      "competition": "III. třída - sk. A",
      "rows": [
        {
          "position": 1,
          "team": "TJ Sokol Praha",
          "played": 10,
          "wins": 8,
          "points": 24
        }
      ]
    }
  ]
}
```

#### **Cache Systém:**

- **Interval:** Každých 30 minut automatický refresh
- **Storage:** JSON soubory v `./cache/` adresáři
- **Důvod:** Minimalizace zátěže na FAČR server + rychlé načítání

```go
// Prefetch service
func StartPrefetcher(targetURL string) {
    ticker := time.NewTicker(30 * time.Minute)
    go func() {
        for range ticker.C {
            fetchAndCacheEndpoint(targetURL + "/matches")
            fetchAndCacheEndpoint(targetURL + "/standings")
        }
    }()
}
```

#### **Aliasy Soutěží:**

Administrátor může upravit názvy soutěží pro lepší zobrazení:

| Originální název | Alias | Priorita |
|------------------|-------|----------|
| III. třída - skupina A | Třetí třída A | 1 |
| Přebor dorostu U19 | Dorost U19 | 2 |

### 6.3 Newsletter a Email Marketing

**Popis:** Plnohodnotný newsletter systém s automatickými digesty, segmentací odběratelů a sledováním metrik.

#### **Typy Newsletterů:**

1. **Týdenní Digest**
   - Automaticky každý pátek v 18:00
   - Obsahuje nové články z týdne
   - Nadcházející zápasy

2. **Match Alerts**
   - 24h před zápasem
   - Info o soupeři, místě konání
   - Odkaz na mapy

3. **Blog Notifications**
   - Při publikování nového článku
   - Pouze pro odběratele "blog" kategorie

4. **Results Digest**
   - Po víkendu s výsledky
   - Aktuální tabulka

#### **Technická Implementace:**

```go
// Newsletter Automation Service
type NewsletterAutomation struct {
    db           *gorm.DB
    emailService *email.EmailService
    ticker       *time.Ticker
}

func (na *NewsletterAutomation) Start() {
    // Denní kontrola v 18:00
    na.ticker = time.NewTicker(1 * time.Hour)
    go func() {
        for range na.ticker.C {
            na.checkAndSendScheduled()
        }
    }()
}

func (na *NewsletterAutomation) checkAndSendScheduled() {
    now := time.Now()
    
    // Týdenní digest (pátek 18:00)
    if now.Weekday() == time.Friday && now.Hour() == 18 {
        na.sendWeeklyDigest()
    }
    
    // Match alerts (24h před zápasem)
    upcomingMatches := na.getMatchesIn24Hours()
    for _, match := range upcomingMatches {
        na.sendMatchAlert(match)
    }
}
```

#### **Email Templates:**

```html
<!DOCTYPE html>
<html>
<head>
    <style>
        /* Inline CSS pro kompatibilitu */
        .header { background: {{.ClubPrimaryColor}}; }
        .button { background: {{.ClubSecondaryColor}}; }
    </style>
</head>
<body>
    <div class="header">
        <img src="{{.ClubLogo}}" alt="Logo">
        <h1>{{.ClubName}} Newsletter</h1>
    </div>
    
    <div class="content">
        {{range .Articles}}
        <div class="article">
            <h2>{{.Title}}</h2>
            <p>{{.Excerpt}}</p>
            <a href="{{.URL}}" class="button">Číst více</a>
        </div>
        {{end}}
    </div>
    
    <!-- Tracking pixel -->
    <img src="{{.TrackingPixelURL}}" width="1" height="1">
</body>
</html>
```

#### **Email Tracking:**

- **Open Rate:** 1x1 tracking pixel
- **Click Through Rate:** Wrapped links
- **Unsubscribe:** Token-based unsubscribe link
- **Spam Reports:** Report spam endpoint

```go
// Email metrics
type EmailLog struct {
    ID            uint      `json:"id"`
    RecipientEmail string   `json:"recipient_email"`
    Subject       string    `json:"subject"`
    Type          string    `json:"type"` // weekly, match, blog
    SentAt        time.Time `json:"sent_at"`
    OpenedAt      *time.Time `json:"opened_at"`
    ClickedAt     *time.Time `json:"clicked_at"`
    UnsubscribedAt *time.Time `json:"unsubscribed_at"`
}
```

### 6.4 Galerie a Multimédia

#### **Zonerama Integrace:**

Systém automaticky synchronizuje alba z populární fotogalerie Zonerama.cz.

**Workflow:**
1. Admin zadá Zonerama username
2. Systém načte veřejná alba přes API
3. Admin vybere alba ke zveřejnění
4. Fotky se zobrazí v galerii na webu

```typescript
// Frontend API call
const fetchAlbum = async (albumUrl: string) => {
  const response = await api.post('/admin/gallery/albums/fetch', {
    album_url: albumUrl
  });
  
  return response.data;
};

// Backend scraping
func (gc *GalleryController) FetchAlbum(c *gin.Context) {
    albumURL := c.PostForm("album_url")
    
    // Parse Zonerama album
    album := scrapeZoneramaAlbum(albumURL)
    
    // Save to database
    gc.DB.Create(&album)
    
    c.JSON(200, album)
}
```

#### **YouTube Video Manager:**

```typescript
// Cached YouTube videos from club channel
interface YouTubeVideo {
  id: string;
  title: string;
  thumbnail: string;
  publishedAt: string;
  duration: string;
  viewCount: number;
}

// Backend cache refresh (každých 6 hodin)
func refreshYouTubeCache() {
    videos := fetchFromYouTubeAPI(channelID)
    saveToCache("youtube_videos.json", videos)
}
```

### 6.5 Kontaktní Systém

#### **Multi-Category Contact Form:**

Uživatelé mohou vybrat kategorii dotazu pro efektivní routing:

| Kategorie | Email | Auto-odpověď |
|-----------|-------|--------------|
| Obecný dotaz | info@club.cz | Ano |
| Technická podpora | tech@club.cz | Ano |
| Tisk a media | pr@club.cz | Ne |
| Reklama | marketing@club.cz | Ne |

```typescript
// Frontend form
<Select name="category">
  <option value="general">Obecný dotaz</option>
  <option value="support">Podpora</option>
  <option value="media">Media</option>
</Select>

// Backend handler
func (cc *ContactController) SubmitContactForm(c *gin.Context) {
    var req ContactRequest
    c.BindJSON(&req)
    
    // Save to database
    message := models.ContactMessage{
        Name: req.Name,
        Email: req.Email,
        Category: req.Category,
        Message: req.Message,
        Status: "new",
    }
    cc.DB.Create(&message)
    
    // Send notification to admin
    cc.emailService.SendContactNotification(message)
    
    // Send auto-reply to user
    if shouldSendAutoReply(req.Category) {
        cc.emailService.SendAutoReply(req.Email)
    }
}
```

### 6.6 Analytics a Statistiky

#### **Umami Analytics Integrace:**

Privacy-first analytický nástroj (alternativa k Google Analytics).

**Sledované metriky:**
- Page views
- Unique visitors
- Bounce rate
- Average time on page
- Traffic sources
- Device breakdown

```typescript
// Frontend tracking hook
export const useUmami = () => {
  const trackEvent = (eventName: string, eventData?: object) => {
    if (window.umami) {
      window.umami.track(eventName, eventData);
    }
  };
  
  return { trackEvent };
};

// Usage
const { trackEvent } = useUmami();
trackEvent('article_read', { article_id: 123 });
```

#### **Custom Analytics:**

Kromě Umami systém uchovává vlastní statistiky:

```go
type VisitorEvent struct {
    ID        uint      `json:"id"`
    EventType string    `json:"event_type"` // page_view, article_read, etc.
    PageURL   string    `json:"page_url"`
    UserAgent string    `json:"user_agent"`
    IPHash    string    `json:"ip_hash"` // Anonymizovaná IP (GDPR)
    CreatedAt time.Time `json:"created_at"`
}

// Article popularity
type ArticleStats struct {
    ArticleID  uint `json:"article_id"`
    ViewCount  int  `json:"view_count"`
    ReadTime   int  `json:"avg_read_time_seconds"`
}
```

### 6.7 Live Scoreboard

**Popis:** Real-time skóre display pro živé přenosy zápasů.

#### **Funkce:**

- ⏱️ **Countdown timer** s přesností na sekundy
- 🔄 **Swap sides** - prohození stran
- ⚽ **Live score updates** - aktualizace skóre
- 📊 **Statistics** - střely, rohy, žluté karty
- 🎨 **Color derivation** - automatické barvy z klubových log
- 💾 **Presets** - uložení často používaných nastavení

```typescript
// Frontend scoreboard control
const ScoreboardControl = () => {
  const [scoreData, setScoreData] = useState({
    homeTeam: '',
    awayTeam: '',
    homeScore: 0,
    awayScore: 0,
    timer: 0,
    isRunning: false,
  });
  
  const updateScore = async (team: 'home' | 'away', increment: number) => {
    await api.put('/admin/scoreboard', {
      [`${team}_score`]: scoreData[`${team}Score`] + increment
    });
  };
  
  return (
    <ScoreboardControls 
      data={scoreData}
      onUpdate={updateScore}
    />
  );
};
```

### 6.8 AI Content Generation

**Popis:** Automatické generování článků a obsahu pomocí AI (OpenRouter API).

#### **Podporované Modely:**
- Mistral Small (primary)
- Mistral Nemo (fallback)
- GPT-4 Turbo (premium)

```go
// AI Blog Generation
func (ai *AIController) GenerateBlog(c *gin.Context) {
    var req struct {
        Topic       string `json:"topic"`
        Keywords    []string `json:"keywords"`
        Tone        string `json:"tone"` // professional, casual, excited
        Length      int    `json:"length"` // word count
    }
    c.BindJSON(&req)
    
    prompt := buildBlogPrompt(req)
    
    // Call OpenRouter API
    article := ai.openRouterClient.Generate(prompt, req.Length)
    
    c.JSON(200, gin.H{
        "title": article.Title,
        "content": article.Content,
        "excerpt": article.Excerpt,
    })
}
```

**Příklad použití:**
1. Admin klikne "Generovat článek AI"
2. Zadá téma: "Vítězství v derby 3:1"
3. AI vygeneruje kompletní článek s titulkem a obsahem
4. Admin upraví detaily a publikuje

---

## 7. API a Integrace

### 7.1 REST API Přehled

Aplikace poskytuje kompletní RESTful API pro všechny operace.

#### **Base URL:**
```
http://localhost:8080/api/v1
```

#### **Autentizace:**
```http
Authorization: Bearer <JWT_TOKEN>
```

#### **Content Type:**
```http
Content-Type: application/json
```

### 7.2 API Endpointy - Kategorie

#### **🔐 Autentizace**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| POST | `/auth/login` | Přihlášení uživatele | ❌ |
| POST | `/auth/register` | Registrace nového uživatele | ❌ |
| POST | `/auth/logout` | Odhlášení | ❌ |
| GET | `/auth/me` | Získání aktuálního uživatele | ✅ |
| POST | `/auth/forgot-password` | Zapomenuté heslo | ❌ |
| POST | `/auth/reset-password` | Reset hesla | ❌ |

**Příklad Login Request:**
```bash
curl -X POST http://localhost:8080/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@club.cz",
    "password": "securepassword"
  }'
```

**Response:**
```json
{
  "success": true,
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "id": 1,
    "email": "admin@club.cz",
    "role": "admin",
    "created_at": "2025-01-01T10:00:00Z"
  }
}
```

#### **📝 Články**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/articles` | Seznam článků | ❌ |
| GET | `/articles/:id` | Detail článku | ❌ |
| GET | `/articles/slug/:slug` | Článek podle slug | ❌ |
| GET | `/articles/featured` | Featured články | ❌ |
| POST | `/articles` | Vytvoření článku | ✅ |
| PUT | `/articles/:id` | Aktualizace článku | ✅ |
| DELETE | `/articles/:id` | Smazání článku | ✅ |
| POST | `/articles/:id/read` | Increment počtu čtení | ❌ |

**Query Parameters pro GET /articles:**
```
?page=1
&limit=10
&category=1
&published=true
&sort=created_at
&order=desc
&search=derby
```

#### **⚽ Zápasy a Tabulky**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/matches` | Seznam zápasů | ❌ |
| GET | `/matches/history` | Historie zápasů | ❌ |
| GET | `/standings` | Tabulky soutěží | ❌ |
| GET | `/admin/matches` | Admin view zápasů | ✅ Admin |

#### **🏆 FAČR API**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/facr/club/search?q={query}` | Vyhledání klubu | ❌ |
| GET | `/facr/club/:type/:id` | Info o klubu | ❌ |
| GET | `/facr/club/:type/:id/table` | Tabulky klubu | ❌ |

**Příklad:**
```bash
# Vyhledání klubu
curl "http://localhost:8080/api/v1/facr/club/search?q=Sparta"

# Získání informací
curl "http://localhost:8080/api/v1/facr/club/klub/123"
```

#### **📧 Kontakt a Newsletter**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| POST | `/contact` | Odeslat kontaktní formulář | ❌ |
| POST | `/newsletter/subscribe` | Odběr newsletteru | ❌ |
| POST | `/newsletter/unsubscribe/:email` | Zrušení odběru | ❌ |
| GET | `/admin/newsletter/subscribers` | Seznam odběratelů | ✅ Admin |
| POST | `/admin/newsletter/send` | Odeslat newsletter | ✅ Admin |

#### **📸 Galerie**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/gallery/albums` | Seznam alb | ❌ |
| GET | `/gallery/albums/:id` | Detail alba s fotkami | ❌ |
| POST | `/admin/gallery/albums/fetch` | Načíst album ze Zonerama | ✅ Admin |
| DELETE | `/admin/gallery/albums/:id` | Smazat album | ✅ Admin |

#### **📊 Analytics**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| POST | `/analytics/track` | Trackování eventu | ❌ |
| GET | `/admin/umami/stats` | Umami statistiky | ✅ Admin |
| GET | `/admin/umami/metrics/:type` | Metriky (pageviews, etc.) | ✅ Admin |

#### **⚙️ Nastavení**

| Metoda | Endpoint | Popis | Auth |
|--------|----------|-------|------|
| GET | `/settings` | Veřejná nastavení | ❌ |
| GET | `/admin/settings` | Všechna nastavení | ✅ Admin |
| PUT | `/admin/settings` | Aktualizace nastavení | ✅ Admin |

### 7.3 Externí API Integrace

#### **FAČR Web Scraping**

**Popis:** Backend funguje jako proxy a scraper pro oficiální FAČR web.

**Technologie:**
- `goquery` - HTML parsing
- HTTP client s custom headers
- Error handling a retry logika

```go
func (fc *FACRController) SearchClubs(c *gin.Context) {
    query := c.Query("q")
    
    // Build search URL
    searchURL := fmt.Sprintf(
        "https://is.fotbal.cz/clubs/search?term=%s",
        url.QueryEscape(query),
    )
    
    // Fetch HTML
    resp, err := http.Get(searchURL)
    if err != nil {
        c.JSON(500, gin.H{"error": "Failed to fetch"})
        return
    }
    defer resp.Body.Close()
    
    // Parse HTML with goquery
    doc, _ := goquery.NewDocumentFromReader(resp.Body)
    
    var clubs []Club
    doc.Find(".club-item").Each(func(i int, s *goquery.Selection) {
        club := Club{
            ID:   s.AttrOr("data-id", ""),
            Name: s.Find(".club-name").Text(),
            Type: s.AttrOr("data-type", ""),
        }
        clubs = append(clubs, club)
    })
    
    c.JSON(200, clubs)
}
```

**Rate Limiting:**
- Max 60 requests/hour na FAČR
- Local cache pro 30 minut
- Background prefetch pro populární endpointy

#### **Zonerama Gallery API**

**Dokumentace:** https://www.zonerama.com/

**Workflow:**
1. Admin zadá Zonerama album URL
2. Backend parsuje HTML (Zonerama nemá veřejné REST API)
3. Extrahuje metadata: title, cover, photo count
4. Ukládá do DB s referencí na Zonerama ID
5. Frontend zobrazuje pomocí Zonerama CDN URLs

```go
func scrapeZoneramaAlbum(albumURL string) (*Album, error) {
    resp, err := http.Get(albumURL)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    doc, _ := goquery.NewDocumentFromReader(resp.Body)
    
    album := &Album{
        Title:      doc.Find("h1.album-title").Text(),
        CoverURL:   doc.Find(".album-cover img").AttrOr("src", ""),
        PhotoCount: len(doc.Find(".photo-item").Nodes),
        ZoneramaID: extractIDFromURL(albumURL),
    }
    
    return album, nil
}
```

#### **YouTube Data API v3**

**API Key:** Vyžadován v `.env` jako `YOUTUBE_API_KEY`

**Endpoint:**
```
https://www.googleapis.com/youtube/v3/search
```

**Použití:**
- Automatické načítání videí z klubového kanálu
- Cache refresh každých 6 hodin
- Zobrazení v sekci "Videa"

```go
func fetchYouTubeVideos(channelID string) ([]Video, error) {
    apiKey := os.Getenv("YOUTUBE_API_KEY")
    
    url := fmt.Sprintf(
        "https://www.googleapis.com/youtube/v3/search?part=snippet&channelId=%s&maxResults=12&order=date&type=video&key=%s",
        channelID, apiKey,
    )
    
    resp, err := http.Get(url)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var result YouTubeAPIResponse
    json.NewDecoder(resp.Body).Decode(&result)
    
    var videos []Video
    for _, item := range result.Items {
        videos = append(videos, Video{
            ID:        item.ID.VideoID,
            Title:     item.Snippet.Title,
            Thumbnail: item.Snippet.Thumbnails.Medium.URL,
            Published: item.Snippet.PublishedAt,
        })
    }
    
    return videos, nil
}
```

#### **Umami Analytics**

**Self-hosted:** https://umami.is/

**Integrace:**
1. **Frontend tracking:**
```typescript
// useUmami hook
export const useUmami = () => {
  useEffect(() => {
    const script = document.createElement('script');
    script.src = process.env.REACT_APP_UMAMI_URL + '/script.js';
    script.async = true;
    script.setAttribute('data-website-id', websiteId);
    document.head.appendChild(script);
  }, []);
};
```

2. **Backend API calls:**
```go
// Získání statistik
func (uc *UmamiController) GetStats(c *gin.Context) {
    umamiURL := os.Getenv("UMAMI_URL")
    username := os.Getenv("UMAMI_USERNAME")
    password := os.Getenv("UMAMI_PASSWORD")
    
    // Login to Umami
    token := loginToUmami(umamiURL, username, password)
    
    // Fetch stats
    statsURL := fmt.Sprintf("%s/api/websites/%s/stats", umamiURL, websiteID)
    req, _ := http.NewRequest("GET", statsURL, nil)
    req.Header.Set("Authorization", "Bearer "+token)
    
    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()
    
    var stats Stats
    json.NewDecoder(resp.Body).Decode(&stats)
    
    c.JSON(200, stats)
}
```

#### **OpenRouter AI API**

**Dokumentace:** https://openrouter.ai/docs

**Modely:**
- `mistralai/mistral-small-3.2-24b-instruct:free` (primary)
- `mistralai/mistral-nemo:free` (fallback)
- Custom GPT modely (placené)

**Použití:**
```go
type OpenRouterClient struct {
    apiKey  string
    baseURL string
}

func (orc *OpenRouterClient) Generate(prompt string, maxTokens int) (*GeneratedContent, error) {
    reqBody := map[string]interface{}{
        "model": "mistralai/mistral-small-3.2-24b-instruct:free",
        "messages": []map[string]string{
            {"role": "system", "content": "You are a professional sports journalist."},
            {"role": "user", "content": prompt},
        },
        "max_tokens": maxTokens,
        "temperature": 0.7,
    }
    
    jsonData, _ := json.Marshal(reqBody)
    req, _ := http.NewRequest("POST", orc.baseURL+"/chat/completions", bytes.NewBuffer(jsonData))
    req.Header.Set("Authorization", "Bearer "+orc.apiKey)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()
    
    var result OpenRouterResponse
    json.NewDecoder(resp.Body).Decode(&result)
    
    return &GeneratedContent{
        Content: result.Choices[0].Message.Content,
    }, nil
}
```

**Rate Limits:**
- Free tier: 200 requests/day
- Token limit: 100k tokens/day
- Fallback na jiný model při překročení

### 7.4 WebSocket Support (Budoucí)

**Plánované funkce:**
- Real-time skóre updates
- Live chat během zápasů
- Admin notifications

```go
// Připraveno pro rozšíření
func setupWebSocket(r *gin.Engine) {
    r.GET("/ws", func(c *gin.Context) {
        upgrader.Upgrade(c.Writer, c.Request, nil)
    })
}
```

---

## 8. Bezpečnost

### 8.1 Autentizace a Autorizace

#### **JWT (JSON Web Tokens)**

Aplikace používá JWT tokeny pro stateless autentizaci.

**Token Structure:**
```
Header.Payload.Signature
```

**Payload obsahuje:**
```json
{
  "user_id": 1,
  "email": "admin@club.cz",
  "role": "admin",
  "exp": 1735689600,  // Expiration timestamp
  "iat": 1735603200   // Issued at timestamp
}
```

**Implementace:**

```go
// Token generování při přihlášení
func generateJWT(user *models.User) (string, error) {
    claims := jwt.MapClaims{
        "user_id": user.ID,
        "email":   user.Email,
        "role":    user.Role,
        "exp":     time.Now().Add(24 * time.Hour).Unix(),
        "iat":     time.Now().Unix(),
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    secret := []byte(os.Getenv("JWT_SECRET"))
    
    return token.SignedString(secret)
}

// Middleware pro ověření tokenu
func JWTAuth(db *gorm.DB) gin.HandlerFunc {
    return func(c *gin.Context) {
        authHeader := c.GetHeader("Authorization")
        if authHeader == "" {
            c.JSON(401, gin.H{"error": "Authorization header missing"})
            c.Abort()
            return
        }
        
        // Extract token from "Bearer <token>"
        tokenString := strings.TrimPrefix(authHeader, "Bearer ")
        
        // Parse and validate token
        token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
            return []byte(os.Getenv("JWT_SECRET")), nil
        })
        
        if err != nil || !token.Valid {
            c.JSON(401, gin.H{"error": "Invalid token"})
            c.Abort()
            return
        }
        
        claims := token.Claims.(jwt.MapClaims)
        userID := uint(claims["user_id"].(float64))
        
        // Load user from database
        var user models.User
        if err := db.First(&user, userID).Error; err != nil {
            c.JSON(401, gin.H{"error": "User not found"})
            c.Abort()
            return
        }
        
        // Store user in context
        c.Set("user", user)
        c.Set("userID", user.ID)
        c.Set("userRole", user.Role)
        
        c.Next()
    }
}
```

#### **Role-Based Access Control (RBAC)**

Systém podporuje dvě role:
- **admin** - plný přístup ke všem funkcím
- **editor** - omezený přístup (bez správy uživatelů a nastavení)

```go
// Middleware pro kontrolu role
func RoleAuth(requiredRole string) gin.HandlerFunc {
    return func(c *gin.Context) {
        userRole, exists := c.Get("userRole")
        if !exists {
            c.JSON(403, gin.H{"error": "Forbidden"})
            c.Abort()
            return
        }
        
        if userRole.(string) != requiredRole {
            c.JSON(403, gin.H{"error": "Insufficient permissions"})
            c.Abort()
            return
        }
        
        c.Next()
    }
}

// Použití
admin := api.Group("/admin")
admin.Use(middleware.JWTAuth(db))
admin.Use(middleware.RoleAuth("admin"))
```

#### **Password Hashing**

Hesla jsou hashována pomocí bcrypt s cost faktorem 14.

```go
import "golang.org/x/crypto/bcrypt"

// Hashování hesla při registraci
func hashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
    return string(bytes), err
}

// Ověření hesla při přihlášení
func verifyPassword(hashedPassword, password string) error {
    return bcrypt.CompareHashAndPassword(
        []byte(hashedPassword),
        []byte(password),
    )
}
```

**Výhody bcrypt:**
- Adaptive algorithm (cost lze zvýšit v budoucnu)
- Built-in salt
- Resistance proti rainbow table útokům
- Slow by design (brání brute-force)

### 8.2 Security Headers

Aplikace automaticky přidává bezpečnostní hlavičky ke každému HTTP response.

```go
// Security headers middleware
r.Use(func(c *gin.Context) {
    // Prevent MIME type sniffing
    c.Writer.Header().Set("X-Content-Type-Options", "nosniff")
    
    // Prevent clickjacking
    c.Writer.Header().Set("X-Frame-Options", "DENY")
    
    // Referrer policy
    c.Writer.Header().Set("Referrer-Policy", "no-referrer-when-downgrade")
    
    // HSTS (when using HTTPS)
    if c.Request.TLS != nil || c.Request.Header.Get("X-Forwarded-Proto") == "https" {
        c.Writer.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload")
    }
    
    // Content Security Policy
    csp := "default-src 'self'; " +
           "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://umami.tdvorak.dev; " +
           "style-src 'self' 'unsafe-inline'; " +
           "img-src 'self' data: https: blob:; " +
           "font-src 'self' data:; " +
           "connect-src 'self' https://umami.tdvorak.dev;"
    c.Writer.Header().Set("Content-Security-Policy", csp)
    
    c.Next()
})
```

**Vysvětlení jednotlivých headers:**

| Header | Účel | Hodnota |
|--------|------|---------|
| `X-Content-Type-Options` | Zabraňuje MIME sniffing | `nosniff` |
| `X-Frame-Options` | Ochrana proti clickjacking | `DENY` |
| `Referrer-Policy` | Kontrola referrer informací | `no-referrer-when-downgrade` |
| `Strict-Transport-Security` | Vynucení HTTPS | `max-age=31536000` |
| `Content-Security-Policy` | Whitelist zdrojů (XSS ochrana) | Custom policy |

### 8.3 CORS (Cross-Origin Resource Sharing)

CORS je nakonfigurován pro povolené origins z `.env` souboru.

```go
// CORS middleware
origin := c.Request.Header.Get("Origin")
allowed := false

for _, ao := range config.AppConfig.AllowedOrigins {
    if ao == origin {
        allowed = true
        break
    }
}

// Relaxed rule for development
if !allowed && origin != "" && config.AppConfig.AppEnv != "production" {
    if strings.HasPrefix(origin, "http://localhost:") || 
       strings.HasPrefix(origin, "http://127.0.0.1:") {
        allowed = true
    }
}

if allowed && origin != "" {
    c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
    c.Writer.Header().Set("Vary", "Origin")
    c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
}

c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, Cache-Control, X-Requested-With")

// Handle preflight requests
if c.Request.Method == "OPTIONS" {
    c.AbortWithStatus(204)
    return
}
```

### 8.4 Rate Limiting

Aplikace implementuje rate limiting pro ochranu proti abuse a DDoS útokům.

```go
// Rate limiting middleware
func RateLimit(maxRequests int, window time.Duration) gin.HandlerFunc {
    type client struct {
        count     int
        resetTime time.Time
    }
    
    clients := make(map[string]*client)
    mu := sync.Mutex{}
    
    return func(c *gin.Context) {
        ip := c.ClientIP()
        
        mu.Lock()
        defer mu.Unlock()
        
        now := time.Now()
        
        // Clean up expired entries
        if cl, exists := clients[ip]; exists && now.After(cl.resetTime) {
            delete(clients, ip)
        }
        
        // Initialize or increment counter
        if _, exists := clients[ip]; !exists {
            clients[ip] = &client{
                count:     1,
                resetTime: now.Add(window),
            }
        } else {
            clients[ip].count++
        }
        
        // Check limit
        if clients[ip].count > maxRequests {
            c.JSON(429, gin.H{
                "error": "Too many requests",
                "retry_after": clients[ip].resetTime.Sub(now).Seconds(),
            })
            c.Abort()
            return
        }
        
        c.Next()
    }
}

// Použití na citlivých endpointech
auth.POST("/login", middleware.RateLimit(15, time.Minute), authController.Login)
auth.POST("/register", middleware.RateLimit(5, time.Hour), authController.Register)
api.POST("/contact", middleware.RateLimit(10, time.Minute), contactController.SubmitContactForm)
```

**Limity:**
- Login: 15 pokusů / minutu
- Registrace: 5 pokusů / hodinu
- Kontaktní formulář: 10 odeslání / minutu
- Newsletter subscribe: 30 pokusů / minutu

### 8.5 Input Validation a Sanitizace

#### **Backend Validace**

```go
type CreateArticleRequest struct {
    Title         string `json:"title" binding:"required,min=3,max=255"`
    Slug          string `json:"slug" binding:"required,min=3,max=255,alphanum_hyphen"`
    Content       string `json:"content" binding:"required,min=10"`
    CategoryID    uint   `json:"category_id" binding:"required,gt=0"`
    Published     bool   `json:"published"`
    FeaturedImage string `json:"featured_image" binding:"omitempty,url"`
}

func (bc *BaseController) CreateArticle(c *gin.Context) {
    var req CreateArticleRequest
    
    // Validace
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // Sanitizace HTML obsahu
    sanitizedContent := sanitizeHTML(req.Content)
    
    // Create article...
}
```

#### **SQL Injection Prevence**

GORM automaticky escapuje parametry v SQL dotazech.

```go
// ✅ Bezpečné - parameterized query
db.Where("email = ?", userEmail).First(&user)

// ❌ NEBEZPEČNÉ - string concatenation
db.Where("email = '" + userEmail + "'").First(&user) // NIKDY NEPOUŽÍVAT!
```

#### **XSS Prevence**

Frontend používá React, který automaticky escapuje obsah. Pro HTML obsah z editoru:

```typescript
// Sanitizace HTML na frontendu
import DOMPurify from 'dompurify';

function ArticleContent({ html }) {
  const sanitizedHTML = DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'h1', 'h2', 'h3', 'ul', 'ol', 'li', 'a', 'img'],
    ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class'],
  });
  
  return <div dangerouslySetInnerHTML={{ __html: sanitizedHTML }} />;
}
```

### 8.6 File Upload Security

```go
func (bc *BaseController) UploadImage(c *gin.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.JSON(400, gin.H{"error": "No file uploaded"})
        return
    }
    
    // 1. Kontrola velikosti
    maxSize := int64(10 * 1024 * 1024) // 10MB
    if file.Size > maxSize {
        c.JSON(400, gin.H{"error": "File too large"})
        return
    }
    
    // 2. Kontrola MIME typu
    allowedTypes := map[string]bool{
        "image/jpeg": true,
        "image/png":  true,
        "image/gif":  true,
        "image/webp": true,
    }
    
    contentType := file.Header.Get("Content-Type")
    if !allowedTypes[contentType] {
        c.JSON(400, gin.H{"error": "Invalid file type"})
        return
    }
    
    // 3. Kontrola magic bytes (skutečný typ souboru)
    fileContent, _ := file.Open()
    buffer := make([]byte, 512)
    fileContent.Read(buffer)
    detectedType := http.DetectContentType(buffer)
    
    if !allowedTypes[detectedType] {
        c.JSON(400, gin.H{"error": "File content mismatch"})
        return
    }
    
    // 4. Generování bezpečného jména souboru
    ext := filepath.Ext(file.Filename)
    safeFilename := fmt.Sprintf("%d_%s%s", 
        time.Now().Unix(),
        generateRandomString(16),
        ext,
    )
    
    // 5. Uložení do izolovaného adresáře
    uploadPath := filepath.Join("./uploads", safeFilename)
    c.SaveUploadedFile(file, uploadPath)
    
    c.JSON(200, gin.H{"url": "/uploads/" + safeFilename})
}
```

### 8.7 GDPR Compliance

#### **Souhlas s Cookies**

```typescript
// Cookie consent banner
const CookieBanner = () => {
  const [consent, setConsent] = useState<CookieConsent | null>(null);
  
  const handleAccept = (categories: string[]) => {
    const consent = {
      necessary: true,
      analytics: categories.includes('analytics'),
      marketing: categories.includes('marketing'),
      timestamp: new Date().toISOString(),
    };
    
    localStorage.setItem('cookie_consent', JSON.stringify(consent));
    setConsent(consent);
    
    // Spustit analytics pouze pokud uživatel souhlasil
    if (consent.analytics) {
      initializeUmami();
    }
  };
  
  return consent ? null : <CookieConsentModal onAccept={handleAccept} />;
};
```

#### **Anonymizace IP Adres**

```go
// Hash IP adresy pro analytics (GDPR compliant)
func hashIP(ip string) string {
    h := sha256.New()
    h.Write([]byte(ip + os.Getenv("IP_SALT")))
    return fmt.Sprintf("%x", h.Sum(nil))[:16]
}

// Uložení trackingu s anonymizovanou IP
event := models.VisitorEvent{
    IPHash: hashIP(c.ClientIP()),
    // ... other fields
}
```

#### **Právo na Výmaz Dat**

```go
// Endpoint pro smazání uživatelských dat
func (uc *UserController) DeleteUserData(c *gin.Context) {
    userID := c.Param("id")
    
    // Smazat veškerá osobní data
    db.Where("user_id = ?", userID).Delete(&models.Article{})
    db.Where("email = ?", user.Email).Delete(&models.NewsletterSubscription{})
    db.Where("user_id = ?", userID).Delete(&models.ContactMessage{})
    db.Delete(&models.User{}, userID)
    
    c.JSON(200, gin.H{"message": "User data deleted"})
}
```

### 8.8 Environment Variables Security

**Nikdy necommitovat:**
- `.env` soubor s produkčními credentials
- API klíče
- JWT secret
- SMTP hesla

**Best practices:**
```bash
# .gitignore
.env
.env.local
.env.production

# Commitovat pouze example
.env.example
```

**Production deployment:**
- Použít environment variables na serveru
- Nebo encrypted secrets (GitHub Secrets, Vault)
- Rotate API keys pravidelně

### 8.9 Production Security Checklist

- ✅ **JWT_SECRET** změněn z default hodnoty
- ✅ **HTTPS** enabled (přes reverse proxy)
- ✅ **HSTS** header aktivován
- ✅ **CORS** omezen na konkrétní domény
- ✅ **Rate limiting** na všech public endpointech
- ✅ **SQL injection** prevence (GORM parametry)
- ✅ **XSS** prevence (React + DOMPurify)
- ✅ **File upload** validace
- ✅ **Password hashing** (bcrypt cost 14)
- ✅ **GDPR** compliance (cookie consent, IP anonymizace)
- ✅ **Security headers** (CSP, X-Frame-Options, etc.)
- ✅ **Input validation** na frontendu i backendu
- ✅ **Error logging** bez citlivých dat

---

## 9. Minimální Požadavky

### 9.1 Hardwarové Požadavky

#### **Development (Lokální vývoj):**
- **CPU:** 2+ cores (Intel i3 nebo ekvivalent)
- **RAM:** 4 GB minimum, 8 GB doporučeno
- **Disk:** 5 GB volného místa (SSD doporučeno)
- **OS:** Windows 10/11, macOS 10.15+, Linux (Ubuntu 20.04+)

#### **Production (Server):**
- **CPU:** 2+ cores
- **RAM:** 2 GB minimum, 4 GB doporučeno pro 1000+ concurrent users
- **Disk:** 20 GB+ (závisí na množství uploadů a databáze)
- **Bandwidth:** 100 Mbps+ pro optimální výkon

### 9.2 Softwarové Požadavky

#### **Backend:**
| Software | Minimální Verze | Doporučená Verze |
|----------|-----------------|------------------|
| Go | 1.23.0 | 1.24.4+ |
| PostgreSQL | 12.0 | 15.0+ |
| Docker | 20.10 | 24.0+ |
| Docker Compose | 1.29 | 2.20+ |

#### **Frontend:**
| Software | Minimální Verze | Doporučená Verze |
|----------|-----------------|------------------|
| Node.js | 16.0 | 20.0+ LTS |
| npm | 8.0 | 10.0+ |

#### **Další nástroje:**
- Git 2.30+
- Text editor (VS Code, GoLand)
- Make (pro Makefile příkazy)

### 9.3 Prohlížeče (Frontend Kompatibilita)

| Prohlížeč | Minimální Verze |
|-----------|-----------------|
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14+ |
| Edge | 90+ |
| Mobile Safari (iOS) | 14+ |
| Chrome Mobile (Android) | 90+ |

---

## 10. Instalace a Konfigurace

### 10.1 Rychlá Instalace (Docker - Doporučeno)

**Krok 1: Klonování repozitáře**
```bash
git clone <repository-url>
cd fotbal-club
```

**Krok 2: Konfigurace prostředí**
```bash
# Zkopírovat example soubor
cp .env.example .env

# Upravit .env soubor
# Minimálně změnit:
# - JWT_SECRET
# - SMTP_* konfigurace
# - ALLOWED_ORIGINS
```

**Krok 3: Spuštění aplikace**
```bash
docker-compose up -d
```

**Krok 4: Přístup do aplikace**
- Frontend: http://localhost:3000
- Backend API: http://localhost:8080
- Databáze: localhost:5432

**Krok 5: Průvodce nastavením**
1. Otevřete http://localhost:3000
2. Budete přesměrováni na setup wizard
3. Vytvořte admin účet
4. Nastavte klubové informace
5. Vyberte klubové barvy
6. Připojte FAČR klub

### 10.2 Manuální Instalace (Bez Dockeru)

#### **Backend Setup:**

```bash
# 1. Instalace Go dependencies
go mod download

# 2. Nastavení PostgreSQL
createdb fotbal_club

# 3. Konfigurace .env
cp .env.example .env
# Upravit DATABASE_URL, JWT_SECRET, etc.

# 4. Migrace databáze
make migrate

# 5. (Volitelně) Seed testovacích dat
make seed

# 6. Spuštění serveru
make run
# Nebo přímo:
go run main.go
```

**Backend běží na:** http://localhost:8080

#### **Frontend Setup:**

```bash
# 1. Přejít do frontend složky
cd frontend

# 2. Instalace dependencies
npm install

# 3. Konfigurace .env
cp .env.example .env
# Nastavit REACT_APP_API_URL=http://localhost:8080/api/v1

# 4. Spuštění dev serveru
npm start
```

**Frontend běží na:** http://localhost:3000

### 10.3 Konfigurační Soubory

#### **Backend .env (Kompletní)**

```bash
# ==========================================
# APPLICATION
# ==========================================
APP_NAME=FotbalClub
APP_ENV=development  # development, staging, production
PORT=8080
DEBUG=true

# ==========================================
# DATABASE
# ==========================================
DB_HOST=db
DB_PORT=5432
DB_USER=postgres
DB_PASSWORD=postgres
DB_NAME=fotbal_club
DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable

# Database Migrations & Seeding
RUN_MIGRATIONS=true
SEED_DATABASE=false

# ==========================================
# JWT AUTHENTICATION
# ==========================================
JWT_SECRET=your_very_secret_key_change_in_production
JWT_EXPIRATION_HOURS=24

# ==========================================
# EMAIL (SMTP)
# ==========================================
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your_email@example.com
SMTP_PASSWORD=your_password
SMTP_FROM=noreply@fotbalclub.com
SMTP_FROM_NAME="Fotbal Club"
SMTP_ENCRYPTION=tls  # tls, ssl, or none
SMTP_AUTH=true
SMTP_SKIP_VERIFY=false

# Email Templates
EMAIL_TEMPLATE_DIR=./templates/emails

# Contact Form
CONTACT_EMAIL=help@example.com
ADMIN_EMAIL=admin@example.com

# ==========================================
# NEWSLETTER
# ==========================================
NEWSLETTER_ENABLED=true

# ==========================================
# FILE UPLOADS
# ==========================================
UPLOAD_DIR=./uploads
MAX_UPLOAD_SIZE=10485760  # 10MB in bytes
ALLOWED_FILE_TYPES=image/jpeg,image/png,image/gif,application/pdf
MAX_FILES=5

# ==========================================
# CORS
# ==========================================
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080

# ==========================================
# EXTERNAL APIs
# ==========================================
# OpenRouter (AI blog generation)
OPENROUTER_API_KEY=sk-or-v1-your-key-here
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL=mistralai/mistral-small-3.2-24b-instruct:free
OPENROUTER_FALLBACK_MODEL=mistralai/mistral-nemo:free

# Umami Analytics
UMAMI_URL=https://umami.example.com
UMAMI_USERNAME=admin
UMAMI_PASSWORD=your_password
UMAMI_WEBSITE_ID=  # Auto-create on first run

# ==========================================
# LOGGING
# ==========================================
LOG_LEVEL=info  # debug, info, warn, error
LOG_FORMAT=text  # text or json
LOG_OUTPUT=stdout  # stdout, stderr, or file path

# ==========================================
# SECURITY
# ==========================================
CONTENT_SECURITY_POLICY=default-src 'self'
IP_SALT=random_salt_for_hashing_ips
```

#### **Frontend .env**

```bash
# API Configuration
REACT_APP_API_URL=http://localhost:8080/api/v1
REACT_APP_API_BASE_URL=http://localhost:8080

# FACR API
REACT_APP_FACR_API_BASE_URL=http://localhost:8080/api/v1/facr
REACT_APP_FACR_API_TIMEOUT=5000
REACT_APP_FACR_CACHE_TTL=3600000  # 1 hour in ms

# Homepage Layout
REACT_APP_HOMEPAGE_LAYOUT=classic  # classic or sparta

# Application Name
REACT_APP_NAME=Fotbal Club Manager
```

### 10.4 Databázové Migrace

Aplikace používá automatické migrace přes GORM.

**Spuštění migrací:**
```bash
# V .env souboru
RUN_MIGRATIONS=true

# Pak restart aplikace
docker-compose restart backend
# Nebo
make migrate
```

**Manuální migrace (SQL):**
```sql
-- Vytvořit novou migraci v database/migrations/
-- Pojmenování: 001_create_users_table.sql

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    role VARCHAR(50) DEFAULT 'editor',
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);
```

### 10.5 Seed Data (Testovací Data)

```bash
# V .env
SEED_DATABASE=true

# Restart
docker-compose restart backend
```

**Co se seeduje:**
- Admin uživatel (admin@club.cz / admin123)
- Základní kategorie článků
- Sample články
- Sample týmy a hráči
- Sponzoři

### 10.6 Production Deployment

#### **Doporučená Architektura:**

```
Internet
   │
   ▼
[Nginx Reverse Proxy]  ← HTTPS/SSL
   │
   ├──► [Frontend Container (React SPA)]
   │
   ├──► [Backend Container (Go API)]
   │
   └──► [PostgreSQL Container/Managed DB]
```

#### **Docker Production Build:**

```bash
# 1. Build production image
docker build -t fotbal-club:latest .

# 2. Run with production env
docker run -d \
  --name fotbal-club \
  -p 8080:8080 \
  --env-file .env.production \
  -v /path/to/uploads:/app/uploads \
  fotbal-club:latest
```

#### **Nginx Konfigurace:**

```nginx
server {
    listen 80;
    server_name fotbalclub.cz;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name fotbalclub.cz;

    ssl_certificate /etc/letsencrypt/live/fotbalclub.cz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/fotbalclub.cz/privkey.pem;

    # Frontend (SPA)
    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Backend API
    location /api/ {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # Uploads
    location /uploads/ {
        alias /var/www/fotbalclub/uploads/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    # Cache
    location /cache/ {
        alias /var/www/fotbalclub/cache/;
        expires 30m;
    }
}
```

#### **Environment Variables na Produkci:**

```bash
# Použít environment variables místo .env souboru
export APP_ENV=production
export JWT_SECRET=$(openssl rand -base64 64)
export DATABASE_URL=postgres://user:pass@db.example.com:5432/fotbal_club?sslmode=require
export ALLOWED_ORIGINS=https://fotbalclub.cz

# Nebo použít Docker secrets / Kubernetes secrets
```

#### **Backup Strategy:**

```bash
# Automatický denní backup databáze
0 2 * * * pg_dump fotbal_club > /backups/fotbal_club_$(date +\%Y\%m\%d).sql

# Backup uploadů
0 3 * * * rsync -av /var/www/fotbalclub/uploads/ /backups/uploads/
```

### 10.7 Troubleshooting

#### **Časté Problémy:**

**1. Backend se nespustí - "Failed to connect to database"**
```bash
# Zkontrolovat DB běží
docker ps | grep postgres

# Zkontrolovat DATABASE_URL v .env
echo $DATABASE_URL

# Restartovat DB
docker-compose restart db
```

**2. Frontend nemůže volat API - CORS error**
```bash
# Zkontrolovat ALLOWED_ORIGINS v backend .env
# Zkontrolovat REACT_APP_API_URL ve frontend .env

# Development:
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
```

**3. Migrace selhaly**
```bash
# Vymazat databázi a začít znovu
docker-compose down -v
docker-compose up -d

# Nebo manuální reset
psql fotbal_club -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
```

**4. Upload souborů nefunguje**
```bash
# Zkontrolovat oprávnění
chmod -R 755 ./uploads

# Zkontrolovat UPLOAD_DIR v .env
# Zkontrolovat MAX_UPLOAD_SIZE
```

**5. JWT token expired příliš rychle**
```bash
# Upravit JWT_EXPIRATION_HOURS v .env
JWT_EXPIRATION_HOURS=168  # 7 dní
```

---

## 11. Příklady Kódu

### 11.1 Backend - Vytvoření Nového Endpointu

**Příklad: Endpoint pro získání statistik článků**

```go
// internal/controllers/stats_controller.go
package controllers

import (
    "fotbal-club/internal/models"
    "github.com/gin-gonic/gin"
    "gorm.io/gorm"
)

type StatsController struct {
    DB *gorm.DB
}

// GetArticleStats vrací statistiky článků
func (sc *StatsController) GetArticleStats(c *gin.Context) {
    var stats struct {
        TotalArticles     int64   `json:"total_articles"`
        PublishedArticles int64   `json:"published_articles"`
        TotalViews        int     `json:"total_views"`
        AverageViews      float64 `json:"average_views"`
        MostViewedArticle *models.Article `json:"most_viewed_article"`
    }
    
    // Celkový počet článků
    sc.DB.Model(&models.Article{}).Count(&stats.TotalArticles)
    
    // Počet publikovaných
    sc.DB.Model(&models.Article{}).Where("published = ?", true).Count(&stats.PublishedArticles)
    
    // Celkový počet zhlédnutí
    var articles []models.Article
    sc.DB.Find(&articles)
    for _, article := range articles {
        stats.TotalViews += article.ViewCount
    }
    
    // Průměrný počet zhlédnutí
    if stats.TotalArticles > 0 {
        stats.AverageViews = float64(stats.TotalViews) / float64(stats.TotalArticles)
    }
    
    // Nejvíce zobrazovaný článek
    sc.DB.Order("view_count DESC").First(&stats.MostViewedArticle)
    
    c.JSON(200, gin.H{
        "success": true,
        "data": stats,
    })
}
```

**Registrace route:**

```go
// internal/routes/routes.go
func SetupRoutes(api *gin.RouterGroup, db *gorm.DB) {
    statsController := &controllers.StatsController{DB: db}
    
    // Public stats
    api.GET("/stats/articles", statsController.GetArticleStats)
}
```

### 11.2 Frontend - Vytvoření Nové Komponenty

**Příklad: Dashboard widget pro statistiky**

```typescript
// frontend/src/components/dashboard/ArticleStatsWidget.tsx
import React from 'react';
import { Box, Stat, StatLabel, StatNumber, StatHelpText, SimpleGrid } from '@chakra-ui/react';
import { useQuery } from '@tanstack/react-query';
import api from '../../services/api';

interface ArticleStats {
  total_articles: number;
  published_articles: number;
  total_views: number;
  average_views: number;
  most_viewed_article: {
    id: number;
    title: string;
    view_count: number;
  };
}

const ArticleStatsWidget: React.FC = () => {
  const { data, isLoading, error } = useQuery<ArticleStats>({
    queryKey: ['article-stats'],
    queryFn: async () => {
      const response = await api.get('/stats/articles');
      return response.data.data;
    },
    refetchInterval: 30000, // Refresh každých 30 sekund
  });

  if (isLoading) {
    return <Box>Načítání...</Box>;
  }

  if (error) {
    return <Box>Chyba při načítání statistik</Box>;
  }

  return (
    <Box bg="white" p={6} rounded="lg" shadow="md">
      <SimpleGrid columns={{ base: 1, md: 2, lg: 4 }} spacing={6}>
        <Stat>
          <StatLabel>Celkem článků</StatLabel>
          <StatNumber>{data?.total_articles}</StatNumber>
          <StatHelpText>Všechny články v systému</StatHelpText>
        </Stat>

        <Stat>
          <StatLabel>Publikované</StatLabel>
          <StatNumber>{data?.published_articles}</StatNumber>
          <StatHelpText>
            {((data?.published_articles / data?.total_articles) * 100).toFixed(1)}% z celku
          </StatHelpText>
        </Stat>

        <Stat>
          <StatLabel>Celková zobrazení</StatLabel>
          <StatNumber>{data?.total_views.toLocaleString()}</StatNumber>
          <StatHelpText>Všechny články dohromady</StatHelpText>
        </Stat>

        <Stat>
          <StatLabel>Průměrná zobrazení</StatLabel>
          <StatNumber>{data?.average_views.toFixed(1)}</StatNumber>
          <StatHelpText>Per článek</StatHelpText>
        </Stat>
      </SimpleGrid>

      {data?.most_viewed_article && (
        <Box mt={4} p={3} bg="gray.50" rounded="md">
          <strong>Nejčtenější článek:</strong> {data.most_viewed_article.title}
          <br />
          <small>{data.most_viewed_article.view_count} zobrazení</small>
        </Box>
      )}
    </Box>
  );
};

export default ArticleStatsWidget;
```

### 11.3 Použití Custom Hooks

**Příklad: useArticles hook**

```typescript
// frontend/src/hooks/useArticles.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import api from '../services/api';
import { Article, CreateArticleRequest } from '../types';

export const useArticles = (filters?: {
  category?: number;
  published?: boolean;
  page?: number;
  limit?: number;
}) => {
  return useQuery<Article[]>({
    queryKey: ['articles', filters],
    queryFn: async () => {
      const params = new URLSearchParams();
      if (filters?.category) params.append('category', filters.category.toString());
      if (filters?.published !== undefined) params.append('published', filters.published.toString());
      if (filters?.page) params.append('page', filters.page.toString());
      if (filters?.limit) params.append('limit', filters.limit.toString());

      const response = await api.get(`/articles?${params.toString()}`);
      return response.data.data;
    },
  });
};

export const useCreateArticle = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (article: CreateArticleRequest) => {
      const response = await api.post('/articles', article);
      return response.data;
    },
    onSuccess: () => {
      // Invalidate and refetch articles
      queryClient.invalidateQueries({ queryKey: ['articles'] });
    },
  });
};

export const useDeleteArticle = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: async (articleId: number) => {
      await api.delete(`/articles/${articleId}`);
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['articles'] });
    },
  });
};
```

**Použití v komponentě:**

```typescript
// frontend/src/pages/admin/ArticlesAdminPage.tsx
import React from 'react';
import { useArticles, useDeleteArticle } from '../../hooks/useArticles';

const ArticlesAdminPage: React.FC = () => {
  const { data: articles, isLoading } = useArticles({ published: true });
  const deleteMutation = useDeleteArticle();

  const handleDelete = async (id: number) => {
    if (window.confirm('Opravdu smazat článek?')) {
      await deleteMutation.mutateAsync(id);
    }
  };

  if (isLoading) return <div>Načítání...</div>;

  return (
    <div>
      {articles?.map((article) => (
        <div key={article.id}>
          <h3>{article.title}</h3>
          <button onClick={() => handleDelete(article.id)}>Smazat</button>
        </div>
      ))}
    </div>
  );
};
```

### 11.4 Background Job - Newsletter Automation

**Příklad: Automatické odesílání newsletteru**

```go
// internal/services/newsletter_automation.go
package services

import (
    "fotbal-club/internal/models"
    "fotbal-club/pkg/email"
    "time"
    "gorm.io/gorm"
    "log"
)

type NewsletterAutomation struct {
    db           *gorm.DB
    emailService *email.EmailService
    ticker       *time.Ticker
    stopChan     chan bool
}

func NewNewsletterAutomation(db *gorm.DB, emailSvc *email.EmailService) *NewsletterAutomation {
    return &NewsletterAutomation{
        db:           db,
        emailService: emailSvc,
        stopChan:     make(chan bool),
    }
}

func (na *NewsletterAutomation) Start() {
    // Check every hour
    na.ticker = time.NewTicker(1 * time.Hour)
    
    go func() {
        for {
            select {
            case <-na.ticker.C:
                na.checkAndSendScheduled()
            case <-na.stopChan:
                na.ticker.Stop()
                return
            }
        }
    }()
    
    log.Println("Newsletter automation started")
}

func (na *NewsletterAutomation) Stop() {
    na.stopChan <- true
    log.Println("Newsletter automation stopped")
}

func (na *NewsletterAutomation) checkAndSendScheduled() {
    now := time.Now()
    
    // Weekly digest - Friday at 18:00
    if now.Weekday() == time.Friday && now.Hour() == 18 {
        na.sendWeeklyDigest()
    }
    
    // Match alerts - 24 hours before match
    na.sendMatchAlerts()
    
    // Results digest - Monday at 10:00
    if now.Weekday() == time.Monday && now.Hour() == 10 {
        na.sendResultsDigest()
    }
}

func (na *NewsletterAutomation) sendWeeklyDigest() {
    // Get articles from last 7 days
    var articles []models.Article
    sevenDaysAgo := time.Now().AddDate(0, 0, -7)
    na.db.Where("published = ? AND created_at > ?", true, sevenDaysAgo).
        Order("created_at DESC").
        Limit(5).
        Find(&articles)
    
    if len(articles) == 0 {
        return // No new content
    }
    
    // Get subscribers who want weekly digest
    var subscribers []models.NewsletterSubscription
    na.db.Where("status = ? AND preferences->>'weekly_digest' = 'true'", "active").
        Find(&subscribers)
    
    for _, sub := range subscribers {
        templateData := map[string]interface{}{
            "Email":    sub.Email,
            "Articles": articles,
            "Type":     "weekly_digest",
        }
        
        err := na.emailService.SendNewsletter(sub.Email, "Týdenní přehled", templateData)
        if err != nil {
            log.Printf("Failed to send newsletter to %s: %v", sub.Email, err)
        } else {
            log.Printf("Weekly digest sent to %s", sub.Email)
        }
        
        // Rate limiting - wait 100ms between emails
        time.Sleep(100 * time.Millisecond)
    }
}

func (na *NewsletterAutomation) sendMatchAlerts() {
    // Get matches in next 24 hours
    tomorrow := time.Now().Add(24 * time.Hour)
    dayAfterTomorrow := time.Now().Add(48 * time.Hour)
    
    // Implementation would fetch from FACR or database
    // For brevity, simplified here
    log.Println("Checking for match alerts...")
}

func (na *NewsletterAutomation) sendResultsDigest() {
    log.Println("Sending results digest...")
}
```

### 11.5 Use Case: Kompletní Flow Vytvoření Článku

**1. Frontend - Formulář pro vytvoření článku:**

```typescript
// frontend/src/components/admin/ArticleForm.tsx
import React, { useState } from 'react';
import { useCreateArticle } from '../../hooks/useArticles';
import { useNavigate } from 'react-router-dom';

const ArticleForm: React.FC = () => {
  const [formData, setFormData] = useState({
    title: '',
    content: '',
    excerpt: '',
    category_id: 1,
    published: false,
    featured_image: '',
  });
  
  const createMutation = useCreateArticle();
  const navigate = useNavigate();

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    
    try {
      await createMutation.mutateAsync(formData);
      alert('Článek vytvořen!');
      navigate('/admin/articles');
    } catch (error) {
      alert('Chyba při vytváření článku');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Název článku"
        value={formData.title}
        onChange={(e) => setFormData({ ...formData, title: e.target.value })}
        required
      />
      
      <textarea
        placeholder="Obsah článku"
        value={formData.content}
        onChange={(e) => setFormData({ ...formData, content: e.target.value })}
        required
      />
      
      <button type="submit" disabled={createMutation.isPending}>
        {createMutation.isPending ? 'Ukládání...' : 'Vytvořit článek'}
      </button>
    </form>
  );
};
```

**2. Backend - API handler:**

```go
// internal/controllers/base_controller.go
func (bc *BaseController) CreateArticle(c *gin.Context) {
    var req struct {
        Title         string `json:"title" binding:"required,min=3,max=255"`
        Content       string `json:"content" binding:"required,min=10"`
        Excerpt       string `json:"excerpt"`
        CategoryID    uint   `json:"category_id" binding:"required"`
        Published     bool   `json:"published"`
        FeaturedImage string `json:"featured_image"`
    }
    
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }
    
    // Get user from context
    userID := c.GetUint("userID")
    
    // Generate slug
    slug := generateSlug(req.Title)
    
    // Create article
    article := models.Article{
        Title:         req.Title,
        Slug:          slug,
        Content:       req.Content,
        Excerpt:       req.Excerpt,
        CategoryID:    req.CategoryID,
        Published:     req.Published,
        FeaturedImage: req.FeaturedImage,
        AuthorID:      userID,
    }
    
    if req.Published {
        now := time.Now()
        article.PublishedAt = &now
    }
    
    if err := bc.DB.Create(&article).Error; err != nil {
        c.JSON(500, gin.H{"error": "Failed to create article"})
        return
    }
    
    // Preload relationships
    bc.DB.Preload("Category").Preload("Author").First(&article, article.ID)
    
    c.JSON(201, gin.H{
        "success": true,
        "data":    article,
        "message": "Article created successfully",
    })
}
```

### 11.6 Performance Optimization - Caching

**Backend caching příklad:**

```go
// pkg/cache/cache.go
package cache

import (
    "encoding/json"
    "os"
    "path/filepath"
    "sync"
    "time"
)

type Cache struct {
    mu   sync.RWMutex
    data map[string]CacheEntry
}

type CacheEntry struct {
    Data      interface{}
    ExpiresAt time.Time
}

var globalCache = &Cache{
    data: make(map[string]CacheEntry),
}

func Set(key string, value interface{}, ttl time.Duration) {
    globalCache.mu.Lock()
    defer globalCache.mu.Unlock()
    
    globalCache.data[key] = CacheEntry{
        Data:      value,
        ExpiresAt: time.Now().Add(ttl),
    }
}

func Get(key string) (interface{}, bool) {
    globalCache.mu.RLock()
    defer globalCache.mu.RUnlock()
    
    entry, exists := globalCache.data[key]
    if !exists {
        return nil, false
    }
    
    if time.Now().After(entry.ExpiresAt) {
        delete(globalCache.data, key)
        return nil, false
    }
    
    return entry.Data, true
}

// Save to file for persistence
func SaveToFile(filename string) error {
    globalCache.mu.RLock()
    defer globalCache.mu.RUnlock()
    
    data, err := json.Marshal(globalCache.data)
    if err != nil {
        return err
    }
    
    return os.WriteFile(filepath.Join("./cache", filename), data, 0644)
}
```

**Použití:**

```go
func (bc *BaseController) GetArticles(c *gin.Context) {
    cacheKey := "articles_published"
    
    // Try cache first
    if cached, found := cache.Get(cacheKey); found {
        c.JSON(200, cached)
        return
    }
    
    // Fetch from database
    var articles []models.Article
    bc.DB.Where("published = ?", true).Find(&articles)
    
    response := gin.H{"success": true, "data": articles}
    
    // Cache for 5 minutes
    cache.Set(cacheKey, response, 5*time.Minute)
    
    c.JSON(200, response)
}
```

---

## 12. Závěr

### 12.1 Shrnutí Projektu

**Fotbal Club** je komplexní full-stack webová aplikace navržená pro efektivní správu fotbalového klubu. Projekt úspěšně kombinuje moderní technologie a best practices pro vytvoření robustního, škálovatelného a bezpečného systému.

#### **Klíčové Úspěchy:**

✅ **Moderní Tech Stack**
- Go backend s vysokým výkonem
- React frontend s TypeScript
- PostgreSQL databáze
- Docker pro snadné deployment

✅ **Komplexní Funkcionalita**
- Správa obsahu (články, galerie, videa)
- Automatická integrace s FAČR API
- Newsletter systém s automatizací
- Analytics a statistiky
- AI generování obsahu

✅ **Bezpečnost**
- JWT autentizace
- RBAC autorizace
- Rate limiting
- GDPR compliance
- Security headers

✅ **Developer Experience**
- Setup wizard pro snadnou instalaci
- Comprehensive API dokumentace
- Docker Compose pro one-command start
- Environment-based konfigurace

### 12.2 Technické Přednosti

| Oblast | Implementace |
|--------|--------------|
| **Performance** | Caching, lazy loading, optimalizované DB queries |
| **Scalability** | Stateless API, horizontální škálování možné |
| **Maintainability** | Čistá architektura, SOLID principy |
| **Testing** | Unit testy, integration testy připraveny |
| **Documentation** | Kompletní technická dokumentace |

### 12.3 Možnosti Rozšíření

Aplikace je navržena s ohledem na budoucí rozšíření:

**Krátk

odobé (1-3 měsíce):**
- [ ] WebSocket podpora pro real-time updates
- [ ] Push notifikace (PWA)
- [ ] Mobilní aplikace (React Native)
- [ ] Advanced SEO features (schema.org markup)

**Střednědobé (3-6 měsíců):**
- [ ] E-shop modul pro klubové produkty
- [ ] Ticketing systém pro zápasy
- [ ] Member portál s přihlášením hráčů
- [ ] Platební brána integrace

**Dlouhodobé (6-12 měsíců):**
- [ ] Multi-tenant architektura (více klubů)
- [ ] API pro třetí strany
- [ ] Machine learning predikce výsledků
- [ ] Video streaming integrace

### 12.4 Lessons Learned

**Co fungovalo dobře:**
- Docker drasticky zjednodušil development workflow
- React Query zjednodušil state management
- GORM AutoMigrate urychlil iterace
- JWT stateless autentizace škáluje výborně

**Co by se dalo zlepšit:**
- Implementovat GraphQL pro flexibilnější API
- Přidat více unit testů (současné pokrytí ~60%)
- Použít message queue pro newsletter (Redis/RabbitMQ)
- Implementovat circuit breaker pro externí API

### 12.5 Použité Zdroje a Reference

#### **12.5.1 Primární Technologie**

**Backend:**
- **Go (Golang)** - https://go.dev/
  - Verze: 1.23.0+
  - Dokumentace: https://go.dev/doc/
  - Tour of Go: https://go.dev/tour/
  - Effective Go: https://go.dev/doc/effective_go

- **Gin Web Framework** - https://gin-gonic.com/
  - GitHub: https://github.com/gin-gonic/gin
  - Dokumentace: https://gin-gonic.com/docs/
  - Licence: MIT

- **GORM** - https://gorm.io/
  - GitHub: https://github.com/go-gorm/gorm
  - Dokumentace: https://gorm.io/docs/
  - Licence: MIT

- **PostgreSQL** - https://www.postgresql.org/
  - Verze: 15+
  - Dokumentace: https://www.postgresql.org/docs/
  - Licence: PostgreSQL License

**Frontend:**
- **React** - https://react.dev/
  - GitHub: https://github.com/facebook/react
  - Verze: 18+
  - Dokumentace: https://react.dev/learn
  - Licence: MIT

- **TypeScript** - https://www.typescriptlang.org/
  - Verze: 5+
  - Dokumentace: https://www.typescriptlang.org/docs/
  - Licence: Apache 2.0

- **Chakra UI** - https://chakra-ui.com/
  - GitHub: https://github.com/chakra-ui/chakra-ui
  - Dokumentace: https://chakra-ui.com/docs/
  - Licence: MIT

- **React Query (TanStack Query)** - https://tanstack.com/query/latest
  - GitHub: https://github.com/TanStack/query
  - Dokumentace: https://tanstack.com/query/latest/docs/react/overview
  - Licence: MIT

- **React Router** - https://reactrouter.com/
  - GitHub: https://github.com/remix-run/react-router
  - Verze: 6+
  - Dokumentace: https://reactrouter.com/en/main
  - Licence: MIT

**DevOps:**
- **Docker** - https://www.docker.com/
  - Dokumentace: https://docs.docker.com/
  - Licence: Apache 2.0

- **Docker Compose** - https://docs.docker.com/compose/
  - Dokumentace: https://docs.docker.com/compose/
  - Licence: Apache 2.0

#### **12.5.2 Go Knihovny a Dependencies**

| Knihovna | Účel | Odkaz | Licence |
|----------|------|-------|---------|
| `golang-jwt/jwt/v5` | JWT autentizace | https://github.com/golang-jwt/jwt | MIT |
| `golang.org/x/crypto` | Bcrypt hashing | https://pkg.go.dev/golang.org/x/crypto | BSD-3 |
| `gopkg.in/mail.v2` | SMTP emaily | https://github.com/go-gomail/gomail | MIT |
| `joho/godotenv` | Environment variables | https://github.com/joho/godotenv | MIT |
| `PuerkitoBio/goquery` | HTML parsing | https://github.com/PuerkitoBio/goquery | BSD-3 |
| `vanng822/go-premailer` | Email CSS inlining | https://github.com/vanng822/go-premailer | MIT |
| `gorm.io/driver/postgres` | PostgreSQL driver | https://github.com/go-gorm/postgres | MIT |

#### **12.5.3 Frontend Knihovny a Dependencies**

| Knihovna | Účel | Odkaz | Licence |
|----------|------|-------|---------|
| `@chakra-ui/react` | UI komponenty | https://chakra-ui.com/ | MIT |
| `@tanstack/react-query` | Server state management | https://tanstack.com/query | MIT |
| `react-router-dom` | Routing | https://reactrouter.com/ | MIT |
| `axios` | HTTP client | https://axios-http.com/ | MIT |
| `dompurify` | XSS sanitizace | https://github.com/cure53/DOMPurify | Apache-2.0 |
| `react-icons` | Ikony | https://react-icons.github.io/react-icons/ | MIT |
| `framer-motion` | Animace | https://www.framer.com/motion/ | MIT |

#### **12.5.4 Externí API a Služby**

**FAČR (Fotbalová asociace České republiky):**
- **URL:** https://is.fotbal.cz/
- **Typ integrace:** Custom wrapper/scraper vytvořen pro tento projekt
- **Účel:** Automatické získávání výsledků, rozpisů a tabulek
- **Poznámka:** Oficiální API není veřejně dostupné, použit web scraping s `goquery`
- **Zdrojový web:** https://www.fotbal.cz/

**YouTube Data API v3:**
- **Dokumentace:** https://developers.google.com/youtube/v3
- **Reference:** https://developers.google.com/youtube/v3/docs
- **Účel:** Načítání videí z klubového YouTube kanálu
- **Licence:** Google APIs Terms of Service
- **Quota:** 10,000 units/day (free tier)

**Zonerama:**
- **URL:** https://www.zonerama.com/
- **Typ integrace:** Custom HTML scraper vytvořen pro tento projekt
- **Účel:** Synchronizace fotografických alb
- **Poznámka:** Zonerama nemá veřejné API, použit scraping

**Umami Analytics:**
- **Oficiální web:** https://umami.is/
- **GitHub:** https://github.com/umami-software/umami
- **Dokumentace:** https://umami.is/docs
- **Typ nasazení:** Self-hosted
- **Účel:** Privacy-first web analytics
- **Licence:** MIT
- **Alternativa k:** Google Analytics

**OpenRouter AI:**
- **Oficiální web:** https://openrouter.ai/
- **Dokumentace:** https://openrouter.ai/docs
- **API Reference:** https://openrouter.ai/docs/api-reference
- **Účel:** AI generování článků pomocí LLM modelů
- **Modely použité:**
  - Mistral Small: https://mistral.ai/
  - Mistral Nemo: https://mistral.ai/news/mistral-nemo/
- **Ceny:** Free tier dostupný, placené modely pro produkci

**Google Maps Embed API:**
- **Dokumentace:** https://developers.google.com/maps/documentation/embed
- **Účel:** Zobrazení lokace klubu na mapě
- **Licence:** Google Maps Platform Terms

#### **12.5.5 Vývojové Nástroje**

| Nástroj | Účel | Odkaz |
|---------|------|-------|
| **Visual Studio Code** | Code editor | https://code.visualstudio.com/ |
| **Git** | Version control | https://git-scm.com/ |
| **Postman** | API testing | https://www.postman.com/ |
| **pgAdmin** | PostgreSQL management | https://www.pgadmin.org/ |
| **Go Playground** | Go testing | https://go.dev/play/ |
| **npm** | Package manager | https://www.npmjs.com/ |

#### **12.5.6 Knihy a Články**

**Knihy:**
1. **"Clean Code: A Handbook of Agile Software Craftsmanship"**
   - Autor: Robert C. Martin
   - Vydavatel: Prentice Hall
   - ISBN: 978-0132350884
   - Použito pro: Best practices v architektuře kódu

2. **"Designing Data-Intensive Applications"**
   - Autor: Martin Kleppmann
   - Vydavatel: O'Reilly Media
   - ISBN: 978-1449373320
   - Použito pro: Databázový design a škálovatelnost

3. **"The Go Programming Language"**
   - Autoři: Alan A. A. Donovan, Brian W. Kernighan
   - Vydavatel: Addison-Wesley
   - ISBN: 978-0134190440
   - Použito pro: Go best practices

**Online Zdroje:**
- **Go by Example** - https://gobyexample.com/
- **React TypeScript Cheatsheet** - https://react-typescript-cheatsheet.netlify.app/
- **PostgreSQL Tutorial** - https://www.postgresqltutorial.com/
- **JWT.io** - https://jwt.io/ (JWT debugger a dokumentace)
- **MDN Web Docs** - https://developer.mozilla.org/ (Web standardy)

#### **12.5.7 Bezpečnostní Standardy a Specifikace**

- **OWASP Top 10** - https://owasp.org/www-project-top-ten/
  - Použito pro: Identifikace a prevenci bezpečnostních rizik

- **GDPR (General Data Protection Regulation)**
  - Oficiální text: https://gdpr-info.eu/
  - Použito pro: Cookie consent, data privacy, právo na výmaz

- **JWT RFC 7519** - https://datatracker.ietf.org/doc/html/rfc7519
  - Použito pro: JWT implementace

- **bcrypt** - https://en.wikipedia.org/wiki/Bcrypt
  - Použito pro: Password hashing

#### **12.5.8 Inspirace a Podobné Projekty**

- **Hacker News Clone (Go + React)** - Inspirace pro strukturu projektu
- **Real World App** - https://github.com/gothinkster/realworld
  - Použito pro: API design patterns

#### **12.5.9 Community a Support**

- **Stack Overflow** - https://stackoverflow.com/
  - Tag: golang, reactjs, postgresql, docker
  
- **Go Forum** - https://forum.golangbridge.org/

- **React Discord** - https://discord.gg/react

- **r/golang** - https://www.reddit.com/r/golang/

- **r/reactjs** - https://www.reddit.com/r/reactjs/

#### **12.5.10 Citace a Poděkování**

Tento projekt využívá open-source software a byl vytvořen díky příspěvkům tisíců vývojářů v komunitách:

**Poděkování:**
- **Go Team** - Za výborný programovací jazyk a ekosystém
- **Facebook/Meta** - Za React a související nástroje
- **Chakra UI Team** - Za krásnou UI knihovnu
- **GORM Team** - Za powerful ORM pro Go
- **PostgreSQL Global Development Group** - Za spolehlivou databázi
- **Docker Inc.** - Za kontejnerizační platformu
- **Všem contributorom** open-source projektů použitých v této aplikaci

**Copyright Notice:**
Všechny použité technologie a knihovny jsou použity v souladu s jejich licencemi. Tento projekt je šířen pod MIT licencí a respektuje všechny licence závislostí.

**Akademické Použití:**
Tento projekt byl vytvořen pro vzdělávací účely jako součást maturitní zkoušky. Veškerý kód je původní dílo autora s výjimkou open-source knihoven a frameworků, které jsou řádně citovány výše.

### 12.6 Autor a Kontakt

**Projekt vytvořil:** Tomáš Dvořák  
**Datum:** Říjen 2025  
**Účel:** Maturitní projekt - Prezentace full-stack webového systému

**Licence:** MIT License

---

### 12.7 Poděkování

Tento projekt vznikl jako demonstrace moderních webových technologií a best practices v oblasti full-stack development. Děkuji všem, kteří přispěli k open-source nástrojům použitým v tomto projektu.

---

**Konec dokumentace**

*Verze 1.0 - Říjen 2025*

