25 KiB
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)
Порядок применения (от внешнего к внутреннему):
- Recovery — catch panics → 500 + stack trace log
- Logging — method, path, status, duration, remote addr
- RateLimit — per-IP token bucket (30 req/min, burst 60)
- CORS —
Access-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()
- Загрузка настроек из
launcher.json(или defaults) - Проверка сессии:
validate→refresh→ если неудачно → показ экрана логина - GUI: BorderLayout (left=серверы, center=контент, bottom=управление)
- Выбор сервера → MainScreen
- PLAY → (заглушка, TODO: launch.Prepare → Game.Start)
- Настройки: слайдер RAM (1024–16384 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)
-
Prepare:
- Скачивание манифеста с сервера
- LoadManifest → парсинг JSON
- Java.Find(version) → поиск или (TODO) загрузка JRE
- WorkerPool(4) → параллельная загрузка файлов с SHA-1 проверкой
- cleanupUnknownMods → неизвестные моды в mods_backup/
-
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}, и т.д.
- JVM args:
-
Start:
exec.Command(java, args...).Run()
5. Общие утилиты
5.1. server/pkg/utils/
Функции:
SHA1Bytes(data) → string— SHA-1 hex digestSHA256Bytes(data) → string— SHA-256 hex digestSHA1File(path) → (string, error)— SHA-1 файлаWriteJSON(w, status, v)— HTTP JSON responseWriteError(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 digestUnzip(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
.oldrename не протестирован.
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