43 Commits

Author SHA1 Message Date
a17b80230a fix: remove unused auth import in templates.go
All checks were successful
CI / lint (push) Successful in 17s
CI / test (push) Successful in 44s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m13s
2026-06-07 23:16:57 +03:00
75ea7c70c2 auth: implement cookie-based auth for HTML endpoints and Bearer token auth for API endpoints
Some checks failed
CI / lint (push) Failing after 15s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
Details:

  • HTML endpoints (/, /profile, /admin, /login, /register):

    - Authenticate via HTTP-only cookie named 'token'

    - Handlers in internal/templates/templates.go check cookie validity

    - /admin endpoint additionally checks for role='admin'

    - Unauthenticated users redirected to /login

    - Non-admin users accessing /admin get HTTP 403 Forbidden

  • API endpoints (/api/*):

    - Authenticate via Bearer token in Authorization header only

    - Handlers in internal/api/api.go use authenticateRequest() function

    - Function extracts token from 'Authorization: Bearer <token>' header

    - Validates token against yggdrasil_sessions table

    - No cookie checking for API endpoints (launcher compatibility)

  • Web login (/api/web/login):

    - Sets HTTP-only cookie 'token' for browser storage

    - Returns JSON with token, UUID, username for JS localStorage

    - Maintains backward compatibility with existing JavaScript

  • JavaScript in HTML pages:

    - Gets token from localStorage (set by login response)

    - Sets Authorization: Bearer <token> header for API fetch calls

    - Updated admin.html and profile.js to include token in headers

This separation ensures:

  • HTML endpoints work automatically with browser cookies

  • API endpoints work with browsers (via JS) and launchers (Bearer tokens)

  • Security sensitive actions require proper role validation

  • Clean separation of concerns between document and API interfaces
2026-06-07 23:11:51 +03:00
5bd8a549ca fix: require authentication and admin role for /admin endpoint
All checks were successful
CI / lint (push) Successful in 17s
CI / test (push) Successful in 42s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m10s
2026-06-07 21:16:23 +03:00
b9e986d25a feat: добавить веб-интерфейс админ-панели для управления модпаками
All checks were successful
CI / lint (push) Successful in 1m1s
CI / test (push) Successful in 42s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m16s
2026-06-07 19:06:27 +03:00
f765fecf24 fix: check skin_hash/cape_hash instead of skin/cape in profile JS
All checks were successful
CI / lint (push) Successful in 16s
CI / test (push) Successful in 41s
CI / build (push) Successful in 17s
CI / docker (push) Successful in 1m13s
2026-06-06 20:36:19 +03:00
f4f7a52749 fix: remove inline onerror from skin/cape images in template
All checks were successful
CI / lint (push) Successful in 16s
CI / docker (push) Successful in 1m10s
CI / test (push) Successful in 40s
CI / build (push) Successful in 17s
Inline onerror was firing on empty src, hiding images before JS could load them
2026-06-06 20:27:18 +03:00
b8c136878b fix: pass os/arch to launcher latest endpoint and improve skin/cape loading
All checks were successful
CI / lint (push) Successful in 16s
CI / test (push) Successful in 41s
CI / build (push) Successful in 17s
CI / docker (push) Successful in 1m7s
2026-06-06 20:08:37 +03:00
329c0d3fda fix: skin/cape image loading with error handling
All checks were successful
CI / lint (push) Successful in 17s
CI / test (push) Successful in 43s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m10s
2026-06-06 20:00:26 +03:00
6a8213a234 fix: use skin_hash and cape_hash for skin/cape URLs in profile page
All checks were successful
CI / lint (push) Successful in 17s
CI / test (push) Successful in 43s
CI / build (push) Successful in 17s
CI / docker (push) Successful in 1m17s
2026-06-06 19:44:58 +03:00
7ae0f44fd6 fix: skin and cape URLs in profile page to use /skins/ endpoint
All checks were successful
CI / lint (push) Successful in 1m0s
CI / test (push) Successful in 42s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m23s
2026-06-06 19:29:14 +03:00
3a148fabe2 fix: add gosu to chown skins directory and drop privileges
All checks were successful
CI / lint (push) Successful in 19s
CI / test (push) Successful in 19s
CI / build (push) Successful in 19s
CI / docker (push) Successful in 1m6s
- Install gosu in container to change ownership of mounted volume\n- Entrypoint now chowns /var/www/cdn/skins to appuser:appuser and runs server as non-root\n- This should resolve permission denied errors when creating skin subdirectories\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:47:33 +03:00
a143399643 fix: ensure upload directories can be created at runtime
All checks were successful
CI / lint (push) Successful in 19s
CI / test (push) Successful in 20s
CI / build (push) Successful in 19s
CI / docker (push) Successful in 1m9s
- Removed fatal directory creation from config.Load to allow server start even if volumes not prepped\n- Kept runtime directory creation in uploadSkin/uploadCape to create skin subdirectories on first upload\n- Confirmed docker-compose.yml includes cdn_skins volume for persistence\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:31:26 +03:00
1c69721b47 fix: ensure skins directory existence and update infrastructure
All checks were successful
CI / lint (push) Successful in 19s
CI / test (push) Successful in 19s
CI / build (push) Successful in 19s
CI / docker (push) Successful in 1m9s
- Create SKINS_DIR on startup to prevent upload errors\n- Add cdn_skins volume to docker-compose for persistence and Caddy serving\n- Update watchtower image to nickfedor/watchtower for Docker API compatibility\n\nCo-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 17:08:07 +03:00
d419d59fe3 fix: create skins directory on startup
All checks were successful
CI / lint (push) Successful in 59s
CI / test (push) Successful in 29s
CI / build (push) Successful in 19s
CI / docker (push) Successful in 1m16s
2026-06-05 16:37:13 +03:00
74ad023a36 fix: align form field names in skin/cape upload (file → skin/cape)
All checks were successful
CI / lint (push) Successful in 18s
CI / test (push) Successful in 20s
CI / build (push) Successful in 20s
CI / docker (push) Successful in 1m7s
JS sent fd.append('file', ...) but Go expected r.FormFile("skin") / r.FormFile("cape").
2026-06-04 18:33:05 +03:00
c22c860ec5 fix: set base.html as root template name and use ExecuteTemplate
All checks were successful
CI / lint (push) Successful in 19s
CI / test (push) Successful in 20s
CI / build (push) Successful in 20s
CI / docker (push) Successful in 1m11s
template.New("base.html") makes base.html the root template instead of ""
2026-06-04 17:30:46 +03:00
3f2fe0043a chore: gofmt
All checks were successful
CI / docker (push) Successful in 1m12s
CI / lint (push) Successful in 19s
CI / test (push) Successful in 20s
CI / build (push) Successful in 20s
2026-06-04 17:06:31 +03:00
21d48200f5 fix: parse templates individually to prevent content block overwrite
Some checks failed
CI / lint (push) Failing after 19s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
Root cause: ParseFS with wildcard html/*.html caused all {{define "content"}}

blocks to overwrite each other — last alphabetically (register.html) won for

all pages. Now each page is parsed separately with base.html as its own template.

Also fix footer link: GitHub → Gitea project page.
2026-06-04 16:57:34 +03:00
2f1f1ef7d6 fix: embed HTML templates into binary via go:embed
All checks were successful
CI / lint (push) Successful in 17s
CI / test (push) Successful in 18s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m1s
Switched from runtime file-based template loading (os.Stat + ParseGlob)
to compile-time embedding (embed.FS + ParseFS). Templates are now
bundled into the binary — no need to COPY them into Docker image.

This fixes the fallback response issue where h.loaded was always false
because templates weren't present in the container filesystem.
2026-06-04 06:51:11 +03:00
394003d8c0 fix: render base.html layout instead of bare page templates
All checks were successful
CI / lint (push) Successful in 48s
CI / test (push) Successful in 18s
CI / build (push) Successful in 17s
CI / docker (push) Successful in 1m10s
ExecuteTemplate now renders base.html which calls {{template "content" .}},
so the full layout (header, footer, styles) wraps each page.
2026-06-04 06:29:46 +03:00
008d9a129e feat: redesign website templates with full pages
All checks were successful
CI / lint (push) Successful in 18s
CI / test (push) Successful in 20s
CI / build (push) Successful in 19s
CI / docker (push) Successful in 1m7s
- Redesigned base.html: dark Minecraft theme, sticky header, responsive grid, cards, server cards with status indicators, profile styles
- index.html: hero section, server list grid, how-to-start steps, features section
- login.html: centered card layout, client-side validation, fetch API
- registration.html: password confirmation, pattern validation, error alerts
- profile.html: new page — skin/cape upload & delete, launcher download links, auth-gated via localStorage token
- templates.go: added /profile route, extended pageData with Username/UUID
2026-06-03 20:37:24 +03:00
e94cd4c23c fix: remove duplicate route for sessionProfile
All checks were successful
CI / lint (push) Successful in 1m3s
CI / test (push) Successful in 26s
CI / build (push) Successful in 20s
CI / docker (push) Successful in 1m22s
Remove conflicting 'GET /sessionserver/session/minecraft/profile/{unsigned}'
route that panicked due to identical pattern matching as '{uuid}'.
The 'unsigned' variant is now handled via ?unsigned=true query parameter.
2026-06-03 18:43:41 +03:00
3e4b97e262 fix: update builder image to golang:1.25-alpine
All checks were successful
CI / lint (push) Successful in 19s
CI / test (push) Successful in 19s
CI / build (push) Successful in 18s
CI / docker (push) Successful in 1m12s
go.mod requires Go 1.25+, but Dockerfile was still using golang:1.22-alpine
as the builder image. This caused CI build failure:
  "go.mod requires go >= 1.25.0 (running go 1.22.12)"
2026-06-01 17:20:51 +03:00
51e45d9327 fix: use PAT for Gitea Container Registry authentication
Some checks failed
CI / lint (push) Successful in 19s
CI / test (push) Successful in 18s
CI / build (push) Successful in 18s
CI / docker (push) Failing after 30s
Gitea's GITHUB_TOKEN is read-only for packages (issue #23642).
Switch to PACKAGES_TOKEN Personal Access Token with write:package scope.

Setup required:
1. Gitea → User Settings → Applications → Generate Token (read:package, write:package)
2. Repo → Settings → Actions → Secrets → PACKAGES_TOKEN
2026-06-01 17:16:47 +03:00
79ebed5b01 fix: repair CI/CD pipeline for Gitea Actions
Some checks failed
CI / lint (push) Successful in 1m57s
CI / test (push) Successful in 1m17s
CI / build (push) Successful in 21s
CI / docker (push) Failing after 2m27s
- Remove actions/upload-artifact@v4 (not supported on Gitea, GHESNotSupportedError)
- Add job permissions (packages: write, contents: read)
- Fix master branch condition (github.ref_name compatible with Gitea)
- Fix Docker login: use gitea.actor + GITHUB_TOKEN instead of reserved GITEA_ secrets
- Lowercase image tags per Docker spec (mrixs/mrixscraft-server)
- Sync docker-compose.yml image reference
2026-06-01 17:01:44 +03:00
3499963205 fix: add per-hash mutex to prevent concurrent CAS writes
Some checks failed
CI / lint (push) Successful in 11m11s
CI / test (push) Successful in 10m55s
CI / build (push) Failing after 5m36s
CI / docker (push) Has been skipped
StoreFile now uses a per-hash sync.Mutex to prevent race conditions
when multiple workers (launcher fetcher or parallel uploads) write
the same file simultaneously. Duplicate writes are idempotent —
if another goroutine stored the file while we waited, return the
existing hash without re-writing.
2026-06-01 06:51:54 +03:00
7b3b97c5f8 ci: add Go modules/build cache, fix master branch triggers
Some checks failed
CI / lint (push) Successful in 11m11s
CI / test (push) Successful in 11m1s
CI / build (push) Failing after 5m40s
CI / docker (push) Has been skipped
- Add actions/cache@v4 for GOMODCACHE and GOCACHE on lint/test/build
- Switch from setup-go built-in cache to explicit cache control
- Fix branch refs: main → master for PR, artifact, and docker jobs
- Use registry-based Docker layer caching (mode=max)
- Extract GO_VERSION env var for single-point version management
2026-05-30 20:49:23 +03:00
4efcc770ac fix: format all Go files with gofmt
All checks were successful
CI / lint (push) Successful in 9m54s
CI / test (push) Successful in 10m19s
CI / build (push) Successful in 9m58s
CI / docker (push) Has been skipped
- Fix alignment in struct fields (sessionProfileResponse, textureInfo, Handler)
- Align struct field values in internal/templates/templates.go, internal/api/api.go
2026-05-30 20:00:54 +03:00
8d1f956a8b fix: regenerate go.sum with go mod tidy
Some checks failed
CI / lint (push) Failing after 5m22s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
- go.sum was missing checksums for pgx/v5 and golang.org/x/crypto
- go.sum was accidentally gitignored; forcing add for CI reproducibility
- Consider un-ignoring go.sum if this repo is a library (per Go conventions)
2026-05-30 19:53:37 +03:00
69eb6ddd5f fix: add go mod download before go vet and tests
Some checks failed
CI / lint (push) Failing after 11s
CI / build (push) Has been skipped
CI / test (push) Has been skipped
CI / docker (push) Has been skipped
- Explicitly run go mod download so modules are available for go vet
- Remove cache: true (unnecessary now that modules are explicitly fetched)
2026-05-30 19:48:30 +03:00
08aaa7a3c8 fix: enable Go module cache in CI workflow
Some checks failed
CI / lint (push) Failing after 7s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / docker (push) Has been skipped
- Add cache: true to actions/setup-go@v5 so modules persist between steps
- Fixes "missing go.sum entry" error on go vet step
2026-05-30 19:45:52 +03:00
7ad02cb1b2 feat: implement email validation, CI/CD pipeline, migration history, and web templates
Some checks failed
CI / lint (push) Failing after 21s
CI / build (push) Has been skipped
CI / test (push) Has been skipped
CI / docker (push) Has been skipped
Email validation:
- Replace @/. check with net/mail.ParseAddress on register
- Add size limit check (max 254 chars, RFC 5321)

CI/CD Pipeline:
- Add .gitea/workflows/ci.yml (lint → test → build → docker push)
- Registry: gitea.mrixs.me/Mrixs/MrixsCraft-server
- Push only on main branch

Database:
- Add migrations/002_migration_history.sql (tracking applied migrations)
- Add migrations/README.md (manual apply instructions)

Web Templates:
- Add base.html with Minecraft-themed layout (dark + green accent)
- Add index.html, login.html, register.html with POST forms
- Rewrite templates.go for data-driven rendering with pageData struct
- Fallback placeholder preserved when templates dir missing
2026-05-30 00:39:51 +03:00
e1cc999ea8 refactor: deduplicate sha1Hex/writeJSON/writeError into pkg/utils
- admin.go: replace local sha1Hex, sha256Hex, writeJSON, writeError with pkg/utils equivalents
- auth.go: replace local writeJSON with utils.WriteJSON; rewrite writeError as thin wrapper
- cas.go: remove local sha1Hex and unused writeJSON; use utils.SHA1Bytes
- pkg/utils.go: add WriteJSON, WriteError; reorder imports
2026-05-29 23:53:33 +03:00
d418ae2b54 fix: add panic recovery, rate limiting, timing-safe CI token
- Add Recovery middleware (catches panics, returns 500, logs stack trace)
- Add RateLimiter to middleware chain (30 req/min, burst 60 per IP)
- Fix CI token comparison with subtle.ConstantTimeCompare (timing attack)
- Middleware chain: Recovery → Logging → RateLimit → CORS → mux

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-29 21:08:01 +03:00
5fba2e78d5 feat: add Docker infrastructure, migrations, CI/CD client, session cleanup, tests
Docker & Deployment:
- Add Dockerfile (multi-stage, alpine, non-root)
- Add docker-compose.yml (caddy, backend, postgres, watchtower)
- Add Caddyfile (TLS, file_server, reverse proxy)
- Add .env.example

Database:
- Add migrations/001_init.sql (all tables + indexes)

CI/CD:
- Add cmd/ci-release/main.go (launcher binary upload tool)

Session management:
- Add internal/session/cleanup.go (background expired session cleanup)
- Integrate cleanup worker into main.go

Bug fixes:
- Fix launcherLatest download URL to include version segment
- Fix serveLauncherAsset path to match route pattern
- Add Content-Type detection from file extension in CAS serveFile
- Add empty-field validation in webLogin
- Format string fix in ci-release (%d → %s for resp.Status)

Tests:
- Add internal/auth/auth_test.go (8 tests)
- Add internal/cas/cas_test.go (7 tests)
- Add internal/session/cleanup_test.go (1 test)
- Add internal/api/api_test.go (5 tests)
- All tests passing, go vet clean

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-05-29 20:09:00 +03:00
81c42e1a9a feat: migrate passwords from SHA-256 to bcrypt
- Replace SHA-256 hex hashing with bcrypt (cost 10) for password storage
- VerifyPassword now uses bcrypt.CompareHashAndPassword
- HashPassword returns (string, error) instead of string
- Add IsBcryptHash helper to detect legacy hashes for future migration
- Remove duplicate verifyPassword from api.go (already done in prev commit)
- Promote golang.org/x/crypto to direct dependency
2026-05-27 16:31:38 +03:00
01cce981c5 feat: implement skins/capes, profile endpoints, session server
Skins & capes:
- Fix uploadSkin auth: Bearer token instead of user_id form hack
- Add POST /api/web/profile/cape (upload cape)
- Add DELETE /api/web/profile/skin and DELETE /api/web/profile/cape
- Validate skin PNG dimensions (64x32, 64x64, 128x128, 128x64)
- Add size limits: 1 MB for skins, 2 MB for capes
- Add basic email validation on register

Profile & session server:
- Add GET /api/web/profile/{uuid} — public profile with skin/cape hashes
- Add GET /sessionserver/session/minecraft/profile/{uuid} — Mojang-compatible
  texture response for game client
- Add POST /authserver/invalidate and POST /authserver/signout
- Export VerifyPassword and ExtractBearer from auth package
- Remove duplicate verifyPassword from api.go
- Add PlayerTextures model to database.go
2026-05-27 11:45:33 +03:00
e4fea937aa feat: implement CAS module, middleware, utils, and templates
- CAS: GET /files/{hash} with immutable cache headers, launcher asset
  serving, hash validation, StoreFile/VerifyAndStore helpers
- Middleware: CORS, request logging, per-IP token bucket rate limiter
- Utils: SHA1Bytes, SHA256Bytes, SHA1File, Unzip with zip-slip protection
- Templates: placeholder handler with html/template discovery
- Wire CAS routes and middleware chain (Logging → CORS) in main.go
2026-05-26 15:11:41 +03:00
2f07fbf379 feat: add admin handler (modpack CRUD, file upload, manifest, launcher release)
- modpack CRUD: GET/POST/PUT/DELETE /api/admin/modpacks
- file upload: POST /api/admin/modpacks/{slug}/upload — multipart, ZIP extraction, CAS storage
- manifest: POST /api/admin/modpacks/{slug}/manifest — scan instance dir, generate manifest.json
- launcher release: POST /api/admin/launcher/release — CI/CD endpoint, SHA-256 verify, DB registration
- auth middleware: Bearer token + admin role check + X-CI-Token for CI/CD
- zip-slip protection in file extraction

Co-Authored-By: OWL <noreply@anthropic.com>
2026-05-26 14:03:17 +03:00
475ff9bfa2 feat: add API handler (register, login, skin, launcher, servers, manifest)
- register: POST /api/web/register — create user with SHA-256 password hash
- login: POST /api/web/login — credentials check + session token
- uploadSkin: POST /api/web/profile/skin — PNG upload, SHA-1 CAS storage
- launcherLatest: GET /api/launcher/latest — latest launcher version + download URL
- serversList: GET /api/servers.json — active modpacks list
- instanceManifest: GET /api/instances/{slug}/manifest.json — modpack manifest
- serveSkin: GET /skins/{hash}.png — skin file serving with cache headers
- PathValue-based routing (Go 1.22+)

Co-Authored-By: OWL <noreply@anthropic.com>
2026-05-26 13:31:22 +03:00
d205320e0e refactor: export GenerateToken for use by api package 2026-05-26 13:30:39 +03:00
aa7d3a8509 feat: add server foundation (config, database, auth, main)
- config: Load from env vars (SERVER_PORT, DATABASE_URL, JWT_SECRET, CAS_DIR, etc.)
- database: pgx/v5 connection pool, models (User, YggdrasilSession, Modpack, GlobalFile, LauncherRelease)
- auth: Yggdrasil endpoints (authenticate, refresh, validate) with SHA-256 password hashing, token rotation
- main: graceful shutdown, HTTP server on configured port
- go.mod: module gitea.mrixs.me/Mrixs/MrixsCraft-server, pgx/v5 dependency

Co-Authored-By: OWL <noreply@anthropic.com>
2026-05-26 13:03:21 +03:00
551c75a232 chore: initial project structure 2026-05-23 17:57:37 +03:00