Manager (Backend API)
The vgate-manager/ component is the control plane: a Go service (Gin + GORM) that owns users, nodes, plans, orders, traffic, and system config — including per-user and per-node speed caps — and exposes both the human-facing REST API and the node data plane.
Source: github.com/vgate-project/vgate-manager
Build & run
cd vgate-manager
go build -o vgate-manager .
./vgate-manager --config config.yml # defaults to ./config.yml; HTTP on server.port (8081)Other commands (cobra subcommands):
vgate-manager admin create --username X --password Y --role admin|super_adminGORM AutoMigrate runs automatically on startup (idempotent; the cmd/migrate.go data-migration hook is currently empty). On first start the DB is auto-migrated and an initial admin is bootstrapped from admin.bootstrap in config.yml (default username admin; the bundled docker-compose.yml sets the password to change-me).
Test & lint
go test ./...
go vet ./... && gofmt -l .Layered structure (internal/)
api/— HTTP layer:router.go(Gin engine + middleware wiring),handler/(one file per resource:admin_node.go,admin_user.go,server.go,user.go,order.go,plan.go, …),dto/(request/response structs). Tests:admin_test.go,server_test.go,sub_test.go, auth-level tests.middleware/—cors.go,jwt.go(user/admin auth),node_auth.go(node token auth),logger.go,ratelimit.go.service/— business logic: auth, user, node, plan, order/billing, traffic, stats, subscription, system_config, email/invite, announcement, telegram, ticket. Many have_test.go.model/— GORM models.util/— helpers.wire/— dependency wiring.pkg/crypto/— shared crypto (VLESS credential helpers).
Startup sequence (run())
- Load config (viper).
- Open DB (SQLite default via
glebarez/sqlite, or Postgres viadb.dialect/db.dsn). AutoMigrateall GORM models.- Run the data-migration hook (
cmd/migrate.go, currently empty). - Merge DB-backed system-config overrides on top of
config.yml. - Bootstrap the admin.
- Build router via
api.NewRouter. - Start background tickers: expired-order closer (5 min), hourly-stats pruning (startup + every 24 h), quota reset (startup + every 24 h), and an hourly traffic-reminder scanner (
reminderSvc.CheckAndSend). Hourly traffic stats are recorded as nodes report it, not by a scheduler.
API surface (all under /api/v1)
| Group | Endpoints |
|---|---|
| Public / user | /user/login, /user/register, /user/verify-email, /user/resend-verification, /user/dashboard, /sub/:sub_token, profile / subscribe / plans / nodes / traffic, orders (+ /:id/pay, /:id/close, /:id/apple-verify), /user/payment-methods, /user/balance, /user/change-plan (+ /change-plan/preview), invites, redemption codes, reminder-channel, support tickets, self-service Telegram link, /billing/:platform/notify (alipay, wechat, stripe, paypal, apple) |
| Node data plane | GET /server/config, GET /server/users, POST /server/traffic |
| Admin | nodes (incl. /:id/users, /:id/regenerate-token), users (incl. /:id/balance read/adjust, /:id/nodes), traffic, stats, system-config, orders, plans, traffic-packages, reference, payment-methods, full admins[/:id] CRUD, invites, redemption codes, announcements, email, tickets, Telegram broadcast/admin-link, zombie-user preview/cleanup |
| Health | GET /health |
Auth
- JWT. Admin login returns an access token plus a refresh token, and a
401on a protected endpoint triggers one automatic silent refresh; user login returns only an access token (users get no refresh token — a401means re-login). - Roles:
adminvssuper_admingate plan/admins endpoints. - Node endpoints use a separate node token (
node_authmiddleware).
Telegram & notifications
The manager can run a Telegram bot that delivers alerts and announcements and lets users and admins bind their personal accounts for ticket notifications. It is enabled and configured via DB-backed system config (TelegramConfig):
| Key | Default | Meaning |
|---|---|---|
telegram.enabled | false | Master switch for the bot. |
telegram.bot_token | "" | BotFather token (secret). |
telegram.bot_username | "" | Bot @username, used to build /start deep links. |
telegram.user_bot_enabled | false | Allow users to self-bind via deep link. |
telegram.alert_ticket | false | Notify linked admins on new tickets / user replies. |
telegram.alert_announcement | false | Forward announcements to linked users. |
telegram.alert_order_paid | false | Notify on paid orders (and other alert toggles). |
telegram.alert_new_registration | false | Notify linked admins on new user registrations. |
telegram.alert_node_up | false | Notify linked admins when a node comes online. |
telegram.alert_node_down | false | Notify linked admins when a node goes offline. |
telegram.alert_traffic_exceeded | false | Notify linked admins when a user exceeds their traffic quota. |
Binding uses a /start <code> deep link. The code carries a u_ (user) or a_ (admin) prefix so the bot routes the bind to the right account: admins link from Settings → Telegram in the admin console, users from Settings in the portal.
When an admin replies to a ticket, the owner is notified on the channel they chose when opening it (none / email / telegram). Every admin with a linked Telegram account also receives an alert on each new ticket and user reply.
Support tickets
Tickets are a lightweight support channel between users and admins.
- Users open tickets (
POST /user/tickets), reply, and can close their own ticket (POST /user/tickets/:id/close). When opening one they pick a notification method (notify_method:none|email|telegram); if omitted it defaults totelegramwhen their account is Telegram-linked, elsenone. - Admins list/view all tickets, reply (
POST /admin/tickets/:id/messages), and move them through a status machineopen → in_progress → resolved → closed(PUT /admin/tickets/:id/status). A later user reply reopens a closed ticket.
Admins can also broadcast a message to every linked Telegram user via POST /admin/telegram/broadcast (optionally also published as an announcement).
Config split
| File / env only | DB-backed (hot-reloadable) |
|---|---|
server.port | jwt TTLs, log.level / log.format |
db.* | cors.allowed_origins, server.read/write_timeout_secs |
jwt.secret | quota.reset_day, password.*, registration (user.register_enabled, user.register_require_invite, user.register_require_email_verify, user.register_email_suffix_whitelist) & invite settings, site.* / sub.base_urls, email.*, captcha.*, telegram.*, payment gateway creds (alipay.*, wechat.*, stripe.*, paypal.*, apple.*), reminder.* |
admin.bootstrap.* | (used only on first run) |
The DB-backed set is broad — see Configuration Reference for the full list of hot-reloadable keys. viper maps SERVER_PORT-style env vars.
Database
- SQLite by default (
glebarez/sqlite) — zero external dependencies, great for small deployments. - Postgres via
db.dialect: postgresanddb.dsn.
Production notes
- Set
jwt.secretand a strongadmin.bootstrap.password(Docker Compose defaults tochange-me). - Put the manager behind a reverse proxy (nginx/Caddy) for TLS and to serve the frontends.
- Enable
ratelimitmiddleware for public endpoints.