# 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. **CORS** — `Access-Control-Allow-*` headers + OPTIONS handling ### 3.5. Content-Addressable Storage (CAS) Путь хранения: `//` Пример: `/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. Проверка сессии: `validate` → `refresh` → если неудачно → показ экрана логина 3. GUI: BorderLayout (left=серверы, center=контент, bottom=управление) 4. Выбор сервера → MainScreen 5. PLAY → (заглушка, TODO: launch.Prepare → Game.Start) 6. Настройки: слайдер 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`) 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=` - 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. Команды ### Сервер ```bash cd server/ go build -o mrixscraft-server ./cmd/server go test ./... go run ./cmd/server ``` ### Лаунчер ```bash cd launcher/ go build -o mrixscraft-launcher ./cmd/launcher go run ./cmd/launcher ``` ### Docker ```bash cd server/ docker compose up -d ```