---
layout: 'page'
uri: '/framework/presentation/http-server'
position: 1
slug: 'framework-presentation-http-server'
parent: 'framework-presentation'
navTitle: 'HTTP Server'
title: 'HTTP Server'
description: 'Routing, SPA fallback, Vite proxy, response helpery, HTTPError interface.'
---

# HTTP Server

## Proč

Server je jediný vstupní bod pro HTTP provoz. Centralizuje routing, middleware chain a response format na jednom místě, takže handlery a doména zůstávají čisté.

## Jak

### Routing

Server používá stdlib `net/http.ServeMux` s Go 1.22+ pattern routingem. Routy se registrují v `presentation/http/server/`.

**Veřejné:**

| Metoda | Route | Popis |
|---|---|---|
| GET | `/health` | Health check |
| POST | `/api/v1/auth/login` | Přihlášení (nickname + heslo) |
| POST | `/api/v1/auth/refresh` | Obnovení tokenu |
| GET | `/{path...}` | SPA fallback |

**Chráněné (JWT Bearer + `AuthMiddleware` wrap):**

| Metoda | Route | Command/Query | Permission |
|---|---|---|---|
| POST | `/api/v1/auth/logout` | `LogoutCommand` | `auth:logout` |
| GET | `/api/v1/profile` | `GetProfileQuery` | `profile:read` |
| PUT | `/api/v1/profile/password` | `ChangePasswordCommand` | `profile:update` |
| GET | `/api/v1/dashboard/user` | `GetUserDashboardQuery` | `dashboard:read` |

**Admin:**

| Metoda | Route | Command/Query | Permission |
|---|---|---|---|
| GET | `/api/v1/dashboard/admin` | `GetAdminDashboardQuery` | `admin:dashboard:read` |
| GET | `/api/v1/admin/users` | `ListUsersQuery` | `admin:users:read` |
| POST | `/api/v1/admin/users` | `CreateUserCommand` | `admin:users:create` |
| PUT | `/api/v1/admin/users/{id}` | `UpdateUserCommand` | `admin:users:update` |
| DELETE | `/api/v1/admin/users/{id}` | `DeleteUserCommand` | `admin:users:delete` |

Žádné samostatné role-guard middleware není potřeba -- bus `AuthorizeMiddleware` v kombinaci s `IsPermissionAllowedForRole` (`admin:*` permissions povolí jen admin role) pokrývá oddělení uživatel / admin.

### SPA fallback

Catch-all `GET /{path...}` vrací soubory z embedovaného `public.FS`. Neexistující cesty vrátí `index.html` -- o routing rozhoduje Vue Router na klientu.

Při servírování `index.html` do něj `SPAHandler` **injektuje runtime config** jako `<meta name="gokick:…">` tagy (Sentry DSN, environment, debug flag) -- jeden buildnutý image tak může běžet ve více prostředích bez rebuildu. SPA je čte přes `runtimeConfig.ts`. Detail viz [Sentry guide](/guides/sentry) + [Config → Sentry](/framework/infrastructure/config#sentry).

### Vite dev proxy

Při vývoji frontend běží na Vite dev serveru (`yarn dev`). Proxy směruje API cesty, health check a favicon na Go backend:

```typescript
// vite.config.ts
// Port se čte z APP_HTTP_PORT v .env (default: 3000)
server: {
    proxy: {
        '^/(api|health|favicon\\.ico)': {
            target: `http://localhost:${backendPort}`,
            changeOrigin: true,
        },
    },
}
```

### Response helpery

Balíček `presentation/http/response/` poskytuje tři funkce pro jednotný JSON output:

```go
// presentation/http/response/response.go

func JSON(w http.ResponseWriter, status int, data any)
func Error(w http.ResponseWriter, status int, err error)
func HandleError(w http.ResponseWriter, err error)
```

- `JSON()` -- serializuje `data` do JSON a nastaví `Content-Type` + status code.
- `Error()` -- zapíše chybovou odpověď. Pokud error implementuje `FieldError` (např. `*shared.ValidationError` s vyplněným polem), použije jeho název jako klíč v JSON; jinak "general".
- `HandleError()` -- automaticky mapuje error na správný HTTP status + volá `Error()`.

**Error response shape** — key-based, každý error má jeden klíč:

```json
// ValidationError{Field: "nickname", Message: "..."}
{ "nickname": "nickname is required" }

// AuthError / PermissionError / systémové chyby
{ "general": "invalid credentials" }
```

Frontend definuje vlastní typ a přiřazuje celé tělo přímo do reactive errors:

```typescript
type LoginErrors = { general?: string; nickname?: string; password?: string };
const errors = ref<LoginErrors>({});

// on failure:
errors.value = result.data;  // server key (general / nickname / …) mapuje na formulář
```

Detaily viz [Errors & Events](/framework/domain/errors-events) a [Frontend Utils](/guides/frontend-utils).

### HTTPError interface

```go
type HTTPError interface {
    error
    HTTPStatus() int
}
```

`HandleError` kontroluje, zda error implementuje `HTTPError`:

- **Ano** -- použije `HTTPStatus()` (např. 400, 401, 403).
- **Ne** -- vrátí 500 Internal Server Error.

## Graceful shutdown

`server.Start(ctx context.Context) error` poslouchá na cancellation z ctx. `cmd/main.go` vytvoří root ctx přes `signal.NotifyContext(ctx, SIGINT, SIGTERM)` a propaguje ho přes `Application.Run` → Cobra → `ServeCommand`. Když přijde signál:

1. `ctx.Done()` se odpálí.
2. `Start` zavolá `http.Server.Shutdown(shutdownCtx)` s 30s timeoutem.
3. `Shutdown` přestane přijímat nová spojení a počká na dokončení inflight requestů.
4. Po dokončení (nebo timeoutu) se vrátí; `Run` ukončí proces s exit code 0.

Pokud handler trvá déle než 30s, `Shutdown` vrátí `context.DeadlineExceeded` a `Run` exitne s nenulovým kódem. Pro long-running endpointy (uploads, exports) je 30s konzervativní default — zvedněte `shutdownGracePeriod` v `server.go` podle potřeby.


## Detaily

- Domain error typy (`ValidationError` 400, `AuthError` 401, `PermissionError` 403) implementují `HTTPError` implicitně (duck typing). Žádný import mezi `response/` a `domain/`. Detaily viz [Errors & Events](/framework/domain/errors-events).
- Server struct drží konfiguraci, logger, JWT service, rate-limitery, IP extractor a HTTP handlery -- **ne** `*http.ServeMux` ani middleware chain. Ty se staví per-call uvnitř `Start`: `registerRoutes()` vrátí lokální `*http.ServeMux` a `buildMiddlewareChain()` ho obalí. `Start(ctx)` pak spustí `http.Server.ListenAndServe` v goroutině a čeká na `ctx.Done()` nebo server error (viz [Graceful shutdown](#graceful-shutdown)).
- **Pořadí v `buildMiddlewareChain` je invariant:** `Trace → IP → Recovery → Security → CORS → CSRF → Logging`. `Trace` a `IP` jsou čisté context-settery (jen razítkují ctx, nikdy neselžou), takže běží **před** `Recovery` schválně — každý panic report tím nese `trace_id` i klientskou IP (Sentry capture čte IP z ctx). Přehození `IP` za `Recovery` by tichounce shodilo `user.ip_address` na zachycených panikách; hlídá to regresní test proti `buildMiddlewareChain`.
- Response balíček nemá žádné závislosti kromě stdlib.

---

[← Presentation](/framework/presentation.md) | [Handlers & Middleware →](/framework/presentation/http-handlers.md)