Files
MrixsCraft/docs/state.md
2026-06-09 18:14:59 +03:00

25 KiB
Raw Blame History

MrixsCraft — Текущее состояние проекта

Дата: 2026-06-05 Версия: dev (pre-alpha)


1. Обзор проекта

MrixsCraft — приватный Minecraft-сервер с веб-лаунчером. Проект состоит из двух независимых репозиториев, объединённых через git submodules:

  • server/ — бэкенд на Go (net/http + PostgreSQL)
  • launcher/ — десктопный лаунчер на Go + Fyne GUI

Домен: minecraft.mrixs.me CDN: cdn.mrixs.me Registry: gitea.mrixs.me/mrixs/mrixscraft-server


2. Структура файлов

MC-server/
├── .gitmodules                          # Submodules: launcher, server
├── docs/
│   ├── server/Specification.md          # Спецификация серверной части (RU)
│   └── launcher/Specification.md        # Спецификация лаунчера (RU)
│
├── launcher/                            # Git submodule (MrixsCraft-launcher)
│   ├── go.mod                           # Module: gitea.mrixs.me/Mrixs/MrixsCraft-launcher (Go 1.22)
│   ├── go.sum
│   ├── README.md
│   ├── cmd/launcher/main.go             # Точка входа лаунчера
│   ├── internal/
│   │   ├── auth/auth.go                 # Yggdrasil клиент (authenticate/refresh/validate)
│   │   ├── config/config.go             # Настройки лаунчера, системные пути
│   │   ├── fetcher/fetcher.go           # HTTP-загрузчик файлов с SHA-1 верификацией
│   │   ├── java/java.java               # Поиск/загрузка JRE
│   │   ├── launch/
│   │   │   ├── launch.go                # Запуск Minecraft (classpath, аргументы, exec)
│   │   │   └── manifest.go             # Парсинг manifest.json
│   │   ├── selfupdate/selfupdate.go     # Автообновление лаунчера
│   │   └── ui/
│   │       ├── ui.go                    # Fyne bootstrap, главное окно
│   │       ├── screens/screens.go       # Экраны (Main, Login, Settings)
│   │       ├── components/components.go # Виджеты (ServerCard, PlayButton, Avatar)
│   │       └── theme/theme.go           # Minecraft-стилизация Fyne
│   └── pkg/utils/utils.go               # SHA1File, SHA1Bytes, Unzip
│
└── server/                              # Git submodule (MrixsCraft-server)
    ├── go.mod                           # Module: gitea.mrixs.me/Mrixs/MrixsCraft-server (Go 1.25)
    ├── go.sum
    ├── README.md
    ├── Dockerfile                       # Multi-stage build (~20 MB, golang:1.25-alpine + alpine:3.19)
    ├── docker-compose.yml               # Caddy + backend + postgres + watchtower
    ├── Caddyfile                        # Reverse proxy, CDN, HTTPS
    ├── .env.example                     # Шаблон переменных окружения
    ├── .gitignore
    ├── .gitea/
    │   └── workflows/
    │       └── ci.yml                   # CI: lint → test → build → docker push
    ├── migrations/
    │   ├── README.md                    # Инструкция по применению миграций
    │   ├── 001_init.sql                 # Полная схема БД (6 таблиц + индексы)
    │   └── 002_migration_history.sql    # Таблица отслеживания миграций
    ├── cmd/
    │   ├── server/main.go               # Точка входа: маршруты, middleware, graceful shutdown
    │   └── ci-release/main.go           # CLI-утилита для загрузки релиза лаунчера из CI
    ├── internal/
    │   ├── admin/admin.go               # CRUD модпаков, загрузка файлов, манифесты, launcher release
    │   ├── api/api.go                   # Публичное API: регистрация, логин, скины, плащи, launcher
    │   ├── auth/auth.go                 # Yggdrasil протокол: authenticate/refresh/validate/invalidate
    │   ├── cas/cas.go                   # Content-Addressable Storage: отдача файлов по SHA-1 хэшу
    │   ├── config/config.go             # Конфигурация из ENV (порт, БД, CAS, JWT, CI token)
    │   ├── database/database.go         # PostgreSQL (pgx/pgxpool), модели данных
    │   ├── middleware/middleware.go      # CORS, Logging, Recovery, RateLimiter
    │   ├── session/cleanup.go           # Фоновая очистка expired-сессий
    │   └── templates/
    │       ├── templates.go             # Go html/template: per-page parsing (base+page pair)
    │       └── html/
    │           ├── base.html            # Базовый layout (тёмная тема, зелёный акцент)
    │           ├── index.html           # Главная страница сервера
    │           ├── login.html           # Форма входа (POST → /api/web/login)
    │           ├── register.html        # Форма регистрации (POST → /api/web/register)
    │           └── profile.html         # Профиль игрока (скины, плащи, лаунчер)
    └── pkg/utils/utils.go               # SHA1Bytes, SHA256Bytes, SHA1File, WriteJSON, WriteError, Unzip

3. Серверная часть (server/)

3.1. Технологический стек

Компонент Технология
Язык Go 1.25
HTTP net/http (стандартная библиотека)
База данных PostgreSQL 16 + pgx/v5
Аутентификация bcrypt, crypto/rand токены
Хеширование SHA-1 (CAS), SHA-256 (релизы лаунчера)
Контейнеризация Docker multi-stage build
Reverse proxy Caddy 2
Автообновление Watchtower + Gitea Container Registry
CI/CD Gitea Actions (.gitea/workflows/ci.yml)

Зависимости go.mod:

  • github.com/jackc/pgx/v5 v5.6.0 — PostgreSQL драйвер
  • golang.org/x/crypto — bcrypt для хеширования паролей

3.2. База данных

Файлы миграций: server/migrations/

Миграция Назначение
001_init.sql Начальная схема: 6 таблиц + 9 индексов
002_migration_history.sql Таблица для отслеживания применённых миграций
README.md Инструкция по ручному применению

7 таблиц:

Таблица Назначение
users Пользователи (username, email, password_hash, uuid, role)
player_textures Скины и плащи (skin_hash, cape_hash → CAS)
yggdrasil_sessions Сессии авторизации (access_token, client_token, expires_at)
modpacks Модпаки/серверы (slug, name, minecraft_version, java_version, server_ip)
global_files CAS-реестр файлов (sha1 PK, size_bytes, file_name, mime_type)
launcher_releases Релизы лаунчера (version, os, arch, sha256, file_path)
migration_history Отслеживание применённых миграций (filename, applied_at)

Индексы: 9 индексов для быстрого поиска по токенам, UUID, username, email, role, файлам.

3.3. API Endpoints

Yggdrasil (Mojang-совместимый)

Метод Путь Описание
POST /authserver/authenticate Аутентификация по логину/паролю
POST /authserver/refresh Обновление токена
POST /authserver/validate Проверка токена (204 No Content)
POST /authserver/invalidate Инвалидация токена
POST /authserver/signout Выход (удаление всех сессий пользователя)
GET /sessionserver/session/minecraft/profile/{uuid} Профиль игрока с текстурами

Публичное API (сайт)

Метод Путь Описание
POST /api/web/register Регистрация игрока (email через net/mail.ParseAddress)
POST /api/web/login Логин на сайт
POST /api/web/profile/skin Загрузка скина (PNG, валидация размеров)
POST /api/web/profile/cape Загрузка плаща (PNG)
DELETE /api/web/profile/skin Удаление скина
DELETE /api/web/profile/cape Удаление плаща
GET /api/web/profile/{uuid} Профиль игрока (UUID, username, текстуры)

Лаунчер

Метод Путь Описание
GET /api/launcher/latest Последняя версия лаунчера (?os=&arch=)
GET /api/servers.json Список активных модпаков
GET /api/instances/{slug}/manifest.json Манифест модпака

Файловый сервер (CAS)

Метод Путь Описание
GET /files/{hash} Файл по SHA-1 хэшу (40 hex chars)
GET /files/launcher/{version}/{os}/{arch}/{filename} Бинарник лаунчера
GET /skins/{hash} Скин/плащ по хэшу

Веб-шаблоны (HTML)

Метод Путь Описание
GET / Главная страница (Minecraft-стиль)
GET /login Страница логина (форма → /api/web/login)
GET /register Страница регистрации (форма → /api/web/register)
GET /profile Профиль игрока (скины, плащи, скачивание лаунчера)

Админ-панель (Bearer token + role=admin)

Метод Путь Описание
GET /api/admin/modpacks Список модпаков
POST /api/admin/modpacks Создать модпак
PUT /api/admin/modpacks/{id} Обновить модпак
DELETE /api/admin/modpacks/{id} Деактивировать модпак
POST /api/admin/modpacks/{slug}/upload Загрузка файлов (multipart, до 500 MB)
POST /api/admin/modpacks/{slug}/manifest Генерация manifest.json
POST /api/admin/launcher/release Загрузка релиза лаунчера (X-CI-Token)
GET /admin Веб-интерфейс админ-панели (требует роль admin)

3.4. Middleware цепочки

 Recovery → Logging → RateLimit → CORS → mux
 (outermost)                        (innermost)

Порядок применения (от внешнего к внутреннему):

  1. Recovery — catch panics → 500 + stack trace log
  2. Logging — method, path, status, duration, remote addr
  3. RateLimit — per-IP token bucket (30 req/min, burst 60)
  4. CORSAccess-Control-Allow-* headers + OPTIONS handling

3.5. Content-Addressable Storage (CAS)

Путь хранения: <CASDir>/<first_2_chars_of_hash>/<full_40_char_hash> Пример: /var/www/cdn/files/a1/a1b2c3d4e5...

  • Иммутабельность: файлы никогда не перезаписываются
  • Cache-Control: public, max-age=31536000, immutable (1 год)
  • Content-Type определяется по расширению оригинального имени файла (из global_files.file_name)
  • Верификация: VerifyAndStore — сравнивает SHA-1 загруженных данных с ожидаемым хэшем (constant-time)
  • Конкурентная безопасность: per-hash sync.Mutex предотвращает race condition при параллельной записи одного файла. StoreFile идемпотентен — если файл уже записан другим воркером, возвращает существующий hash.

Экспортируемые функции (cas пакет):

  • StoreFile(casDir, data) → (hash, error) — сохранение в CAS (потокобезопасен)
  • FileExists(casDir, hash) → bool — проверка наличия
  • VerifyAndStore(casDir, data, expectedHash) → (hash, error) — верификация + сохранение

3.6. Валидация email

Реализована через стандартную библиотеку net/mail.ParseAddress():

  • Проверка длины: ≤ 254 символов (RFC 5321)
  • Синтаксический разбор: mail.ParseAddress() (RFC 5322)
  • Отклоняет адреса типа a@b., user@, @domain.com

3.7. CI/CD Pipeline

Файл: .gitea/workflows/ci.yml (Gitea Actions, совместим с GitHub Actions).

Шаг Что делает Условие
lint go vet ./... + gofmt -l . Всегда
test go test ./... -v -race -cover После lint
build go build -o mrixscraft-server ./cmd/server После test
docker docker build + push в реестр Только master ветка

Registry: gitea.mrixs.me/mrixs/mrixscraft-server:latest, :sha

Docker аутентификация: PAT (Personal Access Token) через secrets.PACKAGES_TOKEN + gitea.repository_owner. GITHUB_TOKEN read-only для packages в Gitea (issue #23642).

Required secrets:

  • PACKAGES_TOKEN — PAT с scopes: read:package, write:package

3.8. Переменные окружения

Переменная По умолчанию Обязательная
SERVER_PORT 8080 Нет
DATABASE_URL Да
CAS_DIR /var/www/cdn/files Нет
SKINS_DIR /var/www/cdn/skins Нет
JWT_SECRET Да
CI_SECRET Нет
BASE_URL https://minecraft.mrixs.me Нет

3.9. Docker Compose сервисы

Сервис Образ Роль
caddy caddy:2-alpine Reverse proxy, HTTPS, статика
backend gitea.mrixs.me/mrixs/mrixscraft-server:latest Go-приложение, порт 8080
postgres postgres:16-alpine БД, healthcheck, авто-миграции
watchtower containrrr/watchtower Авто-обновление контейнеров (5 мин)

Volumes: pgdata, cdn_files (rw для backend, ro для caddy), caddy_data, caddy_config


4. Лаунчер (launcher/)

4.1. Технологический стек

Компонент Технология
Язык Go 1.22
GUI Fyne v2.4.5
Авторизация Yggdrasil API (серверная часть)
Хеширование SHA-1 (файлы), SHA-256 (автообновление)

Зависимости go.mod:

  • fyne.io/fyne/v2 v2.4.5 — GUI фреймворк

4.2. Архитектура пакетов

Пакет Назначение
internal/auth Yggdrasil клиент: authenticate, refresh, validate, ensureValid, сохранение session.json
internal/config launcher.json (ServerURL, MemoryMB, ExtraArgs, окно), системные пути
internal/fetcher Download() с SHA-1 верификацией, WorkerPool (4 воркера)
internal/java Поиск JRE (JavaDir/version/bin/java), заглушка для автозагрузки
internal/launch Manifest (парсинг), Game (Prepare, BuildCommand, Start, cleanupUnknownMods)
internal/selfupdate Check(), Apply(), Restart() — SHA-256 верификация, rename для Windows
internal/ui Fyne bootstrap, главное окно
internal/ui/screens MainScreen, LoginScreen, SettingsScreen
internal/ui/components ServerCard, PlayButton, SettingsButton, LogoutButton, AvatarImage, ProgressBar
internal/ui/theme MinecraftTheme — тёмная тема, зелёный акцент
pkg/utils SHA1File, SHA1Bytes, Unzip

4.3. Жизненный цикл лаунчера

main() → config.EnsureRoot() → config.Load() → auth.NewFromConfig() → EnsureValid() → ui.Launch()
  1. Загрузка настроек из launcher.json (или defaults)
  2. Проверка сессии: validaterefresh → если неудачно → показ экрана логина
  3. GUI: BorderLayout (left=серверы, center=контент, bottom=управление)
  4. Выбор сервера → MainScreen
  5. PLAY → (заглушка, TODO: launch.Prepare → Game.Start)
  6. Настройки: слайдер RAM (102416384 MB), дополнительные JVM-флаги

4.4. Файловая структура клиента

ОС Корневая директория
Windows %APPDATA%\MrixsCraft\
macOS ~/Library/Application Support/MrixsCraft/
Linux ~/.MrixsCraft/

Содержимое: launcher.json, session.json, Java/{8,17,21}/, assets/, libraries/, instances/{slug}/ (mods, config, resourcepacks, mods_backup)

4.5. Запуск игры (launch)

  1. Prepare:

    • Скачивание манифеста с сервера
    • LoadManifest → парсинг JSON
    • Java.Find(version) → поиск или (TODO) загрузка JRE
    • WorkerPool(4) → параллельная загрузка файлов с SHA-1 проверкой
    • cleanupUnknownMods → неизвестные моды в mods_backup/
  2. BuildCommand:

    • JVM args: -Xms/-Xmx (RAM), пользовательские аргументы
    • Authlib-injector: -javaagent:authlib-injector.jar=<URL>
    • Classpath: все .jar из манифеста + libraries/*
    • Game args: интерполяция ${player_name}, ${auth_uuid}, ${auth_access_token}, и т.д.
  3. Start: exec.Command(java, args...).Run()


5. Общие утилиты

5.1. server/pkg/utils/

Функции:

  • SHA1Bytes(data) → string — SHA-1 hex digest
  • SHA256Bytes(data) → string — SHA-256 hex digest
  • SHA1File(path) → (string, error) — SHA-1 файла
  • WriteJSON(w, status, v) — HTTP JSON response
  • WriteError(w, status, msg) — HTTP JSON error ({"error": msg})
  • Unzip(data, dest) → ([]string, error) — распаковка ZIP с zip-slip защитой

Потребители: internal/auth, internal/admin, internal/api, internal/cas

5.2. launcher/pkg/utils/

Функции:

  • SHA1File(path) → (string, error) — SHA-1 файла
  • SHA1Bytes(data) → string — SHA-1 hex digest
  • Unzip(src, dest) → error — распаковка ZIP-файла с zip-slip защитой

Потребитель: internal/fetcher

Примечание: Утилиты SHA-1/SHA-256/Unzip дублируются между сервером и лаунчером (разные модули). Это нормально, т.к. лаунчер — отдельный модуль.


6. Тестирование

6.1. Серверные тесты

Файл Пакет Что тестирует
internal/cas/cas_test.go cas isValidHash, StoreFile, FileExists, VerifyAndStore, detectContentType, StoreFile_ConcurrentSameHash
internal/auth/auth_test.go auth GenerateToken, GenerateUUID, HashPassword, VerifyPassword, IsBcryptHash, ExtractBearer
internal/api/api_test.go api Валидация register/login (email, длина, пустые поля), параметры launcherLatest, auth middleware, регистрация маршрутов
internal/session/cleanup_test.go session StartCleanupWorker с nil DB (graceful no-op)

Тесты используют testing + httptest. DB-интеграционные тесты отсутствуют (ручное тестирование).

6.2. Тесты лаунчера

Отсутствуют (тестирование GUI затруднено).


7. Известные TODO и незавершённые части

Сервер

  • Поиск по БД: API не имеет полнотекстового поиска файлов.
  • Автодеплой релизов лаунчера: CI собирает и пушит Docker-образ, но не вызывает POST /api/admin/launcher/release. Обработчик на сервере и CLI-утилита (cmd/ci-release/) есть, но не подключены к CI-пайплайну.
  • SIGHUP reload: Reload конфига без рестарта — отложен (требует рефакторинга handler'ов на config.Atomic).

Лаунчер

  • PLAY кнопка: Привязка к launch.Prepare + Game.Start не реализована (заглушка).
  • Server list: Список серверов hardcoded (hitech, vanilla), не загружается с /api/servers.json.
  • Java auto-download: java.Find() возвращает ошибку вместо загрузки JRE.
  • Автообновление: selfupdate.Check() вызывается только в main.go если version != "dev".
  • Аватарка: 8x64 лицо из скина не рендерится (заглушка — пустое изображение).
  • Новости/патчноуты: TODO в MainScreen.
  • servers.dat: Добавление сервера в NBT-файл не реализовано.

Инфраструктура

  • Миграции: Ручное применение (нет автоматического Go-мигратора).
  • backup скрипт: Не добавлен в cron (только описан в спецификации).
  • Graceful restart лаунчера: Windows .old rename не протестирован.

8. Архитектурные заметки

Паттерны

  • handler-per-domain: Каждый домен (auth, admin, api, cas, templates) имеет свой Handler с методами NewHandler(db, cfg) и RegisterRoutes(mux).
  • Shared utilities: pkg/utils содержит общие HTTP и crypto функции, используемые несколькими пакетами.
  • Middleware chain: Функциональная композиция через var handler http.Handler = mux.
  • CAS: Content-Addressable Storage — файлы хранятся по хэшу, дедупликация автоматическая. Per-hash mutex для конкурентной безопасности.
  • Per-page templates: Каждая HTML-страница парсит base.html + свой файл отдельно (template.New("base.html").ParseFS(..., "html/base.html", "html/page.html")), хранится в map[string]*template.Template. Это предотвращает перезапись {{define "content"}} блоков при wildcard-парсинге (баг исправлен 2026-06-04).

Безопасность

  • bcrypt для паролей (cost=default)
  • SHA-1 для CAS (не криптографический контекст — целостность файлов)
  • ConstantTimeCompare для хэш-сравнений
  • Bearer token для API авторизации
  • X-CI-Token для CI/CD эндпоинта (constant-time comparison)
  • Path traversal защита в CAS и launcher asset serving
  • Zip-slip защита при распаковке
  • Rate limiting (token bucket, per-IP)
  • Recovery middleware (panic → 500)
  • Email validation через net/mail.ParseAddress (RFC 5322)

Разделение модулей

  • server/ и launcher/разные Go модули (разные go.mod).
  • Лаунчер может собираться и работать автономно.
  • Единственная связь — HTTP API (Yggdrasil + REST).

9. Команды

Сервер

cd server/
go build -o mrixscraft-server ./cmd/server
go test ./...
go run ./cmd/server

Лаунчер

cd launcher/
go build -o mrixscraft-launcher ./cmd/launcher
go run ./cmd/launcher

Docker

cd server/
docker compose up -d