- Go 70%
- Vue 18.3%
- TypeScript 10.2%
- Shell 0.9%
- Makefile 0.2%
- Other 0.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Sicherheit/Bugs (Backend):
- Passwortwechsel widerruft jetzt alle Refresh-Sessions des Kontos
- Refresh-Flow prüft Kontosperre (gesperrte Accounts behalten keinen Zugang)
- Rate-Limiting für /auth (Login 10/min, Register 5/min, Refresh 60/min)
- CORS: ohne konfigurierte Origins kein Allow-All mehr (fail-closed)
- Katalogfilter ?type= wird jetzt in der DB angewandt (war wirkungslos)
- PATCH /me überschreibt nicht mehr ungesendete Felder mit Leerstrings
- Admin-Guthabenkorrektur schreibt adjustment-Transaktion ins Ledger
- Admins können sich nicht selbst degradieren (Lockout-Schutz)
- PDF-Download: Pfad-Check gegen konfiguriertes Verzeichnis, Quantifier-Fix
- Pagination geclampt (Overflow → negativer OFFSET vermieden)
Frontend:
- Doppeltes Unwrapping des {data,meta}-Envelops behoben: Listenansichten
(Produkte, Orders, Tickets, ...) waren dadurch immer leer
- vue-i18n mit DE/EN/ES, Sprachumschalter in Header & Sidebar,
Persistenz via localStorage, Auto-Erkennung der Browsersprache
- Produktformular: Typ-Auswahl (Gameserver/VPS/App), Driver-Dropdown aus
den konfigurierten Treibern (/meta), Zyklus-Checkboxen, Starter-
Templates pro Treiber für das JSON-Schema, Stock/Sort/Active-Felder
- Passwortwechsel meldet sich sauber ab (Session wird serverseitig beendet)
|
||
| backend | ||
| frontend | ||
| scripts | ||
| .env.example | ||
| .gitignore | ||
| docker-compose.yml | ||
| Makefile | ||
| README.md | ||
Hostplate
Open-source modular hosting platform — game servers, VPS & managed apps.
Hostplate is a modular, highly configurable hosting platform for game servers, VPS and managed apps — Go backend + Vue 3 frontend. Every external system (virtualization, panel, payments, mail) is a swappable driver behind an interface: the core has zero hardcoded providers or billing logic.
Module path: The Go module lives at
git.dietzlabs.net/Tronax/hostplate/backend(private Forgejo). Forking or self-hosting? Adapt it with a single search-and-replace of that path acrossbackend/.
Private module usage
Because the module is private, tell the Go toolchain to skip the public proxy and checksum database, and authenticate against Forgejo:
go env -w GOPRIVATE=git.dietzlabs.net/*
# HTTPS (credentials via ~/.netrc):
# machine git.dietzlabs.net login <user> password <token>
# SSH (recommended):
git config --global url."git@git.dietzlabs.net:".insteadOf "https://git.dietzlabs.net/"
Architecture
backend/
├── cmd/
│ ├── server/ HTTP API entrypoint (Gin)
│ └── worker/ asynq worker + periodic billing scheduler
├── internal/
│ ├── config/ viper-based config (YAML + HP_* env overrides)
│ ├── core/
│ │ ├── models/ typed domain entities (GORM mapping only)
│ │ ├── ports/ repository + queue interfaces (hexagonal)
│ │ └── apperr/ typed domain errors → structured JSON
│ ├── repository/ GORM adapters implementing the ports
│ ├── services/ business logic (auth, catalog, orders, billing, …)
│ ├── handlers/ HTTP layer (validation, structured responses)
│ ├── middleware/ JWT auth, RBAC, error handler, request log
│ ├── drivers/ concrete drivers:
│ │ ├── pterodactyl/ game servers (Application + Client API)
│ │ ├── proxmox/ KVM clones from templates, Cloud-Init
│ │ ├── hetzner/ Hetzner Cloud (hcloud-go)
│ │ ├── dockerportainer/ managed app stacks (Portainer/Docker)
│ │ ├── stripe/ hosted Checkout + signed webhooks
│ │ ├── paypal/ Orders v2 + webhook signature verification
│ │ └── wallet/ integrated prepaid balance gateway
│ ├── tasks/ asynq queue, worker handlers, cron scheduler
│ ├── bootstrap/ the only place that wires concrete drivers
│ └── database/ GORM + embedded golang-migrate migrations
└── pkg/
├── provisioner/ THE lifecycle interface + driver registry
├── payment/ THE gateway interface + registry
├── invoice/ maroto/v2 PDF rendering
├── mail/ smtp/log mailer drivers
├── httpx/ shared JSON REST client for drivers
└── schema/ JSON-Schema subset validator (configurator)
frontend/ Vue 3 + TypeScript + Pinia + Tailwind CSS 4
├── storefront catalog, dynamic configurator, checkout
├── customer dashboard services, power control, billing, tickets
└── admin center products, customers, orders, system logs
Core interfaces
Provisioning (pkg/provisioner/provisioner.go) — full lifecycle:
Create / Start / Stop / Restart / Suspend / Unsuspend / Terminate / GetStatus / GetMetrics. Products name their driver in
products.provisioner_driver; static driver parameters live in the
product's config_schema under the reserved key "driver", customer
selections in services.config_data.
Payments (pkg/payment/gateway.go): GetID / CreatePaymentIntent / HandleWebhook / Refund. Webhooks are stored with a unique
(gateway, event_id) pair before processing → duplicate deliveries
are no-ops (idempotent at-least-once).
Billing model
Dual mode, toggled per product and platform-wide via features.*:
- Prepaid wallet — deposits via Stripe/PayPal credit the balance;
orders/renewals charge it atomically (
users.wallet_balanceguarded against overdraft, every movement recorded intransactions). - Subscription — gateway recurring payments extend service expiry via
subscription.paidwebhooks.
A periodic billing run (asynq cron) renews prepaid services, suspends expired ones after the grace period and terminates long-suspended ones.
Tax / invoicing
Configuration-driven, three modes:
billing.small_business.enabled→ no tax line, prints the configurable § 19 UStG notice (Kleinunternehmerregelung).- EU reverse charge for B2B customers with VAT ID and foreign country.
- Regular taxation with
billing.default_tax_rate.
Every paid order/renewal automatically produces a numbered PDF invoice
(maroto/v2) stored under invoice.pdf_dir.
Internationalization
The web interface ships in English, German and Spanish. The language
switcher lives in the public header and the dashboard sidebar; the choice
is persisted in localStorage (hp_locale) and first-time visitors get
their browser language when available. Translations live in
frontend/src/i18n/locales/ — adding a language means adding one catalog
file and registering it in frontend/src/i18n/index.ts.
Quick start (Docker)
cp .env.example .env # set HP_AUTH_JWT_SECRET (openssl rand -hex 32)
docker compose up -d --build
# → storefront http://localhost:5173 (proxies /api to :8080)
# → login with auth.initial_admin credentials
Local development
# infrastructure only
docker compose up -d postgres redis
# backend (needs config: cp backend/config.example.yaml backend/config.yaml)
make api # HTTP API on :8080
make worker # job worker + billing scheduler
# frontend
cd frontend && pnpm install && pnpm dev # Vite on :5173 (proxies /api)
Migrations run automatically on startup (database.automigrate); the SQL
lives in backend/internal/database/sql/ (golang-migrate format).
Product configuration example
A game server product's config_schema shows how driver params and the
storefront configurator combine:
{
"type": "object",
"required": ["location"],
"properties": {
"location": {
"type": "string",
"title": "Location",
"enum": ["fsn1", "nbg1", "hel1"]
},
"memory": {
"type": "integer",
"title": "Memory (MB)",
"minimum": 1024, "maximum": 16384, "default": 2048
},
"driver": {
"egg": 5,
"docker_image": "ghcr.io/pterodactyl/yolks:java_21",
"startup": "java -Xms128M -XX:MaxRAMPercentage=95.0 -jar {{.SERVER_JARFILE}}",
"environment": { "SERVER_JARFILE": "server.jar" }
}
}
}
Customer-visible fields are validated server-side (pkg/schema) at order
time; the driver object is passed straight to the provisioner.
API
REST under /api/v1 — all responses are {"data": …} or
{"error": {"code", "message"}}. Highlights:
| Area | Endpoints |
|---|---|
| Auth | POST /auth/register·login·refresh·logout, JWT access (15 min) + rotating refresh tokens with reuse detection |
| Catalog | GET /products, GET /products/:slug |
| Orders | POST /orders, POST /orders/:id/pay, GET /orders |
| Services | GET /services, POST /services/:id/power, GET /services/:id/refresh |
| Wallet | GET /wallet, POST /wallet/deposits, GET /wallet/transactions |
| Invoices | GET /invoices, GET /invoices/:id/download (PDF) |
| Tickets | POST/GET /tickets, POST /tickets/:id/messages |
| Webhooks | POST /webhooks/payments/:gateway |
| Admin | /admin/{stats,users,products,orders,services,logs} (RBAC: admin/support) |
Status & roadmap
Implemented: all five mission phases — core scaffolding, auth/RBAC, driver interfaces, all seven drivers, dual billing with idempotent webhooks, invoicing with tax toggles, Vue 3 storefront/dashboard/admin.
Potential next steps:
- WebSocket live console per driver (Pterodactyl client WS proxy)
- 2FA login flow (secret column already in schema)
- Stripe Billing native subscriptions for gateway-side recurring charges
- Per-node resource scheduling / cluster status page