Update main repo

This commit is contained in:
2026-06-09 18:14:59 +03:00
parent 7a53666245
commit f603d99587
6 changed files with 1326 additions and 10 deletions

View File

@@ -1,14 +1,19 @@
# Спецификация серверной части Minecraft проекта (Backend & Infrastructure)
## 1. Стек технологий
* **Бэкенд-платформа:** Go (чистый net/http с роутером github.com/go-chi/chi, для максимальной прозрачности и контроля).
* **Бэкенд-платформа:** Go (чистый net/http со стандартным роутером, для максимальной прозрачности и контроля, без внешних зависимостей).
* **База данных:** PostgreSQL (реляционная СУБД для надежного хранения транзакций токенов, пользователей и связей файлов).
* **Фронтенд (Сайт и Админка):** SPA на Vue.js / React / Svelte, либо классический монолит на Go-шаблонах (html/template) для максимального упрощения деплоя (всё в одном исполняемом файле).
* **Статический веб-сервер (CDN):** Nginx (занимается отдачей тяжелых файлов и проксированием API-запросов к Go-бэкенду).
* **Reverse Proxy и статика:** Caddy (автоматический HTTPS, отдача файлов, reverse proxy к Go-бэкенду — всё в одном бинарнике).
* **Контейнеризация:** Docker + Docker Compose.
* **Registry:** Gitea Container Registry.
* **Авто-обновление контейнеров:** Watchtower (pull новых образов из registry).
---
## 2. Архитектура хранилища файлов (Content-Addressable Storage - CAS)
Для экономии места на диске сервера и клиента, а также для сквозного кэширования, все файлы модов, библиотек и ассетов хранятся по их SHA-1 хэшам.
* **Путь на сервере:** `/var/www/cdn/files/[первые два символа хэша]/[полный хэш]`
@@ -85,7 +90,9 @@ CREATE TABLE launcher_releases (
## 4. Спецификация API Эндпоинтов
### 4.1. Автоматический деплой из CI/CD (Protected Route)
Эндпоинт, который вызывает твой пайплайн сборки после успешной компиляции лаунчера.
* **Маршрут:** `POST /api/admin/launcher/release`
* **Авторизация:** Заголовок `X-CI-Token: <секретный_токен_из_секретов_репозитория>`
* **Тело запроса (Multipart Form Data):**
@@ -96,12 +103,14 @@ CREATE TABLE launcher_releases (
* `file`: (бинарный файл лаунчера)
### 4.2. Yggdrasil API (Mojang Emulation)
* `POST /authserver/authenticate` — Аутентификация по логину/паролю. Возвращает UUID и токены.
* `POST /authserver/authenticate` — Аутентификация по логину/пароля. Возвращает UUID и токены.
* `POST /authserver/refresh` — Обновление сессии лаунчера по `clientToken` и `accessToken`.
* `POST /authserver/validate` — Быстрая проверка токена при запуске лаунчера.
* `GET /sessionserver/session/minecraft/profile/{uuid}` — Эндпоинт, к которому обращается сам игровой клиент для загрузки скинов игроков на сервере.
### 4.3. Публичное API сайта и лаунчера
* `POST /api/web/register` — Регистрация игрока.
* `POST /api/web/login` — Логин в личный кабинет сайта.
* `POST /api/web/profile/skin` — Загрузка скина (принимает PNG, считает его SHA-1, сохраняет в `/files/` и привязывает к юзеру).
@@ -164,9 +173,11 @@ CREATE TABLE launcher_releases (
```
### 5.2. Автоматическое добавление сервера в игру (servers.dat)
Перед запуском игры лаунчер читает блок `server_info` из манифеста. Если в `instances/<slug>/servers.dat` отсутствует сервер с IP `server_info.ip`, лаунчер парсит этот NBT-файл и добавляет в него новую запись:
* **Имя:** `server_info.name`
* **IP:** `server_info.ip`
Это избавляет игроков от ручного ввода IP в клиенте.
### 5.3. Пайплайн CI/CD Релиза Лаунчера
@@ -190,10 +201,334 @@ CREATE TABLE launcher_releases (
---
## 6. Конфигурация Nginx (CDN & Static)
## 6. Docker Compose
Для максимальной производительности Nginx настраивается на прямую отдачу файлов, минуя Go-бэкенд. Запросы к API проксируются.
Все сервисы проекта поднимаются на VPS через Docker Compose. Структура:
* **Статика (`/files/`)**: Настраивается отдавать файлы с вечным кэшированием, так как они никогда не меняются (меняется только сам хэш в манифесте).
* **Скины (`/skins/`)**: Кэширование настраивается на меньший срок (например, 1 час), чтобы при смене скина игроки видели изменения без жесткого сброса кэша.
* **API (`/api/`, `/authserver/`)**: Все запросы перенаправляются на локальный порт Go-приложения (например, `127.0.0.1:8080`).
### 6.1. Сервисы
#### `backend` — Go-приложение
Собирается как Docker-образ и публикуется в Gitea Container Registry.
```yaml
backend:
image: git.myserver.com/yourname/mc-backend:latest
container_name: mc-backend
restart: unless-stopped
environment:
DATABASE_URL: postgres://mcuser:${DB_PASSWORD}@postgres:5432/mcserver?sslmode=disable
CI_TOKEN: ${CI_TOKEN}
CDN_BASE_URL: https://cdn.myserver.com
volumes:
- cdn_files:/var/www/cdn/files
depends_on:
postgres:
condition: service_healthy
expose:
- "8080"
labels:
- "com.centurylinklabs.watchtower.enable=true"
```
*Переменные `.env` на сервере:*
```
DB_PASSWORD=...
CI_TOKEN=...
```
#### `postgres` — База данных
```yaml
postgres:
image: postgres:16-alpine
container_name: mc-postgres
restart: unless-stopped
environment:
POSTGRES_DB: mcserver
POSTGRES_USER: mcuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
- ./migrations:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mcuser -d mcserver"]
interval: 10s
timeout: 5s
retries: 5
expose:
- "5432"
```
Миграции из папки `./migrations/` применяются автоматически при первом запуске (согласно поведению официального образа PostgreSQL).
#### `caddy` — Reverse Proxy + статика
```yaml
caddy:
image: caddy:2-alpine
container_name: mc-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
- cdn_files:/var/www/cdn/files:ro
```
Caddy автоматически получает и обновляет TLS-сертификаты для всех доменов, указанных в Caddyfile.
#### `watchtower` — Авто-обновление контейнеров
```yaml
watchtower:
image: containrrr/watchtower
container_name: mc-watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_POLL_INTERVAL: 300
WATCHTOWER_LABEL_ENABLE: "true"
WATCHTOWER_NOTIFICATIONS: ""
```
Watchtower проверяет наличие новых образов каждые 300 секунд (5 минут). Обновляются только контейнеры с меткой `com.centurylinklabs.watchtower.enable=true`. Старые образы удаляются после успешного обновления (`CLEANUP=true`).
---
### 6.2. Volumes
```yaml
volumes:
pgdata:
driver: local
cdn_files:
driver: local
caddy_data:
driver: local
caddy_config:
driver: local
```
| Volume | Назначение | Доступ |
|---|---|---|
| `pgdata` | Данные PostgreSQL | `postgres` (rw) |
| `cdn_files` | CAS-файлы (моды, ассеты, скины) | `backend` (rw), `caddy` (ro) |
| `caddy_data` | TLS-сертификаты и состояние Caddy | `caddy` (rw) |
| `caddy_config` | Кэш конфигурации Caddy | `caddy` (rw) |
Том `cdn_files` примонтирован read-only в Caddy, чтобы proxy-сервер не мог случайно модифицировать файлы. Только `backend` имеет права на запись.
---
### 6.3. Полный docker-compose.yml
```yaml
services:
caddy:
image: caddy:2-alpine
container_name: mc-caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
- cdn_files:/var/www/cdn/files:ro
backend:
image: git.myserver.com/yourname/mc-backend:latest
container_name: mc-backend
restart: unless-stopped
environment:
DATABASE_URL: postgres://mcuser:${DB_PASSWORD}@postgres:5432/mcserver?sslmode=disable
CI_TOKEN: ${CI_TOKEN}
CDN_BASE_URL: https://cdn.myserver.com
volumes:
- cdn_files:/var/www/cdn/files
depends_on:
postgres:
condition: service_healthy
expose:
- "8080"
labels:
- "com.centurylinklabs.watchtower.enable=true"
postgres:
image: postgres:16-alpine
container_name: mc-postgres
restart: unless-stopped
environment:
POSTGRES_DB: mcserver
POSTGRES_USER: mcuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
- ./migrations:/docker-entrypoint-initdb.d
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mcuser -d mcserver"]
interval: 10s
timeout: 5s
retries: 5
expose:
- "5432"
watchtower:
image: containrrr/watchtower
container_name: mc-watchtower
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
WATCHTOWER_CLEANUP: "true"
WATCHTOWER_POLL_INTERVAL: 300
WATCHTOWER_LABEL_ENABLE: "true"
volumes:
pgdata:
cdn_files:
caddy_data:
caddy_config:
```
---
## 7. Конфигурация Caddy
Caddy выполняет три роли: reverse proxy для API, отдача CAS-файлов и автоматический HTTPS.
```
# Caddyfile
# CDN — отдача CAS-файлов
cdn.myserver.com {
root * /var/www/cdn/files
@hasPrefix path /files/*
handle /files/* {
file_server {
hide .htaccess
}
header Cache-Control "public, max-age=31536000, immutable"
}
handle /skins/* {
file_server {
hide .htaccess
}
header Cache-Control "public, max-age=3600"
}
}
# API и Yggdrasil
api.myserver.com {
reverse_proxy backend:8080
}
```
**Логика кэширования:**
* **CAS-файлы (`/files/`):** `Cache-Control: max-age=31536000, immutable` — на 1 год, потому что содержимое файла никогда не меняется (меняется только хэш в манифесте).
* **Скины (`/skins/`):** `Cache-Control: max-age=3600` — 1 час, чтобы игроки видели смену скина без жёсткого сброса кэша.
---
## 8. Docker-образы и CI/CD
### 8.1. Тегирование образов
Все образы публикуются в Gitea Container Registry с двумя тегами:
* `latest` — всегда последняя успешная сборка
* `${GIT_COMMIT_SHA:0:8}` — первые 8 символов хэша коммита для отслеживаемости
### 8.2. Dockerfile для backend
```dockerfile
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /mc-backend ./cmd/server
FROM alpine:3.19
RUN apk --no-cache add ca-certificates
WORKDIR /app
COPY --from=builder /mc-backend .
RUN adduser -D -g '' appuser
USER appuser
EXPOSE 8080
ENTRYPOINT ["/app/mc-backend"]
```
Используется multi-stage сборка: финальный образ на базе alpine (~20 MB), без Go toolchains и исходного кода. Приложение запускается от непривилегированного пользователя.
### 8.3. Схема деплоя
```text
[Push в main] → [CI: тесты] → [CI: сборка Docker-образа]
[Push в Gitea Container Registry]
(теги: latest + sha)
[Watchtower на VPS замечает новый образ]
(опрос каждые 5 мин)
[Pull → stop old → start new container]
[Healthcheck PostgreSQL: OK]
[Backend отвечает на :8080]
[Caddy проксирует запросы]
```
---
## 9. Переменные окружения на VPS
Файл `.env` в корне проекта на сервере:
```
DB_PASSWORD=your-secure-password-here
CI_TOKEN=your-ci-secret-token
```
Файл `.env` **не коммитится** в репозиторий. CI_TOKEN также хранится в секретах Gitea (Settings → Secrets) для использования в CI-пайплайне.
---
## 10. Структура проекта на VPS
```
/opt/mc-server/
├── .env # Секретные переменные (не в git)
├── docker-compose.yml
├── Caddyfile
├── migrations/
│ ├── 001_init.sql
│ └── 002_launcher_releases.sql
└── backups/ # Резервные копии PostgreSQL
```
---
## 11. Резервное копирование
Ежедневный dump PostgreSQL через cron на хосте:
```bash
docker exec mc-postgres pg_dump -U mcuser mcserver | gzip > /opt/mc-server/backups/mcserver_$(date +%Y%m%d).sql.gz
```
CAS-файлы рекомендуется бэкапить отдельно (rsync) — при утере их можно восстановить из манифестов модпаков, но займёт время.