core

package
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 26, 2026 License: AGPL-3.0 Imports: 56 Imported by: 0

Documentation

Overview

Package core ties the store and the Xray supervisor together: mutations go through it so the proxy config is reconciled from the DB after every change.

Index

Constants

View Source
const (
	RestartPending = "pending" // asked, waiting for the node to prove it happened
	RestartDone    = "done"    // the node reported a genuinely restarted Xray
	RestartTimeout = "timeout" // waited long enough and never got proof
)

What a node's restart request currently reads as, for the UI.

Variables

View Source
var ErrBroadcastNotFound = errors.New("рассылка не найдена")

ErrBroadcastNotFound lets the API answer 404 instead of leaking a raw SQL error as a 500.

Functions

func EnsureHostHops added in v1.5.0

func EnsureHostHops(st *store.Store) error

EnsureHostHops re-installs this host's nftables funnels: the built-in Hysteria2 lane's range plus every custom Hysteria2 inbound that asks for hopping.

It must be the ONLY thing that writes those rules. The table is dropped and recreated on every apply, so anything installing a subset erases the rest — which is exactly what boot used to do to the custom funnels, leaving them missing until an unrelated edit happened to re-apply them.

Package-level and store-driven rather than a Manager method, because boot needs it before the Manager exists.

func EventPageLimit added in v0.13.0

func EventPageLimit(n int) int

EventPageLimit is the effective page size for a requested limit. The HTTP layer applies it too, so its "was this page full?" cursor check compares against the same number the store was actually asked for.

func HostConnGuardPorts added in v1.5.0

func HostConnGuardPorts(st *store.Store) ([]int, error)

HostConnGuardPorts is the set of public TCP ports the per-IP flood guard should protect on this host: the built-in lanes plus every enabled custom inbound that listens on TCP.

Custom inbounds belong here for the same reason they belong in the node's list — they are public listeners on the same box, and leaving them out would quietly make "add a custom inbound" the way to bypass the guard. Hysteria2 is excluded: the guard counts connections, which QUIC has none of.

func NormalizeACMEHost added in v0.5.0

func NormalizeACMEHost(target string) string

NormalizeACMEHost lowercases domain targets for ACME (CAs canonicalize DNS names; mixed case like WaifuVPN.example.com makes lego reject the order).

Types

type ConnInfo

type ConnInfo struct {
	Key string `json:"key"`
	// Name is the default protocol label (shown as the input placeholder).
	Name string `json:"name"`
	// DisplayName is the admin-configured custom node name (empty ⇒ use Name).
	DisplayName string `json:"display_name"`
	Transport   string `json:"transport"`
	Security    string `json:"security"`
	Port        string `json:"port"`
	Note        string `json:"note"`
	Enabled     bool   `json:"enabled"`
	// Fingerprint is the protocol's uTLS fingerprint (empty for Hysteria2, which
	// has no uTLS — the UI then shows no fingerprint control for it).
	Fingerprint string `json:"fingerprint"`
}

ConnInfo describes one client-facing protocol endpoint. Key is the stable identifier used to toggle the protocol; Enabled reports whether it currently appears in user subscriptions.

type ConnectionsStatus

type ConnectionsStatus struct {
	Host      string     `json:"host"`
	SNI       string     `json:"sni"`
	Protocols []ConnInfo `json:"protocols"`
	// Hysteria2 base port, hop range, and rotation interval (UDP port-hopping).
	HysteriaPort int    `json:"hysteria_port"`
	HopStart     int    `json:"hop_start"`
	HopEnd       int    `json:"hop_end"`
	HopInterval  string `json:"hop_interval"`
	// VLESS + XHTTP + REALITY parameters (public key / shortId / path are generated
	// by the panel and shown read-only for reference).
	RealityPort       int    `json:"reality_port"`
	RealityDest       string `json:"reality_dest"`
	RealityPublicKey  string `json:"reality_public_key"`
	RealityShortID    string `json:"reality_short_id"`
	RealityPath       string `json:"reality_path"`
	RealityAntiReplay bool   `json:"reality_anti_replay"` // REALITY maxTimeDiff > 0

	// Anti-DPI transport hardening (cross-protocol).
	TLSFragment bool `json:"tls_fragment"` // sing-box ClientHello fragmentation
	TLSMin13    bool `json:"tls_min13"`    // require TLS 1.3 on :443
	BlockQUIC   bool `json:"block_quic"`   // drop untunneled browser QUIC in client configs
}

ConnectionsStatus is the public connection surface: where clients connect and how, per protocol.

type ConnectionsUpdate

type ConnectionsUpdate struct {
	Protocols         map[string]bool   `json:"protocols"`    // key → enabled
	Fingerprints      map[string]string `json:"fingerprints"` // key → uTLS fingerprint
	Names             map[string]string `json:"names"`        // key → custom node name ("" = default)
	HysteriaPort      int               `json:"hysteria_port"`
	HopStart          int               `json:"hop_start"`
	HopEnd            int               `json:"hop_end"`
	HopInterval       string            `json:"hop_interval"`
	RealityPort       int               `json:"reality_port"`
	RealityDest       string            `json:"reality_dest"`
	RealityAntiReplay bool              `json:"reality_anti_replay"`
	// RegenRealityKeys requests a fresh REALITY keypair / shortId / service name.
	RegenRealityKeys bool `json:"regen_reality_keys"`

	// Anti-DPI transport hardening (cross-protocol).
	TLSFragment bool `json:"tls_fragment"`
	TLSMin13    bool `json:"tls_min13"`
	BlockQUIC   bool `json:"block_quic"`
}

ConnectionsUpdate is the full client-facing connection configuration applied in one shot: protocol toggles, link fingerprints, display names, the REALITY donor/ port, and the Hysteria2 base port / hop range / rotation interval.

type GroupInboundOpt added in v1.5.0

type GroupInboundOpt struct {
	ID      int64  `json:"id"`
	Name    string `json:"name"`
	Token   string `json:"token"`
	Enabled bool   `json:"enabled"`
}

GroupInboundOpt is one custom inbound as a grantable item.

type GroupLaneOpt added in v1.5.0

type GroupLaneOpt struct {
	Lane    string `json:"lane"`  // vless | reality | hysteria2
	Label   string `json:"label"` // display name (custom or default)
	Token   string `json:"token"`
	Enabled bool   `json:"enabled"`
}

GroupLaneOpt is one built-in lane as a grantable item.

type GroupTarget added in v1.5.0

type GroupTarget struct {
	ServerID   int64             `json:"server_id"`
	ServerName string            `json:"server_name"`
	Lanes      []GroupLaneOpt    `json:"lanes"`
	Inbounds   []GroupInboundOpt `json:"inbounds"`
}

GroupTarget is one server's grantable connections, for the group editor: its built-in lanes and its custom inbounds, each with the token a grant would store.

type HealthCheck added in v0.7.0

type HealthCheck struct {
	Key    string `json:"key"`
	Label  string `json:"label"`
	Status string `json:"status"` // ok | warn | error | info
	Detail string `json:"detail"`
	Hint   string `json:"hint,omitempty"` // shown when the check isn't ok
}

HealthCheck is one diagnostic line shown on the Health page.

type HealthReport added in v0.7.0

type HealthReport struct {
	Status string        `json:"status"`
	Checks []HealthCheck `json:"checks"`
}

HealthReport aggregates the per-component checks plus the worst overall status.

type InboundView added in v1.5.0

type InboundView struct {
	model.Inbound
	// Unsupported names the subscription formats that will silently skip this
	// inbound (see model.Inbound.UnsupportedFormats). Shown as a warning, not an
	// error: those clients simply won't see this lane, everything else still works.
	Unsupported []string `json:"unsupported"`
	// RealityPublicKey / RealityShortID are the public halves clients need. The
	// private key never leaves the panel and is not part of this view.
	RealityPublicKey string `json:"reality_public_key,omitempty"`
	RealityShortID   string `json:"reality_short_id,omitempty"`

	// The advanced blobs, taken apart into the same typed forms the editor submits, so
	// the UI binds fields directly instead of parsing JSON. The raw json.RawMessage
	// versions on the embedded Inbound.Opts are cleared below so the view carries one
	// representation, not two.
	XHTTPExtraForm model.XHTTPExtraForm `json:"xhttp_extra_form"`
	SockoptForm    model.SockoptForm    `json:"sockopt_form"`
	TLSExtraForm   model.TLSExtraForm   `json:"tls_extra_form"`
}

InboundView is one custom inbound as the UI sees it: the stored record plus the facts the editor needs but shouldn't have to derive — which subscription formats can't carry this combination, and the REALITY public material a client needs.

type Manager

type Manager struct {
	// contains filtered or unexported fields
}

Manager is the application service layer.

func New

func New(st *store.Store, sup *xray.Supervisor, opts xray.Options, tls TLSPaths, operaDir string) *Manager

New builds a Manager. opts carries non-DB generation parameters (e.g. the panel's loopback fallback dest); tls carries the managed cert paths; operaDir is where the opera-proxy helper binary is downloaded/run from.

func (*Manager) AbuseConfig added in v1.3.0

func (m *Manager) AbuseConfig() (enabled bool, categories map[string]bool, custom string, alertMin int)

AbuseConfig returns the current blocklist config for the settings UI.

func (*Manager) AbuseStatus added in v1.3.0

func (m *Manager) AbuseStatus() []abuse.FileInfo

AbuseStatus exposes the loaded feeds for the settings UI.

func (*Manager) ActivePaidPlan added in v0.11.0

func (m *Manager) ActivePaidPlan(u model.User) *model.TariffPlan

ActivePaidPlan returns the user's current tariff when it's a paid plan that is still active (expiry in the future), else nil. This is the "locked" state where only renewal or cancellation is allowed — not switching to another plan. A trial or free plan (price 0) never counts, so upgrading from those stays open.

func (*Manager) AddAdminAudit added in v0.14.0

func (m *Manager) AddAdminAudit(ev model.AdminAudit)

AddAdminAudit records one event.

func (*Manager) AdminAudit added in v0.14.0

func (m *Manager) AdminAudit(f store.AdminAuditFilter) ([]model.AdminAudit, error)

AdminAudit returns the admin trail, newest first.

func (*Manager) AdminEventPrefs added in v0.7.0

func (m *Manager) AdminEventPrefs() map[string]bool

AdminEventPrefs returns the per-category on/off map for the settings UI.

func (*Manager) AppLogTail

func (m *Manager) AppLogTail() []string

AppLogTail returns the buffered recent panel log lines (for the dashboard).

func (*Manager) ApplyConnections

func (m *Manager) ApplyConnections(u ConnectionsUpdate) error

ApplyConnections validates and persists the whole connection surface, then does a SINGLE reconcile (and one nft hop refresh) — so a multi-field save restarts Xray at most once instead of once per field. The reconcile is a no-op for the Xray process when only link-level fields (fingerprint, interval) changed.

func (*Manager) ApplyNodeConnections added in v1.0.0

func (m *Manager) ApplyNodeConnections(id int64, u ConnectionsUpdate) error

ApplyNodeConnections applies a full connections update to a node: its protocols, REALITY donor/keys, and transport (ports, hop, WS, anti-replay, fingerprints, names, anti-DPI) — all the node's OWN. Validation is syntactic; the node's local `xray -test` is the backstop, and port-free / donor-live checks need the node's own host, which the panel can't reach.

func (*Manager) ApplyPlanToUser added in v0.4.0

func (m *Manager) ApplyPlanToUser(ctx context.Context, userID int64, planID int64, extendFromCurrent bool) error

ApplyPlanToUser assigns a tariff and updates limits. extendFromCurrent stacks paid time. planID 0 switches to manual mode: clears plan link and resets limits to unlimited.

func (*Manager) ApplyRouting

func (m *Manager) ApplyRouting(cfg model.RoutingConfig, warpEnabled, operaEnabled bool, operaCountry string) error

ApplyRouting persists the routing config plus the WARP/Opera on/off state in one shot, then reconciles once. The first WARP enable provisions a free WARP account (Cloudflare device registration) and caches the WireGuard credentials; later toggles reuse them. Enabling Opera downloads + launches the helper for the chosen region.

func (*Manager) ApproveRegistrationRequest added in v1.0.0

func (m *Manager) ApproveRegistrationRequest(ctx context.Context, reqID int64) error

ApproveRegistrationRequest turns a pending request into a real (active) user: it creates the account, links the applicant's chat, drops the request and notifies them. The request is claimed atomically first, so concurrent approvals (or an approve racing a reject) resolve to a single winner — no duplicate account.

func (*Manager) AudiencePreview added in v1.2.0

func (m *Manager) AudiencePreview(audience string) (int, error)

AudiencePreview reports how many recipients an audience currently resolves to, so the operator sees the size before launching rather than after.

It normalises and validates exactly as CreateBroadcast does. Without that, an unrecognised audience resolved to an empty list and previewed as "0 получателей" — while the launch itself would either refuse it or, for an empty string, fall back to everyone. A preview that disagrees with the send is worse than no preview.

func (*Manager) AuditTelegramLinked added in v0.13.0

func (m *Manager) AuditTelegramLinked(ctx context.Context, id int64, username string)

AuditTelegramLinked records that a user bound their Telegram account (the bind itself happens in the user bot, which owns the one-time code).

func (*Manager) BackupManifest

func (m *Manager) BackupManifest() backup.Manifest

BackupManifest returns a Manifest describing the current server state for inclusion in backup archives.

func (*Manager) BulkUserAction added in v0.7.0

func (m *Manager) BulkUserAction(ctx context.Context, ids []int64, action string, days int) (int, error)

BulkUserAction applies one action to many users with a SINGLE config sync at the end (instead of one per user), returning how many users were actually affected. Actions: "enable", "disable", "delete", "reset" (traffic), "extend" (push expiry by `days`). "extend" only touches users that already have an expiry — unlimited users are left alone (extending "never" is meaningless) and not counted.

func (*Manager) CancelPayment added in v0.4.0

func (m *Manager) CancelPayment(ctx context.Context, orderID int64) error
func (m *Manager) CancelTelegramLink() error

CancelTelegramLink clears the pending one-time link code (cancels a link request).

func (*Manager) CancelUserPlan added in v0.11.0

func (m *Manager) CancelUserPlan(ctx context.Context, userID int64) error

CancelUserPlan cancels a paid subscription immediately: the user is moved to the free plan right away (losing any remaining paid time), matching what EnforceBilling does on expiry — but on demand. With no free plan configured, access is ended instead (plan cleared, expired now). The consumed-trial flag is preserved so cancelling can't reopen a fresh trial.

func (*Manager) CertPinSHA256

func (m *Manager) CertPinSHA256() string

CertPinSHA256 returns the hex SHA-256 of the active leaf certificate, for clients to pin via pinnedPeerCertSha256 when the cert isn't CA-trusted. "" if unavailable.

func (*Manager) ChangeAdminPassword

func (m *Manager) ChangeAdminPassword(adminID int64, newPassword string) error

ChangeAdminPassword hashes and stores a new password for the given admin and lifts that admin's forced-password-change gate (a password the admin picked themselves is exactly what the gate is waiting for).

func (*Manager) ConfirmNodeXrayRestart added in v1.4.0

func (m *Manager) ConfirmNodeXrayRestart(id int64, reportedStart int64)

ConfirmNodeXrayRestart clears a pending restart once the node reports an Xray that started at a different time than the one running when the command went out — the node's own proof that the process really came back.

A node that reports no start time at all (an agent older than this field) can never confirm; its request simply times out, which reads as "we asked, we can't tell" rather than a false "готово".

func (*Manager) ConfirmPayment added in v0.4.0

func (m *Manager) ConfirmPayment(ctx context.Context, orderID int64) error

ConfirmPayment marks an order paid and applies the plan. Idempotent: the atomic pending→paid claim means a double-submit / retry (or an overlap with the provider webhook on a provider order) applies the plan at most once.

func (*Manager) Connections

func (m *Manager) Connections(id int64) ([]model.Connection, error)

Connections returns a user's recent source IPs.

func (*Manager) ConnectionsInfo

func (m *Manager) ConnectionsInfo() (*ConnectionsStatus, error)

ConnectionsInfo reports the master's enabled protocols and their connection parameters, derived from settings.

func (*Manager) CreateAdmin added in v0.14.0

func (m *Manager) CreateAdmin(username, password, role string) (model.Admin, error)

CreateAdmin adds an account with a role and a password the owner picked. The account is gated on a password change at first login: a password chosen by someone else and delivered over a chat window is a bootstrap credential, not a permanent one.

func (*Manager) CreateBroadcast added in v1.2.0

func (m *Manager) CreateBroadcast(ctx context.Context, b *model.Broadcast) (*model.Broadcast, error)

CreateBroadcast validates a composed broadcast, resolves its audience to a fixed recipient list, and starts it. The audience is snapshotted here and never recomputed: a run that re-evaluated itself would pick up people who arrived halfway and give a progress total that moves under the operator's feet.

func (*Manager) CreateGroup added in v1.5.0

func (m *Manager) CreateGroup(name string, grants []string) (*model.Group, error)

CreateGroup validates and stores a new group.

func (*Manager) CreateInbound added in v1.5.0

func (m *Manager) CreateInbound(ctx context.Context, in model.Inbound) (*InboundView, error)

CreateInbound validates and stores a new custom inbound, generating REALITY key material when the combination needs it.

func (*Manager) CreateNode added in v1.0.0

func (m *Manager) CreateNode(name, host string) (*model.Node, error)

CreateNode registers a node with a random decoy and a one-time join token, ensuring the node-API surface exists. The returned node carries RawJoinToken. The name must be unique (it becomes a subscription proxy name/tag).

func (*Manager) CreateRegisteredUser added in v0.4.0

func (m *Manager) CreateRegisteredUser(ctx context.Context, name string) (*model.User, error)

CreateRegisteredUser creates an active user from self-registration (trial/free/ plain per billing config), links nothing itself, and alerts the admin chats. Used by the open and invite modes; moderation instead goes through RequestRegistration.

func (*Manager) CreateUser

func (m *Manager) CreateUser(ctx context.Context, name string, dataLimit, expireAt int64) (*model.User, error)

CreateUser creates a user (one credential set for all protocols) with optional data limit (bytes, 0=unlimited) and expiry (unix, 0=never), then reconciles.

func (*Manager) DeleteAdmin added in v0.14.0

func (m *Manager) DeleteAdmin(actorID, targetID int64) error

DeleteAdmin removes an account. Deleting it revokes its sessions too (the admin_sessions rows cascade), so a colleague who is let go loses the panel on their next request, not when their cookie happens to expire.

func (*Manager) DeleteGroup added in v1.5.0

func (m *Manager) DeleteGroup(id int64) error

DeleteGroup removes a group; its members lose that grant.

func (*Manager) DeleteInbound added in v1.5.0

func (m *Manager) DeleteInbound(id int64) error

DeleteInbound removes an inbound; its users lose that lane on the next apply.

func (*Manager) DeleteNode added in v1.0.0

func (m *Manager) DeleteNode(id int64) error

DeleteNode removes a node and wakes any held poll so it learns it's revoked.

A node that is CONNECTED when deleted is almost always parked in its held poll, so wakeOne makes it return, find its row gone, and be told revoked (see handleNodeSync). A node that is OFFLINE at delete time and reconnects later gets only the decoy (its token row is gone), which the agent reads as "panel unreachable" and keeps serving the last config. Closing that residual window needs a tombstone (keep the token briefly, answer revoked) — deferred to the node-agent PR, where it first becomes reachable. Until then, disabling a node (which keeps the token and answers revoked) is the reliable "stop now" control.

func (*Manager) DeleteTariffPlan added in v0.4.0

func (m *Manager) DeleteTariffPlan(id int64) error

func (*Manager) DeleteUser

func (m *Manager) DeleteUser(ctx context.Context, id int64) error

DeleteUser removes a user and reconciles.

func (*Manager) EmitWebhook added in v0.10.0

func (m *Manager) EmitWebhook(event string, data any)

EmitWebhook fans an event out to all subscribed endpoints. It builds the signed body once per endpoint (each has its own secret) and enqueues the deliveries. Best-effort and non-blocking: any error is logged, never surfaced to the caller.

func (*Manager) EnforceBilling added in v0.4.0

func (m *Manager) EnforceBilling(now int64) error

EnforceBilling downgrades users whose paid/trial period ended to the free plan. It runs off the background poller, so its audit rows are attributed to the system.

func (*Manager) EnsureNodeAPIPath added in v1.0.0

func (m *Manager) EnsureNodeAPIPath() error

EnsureNodeAPIPath generates the node-API URL segment the first time a node is created, then swaps it live into the router via the registered callback. It is serialized so two nodes created concurrently can't each mint a different path (which would leave the router and the DB disagreeing on the segment).

func (*Manager) Events added in v0.13.0

func (m *Manager) Events(f store.UserEventFilter) ([]model.UserEvent, error)

Events returns the global audit trail, newest first (the journal page).

func (*Manager) FetchRoutingTemplate

func (m *Manager) FetchRoutingTemplate(url string) (string, error)

FetchRoutingTemplate returns the body of a routing-template URL WITHOUT ever blocking the caller on a slow remote when a cached copy exists: a fresh entry is returned as-is, a stale one is returned immediately while a refresh runs in the background (stale-while-revalidate). Only a completely cold cache fetches synchronously — and then with a short budget. This is what keeps the Happ/INCY subscription pull from timing out when GitHub is slow: previously a cold/stale cache forced an inline 8s GET, so the whole subscription response hung.

func (*Manager) FinishSetup

func (m *Manager) FinishSetup() error

FinishSetup marks the first-run wizard as completed.

func (*Manager) FlushAbuse added in v1.3.0

func (m *Manager) FlushAbuse()

FlushAbuse writes the buffered matches in one transaction and alerts on users who crossed the threshold. Driven by the same loop as FlushAccess.

func (*Manager) FlushAccess added in v1.2.0

func (m *Manager) FlushAccess()

FlushAccess writes the buffered access sightings in one transaction and, if the new devices changed who should be online, syncs Xray.

The device-cap re-check used to run per sighting — a full WorkingUsers query for every user+IP every 10s. It belongs here: it only has to happen when something was actually recorded, and once per batch answers the same question.

func (*Manager) GenerateTelegramLinkCode added in v0.2.0

func (m *Manager) GenerateTelegramLinkCode() (string, error)

GenerateTelegramLinkCode issues a fresh one-time linking code and persists it. The operator sends "/start <code>" to the bot once to authorize their chat; the bot burns the code on use. Refused when the bot is disabled — it isn't polling then, so the code could never be redeemed.

func (*Manager) GenerateUserTgLinkCode added in v0.4.0

func (m *Manager) GenerateUserTgLinkCode(userID int64) (string, error)

GenerateUserTgLinkCode issues a fresh one-time code for binding a VPN user to the public Telegram bot. It replaces the old sub-token deep link, so a leaked link expires (TelegramLinkCodeTTL) and can't be reused after binding.

func (*Manager) GeoCategories

func (m *Manager) GeoCategories() (geosite, geoip []string, err error)

GeoCategories returns the geosite + geoip category codes from the on-disk databases, parsed once and cached (the .dat files only change on refresh).

func (*Manager) GeoGroups added in v1.1.0

func (m *Manager) GeoGroups() (geo.GroupSet, error)

GeoGroups returns the iplist groups parsed from the on-disk databases, cached like GeoCategories (they only change on a refresh). Callers must not mutate the returned set.

func (*Manager) GeoRefreshHours added in v1.0.0

func (m *Manager) GeoRefreshHours() int

GeoRefreshHours returns the configured geo auto-refresh cadence (hours; 0 ⇒ off).

func (*Manager) GeoStatus

func (m *Manager) GeoStatus() []geo.FileInfo

GeoStatus reports the on-disk state of the Xray geo databases (presence, size, last-download time) for the settings UI.

func (*Manager) GetBroadcast added in v1.2.0

func (m *Manager) GetBroadcast(id int64) (*model.Broadcast, error)

GetBroadcast returns one broadcast with its progress.

func (*Manager) GetNode added in v1.0.0

func (m *Manager) GetNode(id int64) (*model.Node, error)

GetNode returns one node, or (nil, nil) if absent.

func (*Manager) GroupTargets added in v1.5.0

func (m *Manager) GroupTargets() ([]GroupTarget, error)

GroupTargets lists every server (master + nodes) with the connections a group can grant. Disabled lanes/inbounds are included too, so a grant survives a temporary disable and the operator can pre-grant.

func (*Manager) Groups added in v1.5.0

func (m *Manager) Groups() ([]model.Group, error)

Groups returns every group with its grants and member count, for the management UI.

func (*Manager) GroupsForAllUsers added in v1.5.0

func (m *Manager) GroupsForAllUsers() (map[int64][]model.GroupRef, error)

func (*Manager) GroupsForUser added in v1.5.0

func (m *Manager) GroupsForUser(userID int64) ([]model.GroupRef, error)

GroupsForUser / GroupsForAllUsers expose membership for the user views.

func (*Manager) HandleProviderWebhook added in v1.0.0

func (m *Manager) HandleProviderWebhook(key string, body []byte, h http.Header) error

HandleProviderWebhook processes a callback from provider key. The provider's client authenticates it (signature, or a re-fetch over the API for providers that sign nothing) and reports what it says about the payment; nothing here trusts the POST body. A callback for a provider that is off or unconfigured is refused — otherwise a stale/forged callback could still move an order.

func (*Manager) HasValidCert

func (m *Manager) HasValidCert() bool

HasValidCert reports whether a non-expired, CA-issued certificate is present. A self-signed fallback cert is deliberately treated as "not valid" so the renew loop stays in its fast-retry cadence until a real ACME cert is obtained.

func (*Manager) Health added in v0.7.0

func (m *Manager) Health() *HealthReport

Health gathers the panel's self-diagnostics: the Xray process, last config apply, TLS certificate, disk/RAM headroom, geo databases, and any enabled helper egress lane. Every signal is read from memory/disk — no extra network calls — so the page is cheap to poll.

func (*Manager) IPListRefreshHours added in v1.1.0

func (m *Manager) IPListRefreshHours() int

IPListRefreshHours returns the configured iplist auto-refresh cadence (hours; 0 ⇒ off).

func (*Manager) IPListStatus added in v1.1.0

func (m *Manager) IPListStatus() []geo.FileInfo

IPListStatus reports the on-disk state of the iplist databases. Separate from GeoStatus because they are a separate concern with their own panel tab: Xray reads the geo .dat files, while the iplist lists are the panel's own source for "iplist:" rules.

func (*Manager) Inbounds added in v1.5.0

func (m *Manager) Inbounds(serverID int64) ([]InboundView, error)

Inbounds returns one server's custom inbounds for the UI (LocalNodeID = master).

func (*Manager) IngestNodeSync added in v1.0.0

func (m *Manager) IngestNodeSync(n *model.Node, req nodeapi.SyncRequest) (*nodeapi.SyncResponse, error)

IngestNodeSync records a node's reported status, ingests its traffic deltas idempotently, and computes the response (whether the node's applied hash still matches desired state). It does NOT block for the long-poll — the handler owns the hold; this is the pure state transition.

func (*Manager) IssueJoinToken added in v1.0.0

func (m *Manager) IssueJoinToken(id int64) (string, error)

IssueJoinToken issues a fresh install token WITHOUT revoking the node's current permanent token — for SSH re-provisioning, so a failed install can't down a live node.

func (*Manager) ListAdmins added in v0.14.0

func (m *Manager) ListAdmins() ([]model.Admin, error)

ListAdmins returns the admin roster.

func (*Manager) ListBroadcasts added in v1.2.0

func (m *Manager) ListBroadcasts(limit int) ([]model.Broadcast, error)

ListBroadcasts returns the most recent broadcasts with their progress.

func (*Manager) ListNodes added in v1.0.0

func (m *Manager) ListNodes() ([]model.Node, error)

ListNodes returns all configured nodes.

func (*Manager) ListPaymentOrders added in v0.4.0

func (m *Manager) ListPaymentOrders(status string) ([]model.PaymentOrder, error)

func (*Manager) ListRegistrationRequests added in v1.0.0

func (m *Manager) ListRegistrationRequests() ([]model.RegistrationRequest, error)

ListRegistrationRequests returns the pending moderated signups (for the panel).

func (*Manager) ListSupportGroups added in v1.2.0

func (m *Manager) ListSupportGroups() ([]model.SupportGroup, error)

ListSupportGroups returns the groups the support bot has been added to, for the settings picker. They are options only — see the store for why none is ever applied on its own.

func (*Manager) ListTariffPlans added in v0.4.0

func (m *Manager) ListTariffPlans(includeDisabled bool) ([]model.TariffPlan, error)

ListTariffPlans returns tariff plans for admin UI.

func (*Manager) ListUsers added in v0.2.0

func (m *Manager) ListUsers() ([]model.User, error)

ListUsers returns all users, newest first (read-only; used by the Telegram bot).

func (*Manager) Location

func (m *Manager) Location() *time.Location

Location exposes the operator timezone for handlers that compute date ranges.

func (*Manager) MigratePlanUsers added in v0.11.0

func (m *Manager) MigratePlanUsers(ctx context.Context, fromPlanID, toPlanID int64) (int, error)

MigratePlanUsers moves every user currently on fromPlanID to toPlanID (applying the target plan's limits and period). Used when retiring a plan. Returns how many users were moved.

func (*Manager) NodeConfigCheck added in v1.5.0

func (m *Manager) NodeConfigCheck(nodeID int64) *nodeapi.ConfigCheckRequest

NodeConfigCheck returns the config a node should validate on this response.

func (*Manager) NodeConnectionsInfo added in v1.0.0

func (m *Manager) NodeConnectionsInfo(id int64) (*ConnectionsStatus, error)

NodeConnectionsInfo reports a node's effective connection status (its own transport where set, else the master's), for the per-node connections editor.

func (*Manager) NodeDesiredState added in v1.0.0

func (m *Manager) NodeDesiredState(n *model.Node) (*nodeapi.NodeState, error)

NodeDesiredState builds the full desired state for a node: its Xray config (generated panel-side from nodeSettings + the working user set), the host-level meta the agent needs, and a hash over both so the sync handler can skip no-ops.

func (*Manager) NodeGeoFiles added in v1.0.0

func (m *Manager) NodeGeoFiles(id int64) []nodeapi.GeoFile

NodeGeoFiles returns a node's last-reported geo database status (nil if it hasn't reported yet).

func (*Manager) NodeHasFreshWork added in v1.5.0

func (m *Manager) NodeHasFreshWork(nodeID int64) bool

NodeHasFreshWork reports whether this node has an unsent probe or config check waiting — the signal the sync handler uses to answer at once instead of holding.

func (*Manager) NodeHealth added in v1.4.0

func (m *Manager) NodeHealth(id int64) (*HealthReport, error)

NodeHealth is the diagnostics for one server, so the Nodes page can show the same report next to every card. Node 0 is the panel's own server and reuses the full local report (Xray, config, TLS, disk/RAM, geo, connguard, BBR).

A remote node is diagnosed entirely from what it last reported — the panel never dials it — so the report is honest about staleness instead of pretending to probe: everything is qualified by "как сообщила нода" through the connection check, which leads and says how fresh that picture is.

func (*Manager) NodeHostStats added in v1.4.0

func (m *Manager) NodeHostStats(id int64) (nodeapi.HostStats, bool)

NodeHostStats returns a node's last-reported machine state (ok=false when the node hasn't reported one — an agent older than this feature never will).

func (*Manager) NodeLinkSettings added in v1.0.0

func (m *Manager) NodeLinkSettings() ([]*model.Settings, error)

NodeLinkSettings returns per-node settings clones for share-link/subscription generation: one for each enabled node that has connected at least once (so links point at a live server with a known cert), each carrying its NodeLabel and TLS hints. The local server is NOT included — the caller prepends it (with its own TLS hints applied by the server layer). Returns nil when there are no such nodes, so a single-server install produces byte-identical output.

func (*Manager) NodeProbePorts added in v1.5.0

func (m *Manager) NodeProbePorts(nodeID int64) []nodeapi.PortProbe

NodeProbePorts returns the port probes a node should run, for the sync handler to attach to its response.

Nil-guarded because the package's tests build partial Managers by design; a missing registry means "nothing pending", never a panic on the node sync path.

func (*Manager) NodeRestartState added in v1.4.0

func (m *Manager) NodeRestartState(id int64) string

NodeRestartState is what this node's restart request currently reads as: RestartPending while waiting, then RestartDone or RestartTimeout for a short window, then "" (nothing to say). Records past their window are dropped here, so the state can never be left stuck on a stale answer.

The timeout is decided lazily, on read, rather than by a timer: nothing else in the panel needs to know, and a request nobody is looking at costs nothing to let sit until someone asks.

func (*Manager) NodeTLSStatus added in v1.0.0

func (m *Manager) NodeTLSStatus(id int64) (*TLSStatus, error)

NodeTLSStatus reports a node's effective TLS/ACME status for its Домен tab: its address, its own (or inherited) ACME provider/email, and its cert metadata (built from what the node last reported). Mirrors the master's TLSStatus.

func (*Manager) NodeTrafficBreakdown added in v1.2.0

func (m *Manager) NodeTrafficBreakdown(userID int64, from, to string) ([]NodeTraffic, error)

NodeTrafficBreakdown splits a period's traffic by the server that carried it, busiest first. userID 0 covers everyone, matching StatsSeries.

Names are resolved here rather than left to the client: the caller would otherwise need the node list too, and an operator without rights to see nodes still gets the breakdown this way. A deleted node keeps its traffic rows (they carry the numeric id), so it is named rather than dropped — its bytes were real.

func (*Manager) NodeViews added in v1.0.0

func (m *Manager) NodeViews() ([]NodeView, error)

NodeViews returns the local server (node 0) followed by every remote node, each with resolved protocols and today's traffic, for the Nodes UI.

func (*Manager) NodeWakeChan added in v1.0.0

func (m *Manager) NodeWakeChan(nodeID int64) <-chan struct{}

NodeWakeChan exposes a node's wake channel to the sync handler.

func (*Manager) NodeXrayConfig added in v1.4.0

func (m *Manager) NodeXrayConfig(id int64) ([]byte, error)

NodeXrayConfig returns one server's Xray config for the read-only viewer: the master's live on-disk config.json for node 0, and for a remote node the config the panel generates and pushes (the same bytes the node applies).

The node's copy differs in one respect: the cert/key paths are still the panel's sentinels here, because only the agent knows its own data dir — it substitutes them before handing the config to Xray. The viewer says so.

func (*Manager) NoteNodeCertError added in v1.5.0

func (m *Manager) NoteNodeCertError(nodeID int64, msg string)

NoteNodeCertError records the TLS error a node reported on its sync (empty ⇒ its certificate is fine). Only the state is written here — the alert is raised by the sweep, which owns the throttle and the rest of the node's alert state.

func (*Manager) OperaHealthy

func (m *Manager) OperaHealthy() bool

OperaHealthy reports the last probe result for the Opera lane.

func (*Manager) OperaRunning

func (m *Manager) OperaRunning() bool

OperaRunning reports whether the opera-proxy helper is currently up.

func (*Manager) PaymentMethods added in v0.6.0

func (m *Manager) PaymentMethods() []string

PaymentMethods returns the enabled, fully-configured provider keys, in registry order (which is the order the bot and the subscription page offer them in).

func (*Manager) PaymentProviders added in v1.0.0

func (m *Manager) PaymentProviders() ([]payments.Descriptor, map[string]model.PaymentProvider, error)

PaymentProviders returns every provider in the registry paired with its saved setup (a provider the operator never configured comes back with an empty config).

func (*Manager) PaymentStats added in v0.10.0

func (m *Manager) PaymentStats() (*model.PaymentStats, error)

PaymentStats assembles the revenue dashboard: all-time and per-provider paid totals, revenue since local midnight / the 1st of the month, and the pending backlog. Day/month boundaries use the operator's configured timezone.

func (*Manager) PaymentWebhookSecret added in v0.6.0

func (m *Manager) PaymentWebhookSecret() string

PaymentWebhookSecret returns the random webhook URL segment (may be empty).

func (*Manager) PaymentWebhookURL added in v1.0.0

func (m *Manager) PaymentWebhookURL(key string) string

PaymentWebhookURL is the public callback URL to paste into a provider's dashboard: /<random secret>/<provider key>. Empty when the panel doesn't know its own host yet, or before the secret has been generated.

func (*Manager) PlanName added in v0.4.0

func (m *Manager) PlanName(planID int64) string

func (*Manager) PollPendingPayments added in v0.6.0

func (m *Manager) PollPendingPayments()

PollPendingPayments is the fallback for missed webhooks: it queries each pending provider order's status and confirms/cancels accordingly. Providers with no status endpoint (ErrNoStatusAPI) are left to their webhook — for those, a missed callback is only ever resolved by the abandoned sweep or by hand.

func (*Manager) PollStats

func (m *Manager) PollStats() error

PollStats reads per-user traffic from Xray, accumulates lifetime totals (handling counter resets on Xray restart), and enforces quotas/expiry.

func (*Manager) ProbeNodePort added in v1.5.0

func (m *Manager) ProbeNodePort(ctx context.Context, nodeID int64, network string, port int) (bool, error)

ProbeNodePort asks a node whether a port is free and waits for the answer.

Returns errProbeUnavailable when the node doesn't answer in time — offline, or running an agent that predates this request. The caller treats that as "couldn't check" and lets the save through: refusing to configure a temporarily unreachable server would be a worse failure than the one being guarded against, and the node still validates and rolls back a config its Xray can't start.

func (*Manager) ProviderLabel added in v1.0.0

func (m *Manager) ProviderLabel(key string) string

ProviderLabel is the pay-button label for a provider key: the operator's custom name if they set one, otherwise the provider's default. "" ⇒ a manual order.

func (*Manager) ProxyCount

func (m *Manager) ProxyCount() int

ProxyCount reports how many proxies are currently live across all lanes.

func (*Manager) ProxyCounts added in v0.14.0

func (m *Manager) ProxyCounts() map[string]int

ProxyCounts reports how many proxies each lane currently has, keyed by lane ID (a lane with none is absent). Feeds the per-lane status badges in the panel.

func (*Manager) PurgeDeletedNodes added in v1.0.0

func (m *Manager) PurgeDeletedNodes()

PurgeDeletedNodes reclaims tombstoned node rows past the grace window.

func (*Manager) PurgeExpiredUsers added in v0.15.0

func (m *Manager) PurgeExpiredUsers()

PurgeExpiredUsers deletes users whose expiry date is further in the past than the configured grace period. Called from the retention sweep; a no-op when the setting is 0 (the default), so an operator who never opts in never loses a user.

Deletion is irreversible, so it is deliberately conservative:

  • it keys off expire_at, not the derived `status` (see store.ExpiredUsersBefore) — a user whose plan was renewed has a future expiry and is never a candidate;
  • users with no expiry date at all are never touched;
  • every deletion is written to the user journal (which outlives the user) and pushed as a webhook, so «где мой пользователь?» always has an answer.

func (*Manager) PurgeOldAbuse added in v1.3.0

func (m *Manager) PurgeOldAbuse()

PurgeOldAbuse drops matches past the retention window.

func (*Manager) PurgeOldAdminAudit added in v0.14.0

func (m *Manager) PurgeOldAdminAudit()

PurgeOldAdminAudit drops trail rows past the retention window. Called from the same slow timer as the user journal's sweep.

func (*Manager) PurgeOldConnections added in v0.13.0

func (m *Manager) PurgeOldConnections()

PurgeOldConnections drops connection rows whose IP hasn't been seen inside the retention window. Shares the audit sweep's slow timer; safe to call repeatedly.

func (*Manager) PurgeOldEvents added in v0.13.0

func (m *Manager) PurgeOldEvents()

PurgeOldEvents drops audit rows past the retention window. Called on a slow timer from the service loop; safe to call as often as you like.

func (*Manager) PurgeOldTraffic added in v1.2.0

func (m *Manager) PurgeOldTraffic()

PurgeOldTraffic drops per-day traffic history past the retention window. Shares the journals' slow timer; safe to call as often as you like. The cutoff is a calendar day in the operator's timezone rather than a duration, because that is the calendar the rows were written on — deriving it any other way would shift the boundary by a day for anyone east or west of UTC.

func (*Manager) RecentAbuse added in v1.3.0

func (m *Manager) RecentAbuse(limit int) ([]store.AbuseMatch, error)

RecentAbuse returns the fleet's recent matches for the panel's view.

func (*Manager) Reconcile

func (m *Manager) Reconcile() error

Reconcile regenerates the Xray config from current DB state and applies it. Failures are recorded in settings.last_config_error and returned. It serializes with the live user-sync via applyMu.

func (*Manager) RecordAccess

func (m *Manager) RecordAccess(email, ip, dest string)

RecordAccess notes a connection from an Xray access-log line (email "uN" + source IP, and the destination host when the line carried a usable one). Throttled to one recorded sighting per user+IP per 10s to absorb bursts, then buffered — FlushAccess writes them.

This is called from the access-log reader for every line Xray emits, so it does no I/O at all: it takes a lock, updates two maps, and returns.

func (*Manager) RecordNodeAbuse added in v1.3.0

func (m *Manager) RecordNodeAbuse(nodeID, userID int64, dest string, count int64) bool

RecordNodeAbuse folds in a match a node reported, attributed to that node, and reports whether it matched — the caller bounds how many a single sync may add.

Node abuse detection is weaker than the master's by design: the node ships only each user's busiest destinations (a truncated top-N), so a low-volume callback by a heavy browser can be trimmed out before the panel ever sees it — the exact low-volume case the master's unthrottled path catches. That is the cost of not shipping the feed to every node; the master's own traffic is matched in full.

func (*Manager) RecordNodeConfigCheck added in v1.5.0

func (m *Manager) RecordNodeConfigCheck(nodeID int64, res *nodeapi.ConfigCheckResult)

RecordNodeConfigCheck delivers a node's verdict, called from the sync handler.

func (*Manager) RecordNodeProbeResults added in v1.5.0

func (m *Manager) RecordNodeProbeResults(nodeID int64, results []nodeapi.PortProbeResult)

RecordNodeProbeResults delivers a node's probe answers, called from the sync handler when the node reports them.

func (*Manager) RefreshAbuse added in v1.3.0

func (m *Manager) RefreshAbuse()

RefreshAbuse forces an immediate re-download of the enabled feeds, in the background so the operator's request returns promptly.

func (*Manager) RefreshGeo

func (m *Manager) RefreshGeo() ([]geo.FileInfo, error)

RefreshGeo re-downloads the Xray geo databases to their latest version, drops the parsed caches, and reloads Xray so routing rules pick up the new data.

func (*Manager) RefreshIPLists added in v1.1.0

func (m *Manager) RefreshIPLists() ([]geo.FileInfo, error)

RefreshIPLists re-downloads the iplist databases, drops the parsed caches and reloads Xray, so a changed group takes effect at once.

func (*Manager) RefreshNodeProxies added in v1.0.0

func (m *Manager) RefreshNodeProxies()

RefreshNodeProxies re-resolves every enabled node's own lane proxies and wakes any node whose pool changed so it re-pulls a config with the fresh endpoints. Runs on the same timer as RefreshProxies. Nodes with no lanes (disabled, or lanes removed) have their cache dropped and are woken so their config loses the stale endpoints.

func (*Manager) RefreshProxies

func (m *Manager) RefreshProxies()

RefreshProxies reloads the pool from current settings and reconciles if it changed. Runs on a timer and right after the routing config is saved.

func (*Manager) RegenInboundReality added in v1.5.0

func (m *Manager) RegenInboundReality(id int64) (*InboundView, error)

RegenInboundReality mints fresh REALITY material for one inbound. Existing clients must re-import their links afterwards, so it is its own action rather than something an edit does silently.

func (*Manager) RegenJoinToken added in v1.0.0

func (m *Manager) RegenJoinToken(id int64) (string, error)

RegenJoinToken issues a fresh install token for an existing node.

func (*Manager) RegenerateSecretPath

func (m *Manager) RegenerateSecretPath() (string, error)

RegenerateSecretPath issues a fresh random panel path and persists it. The caller is responsible for swapping the live router. Returns the new path.

func (*Manager) RegistrationPending added in v1.0.0

func (m *Manager) RegistrationPending(chatID int64) bool

RegistrationPending reports whether a chat has a signup awaiting a decision.

func (*Manager) RejectRegistrationRequest added in v1.0.0

func (m *Manager) RejectRegistrationRequest(ctx context.Context, reqID int64) error

RejectRegistrationRequest declines a pending request: it's dropped and the applicant is told. No user was ever created. Claimed atomically so it can't race an approval into a contradictory outcome.

func (*Manager) RenameUser

func (m *Manager) RenameUser(ctx context.Context, id int64, name string) error

RenameUser updates a user's display name. The name is cosmetic (Xray keys users by "u<id>"), so no config reload is needed.

func (*Manager) RenewTLSIfNeeded

func (m *Manager) RenewTLSIfNeeded() (bool, error)

RenewTLSIfNeeded renews an ACME cert when near expiry. It reports whether the certificate actually changed (so the caller can reload Xray only then).

func (*Manager) RequestAllNodesUpdate added in v1.0.0

func (m *Manager) RequestAllNodesUpdate() (int, error)

RequestAllNodesUpdate flags every enabled, connected node to self-update.

func (*Manager) RequestNodeGeoRefresh added in v1.0.0

func (m *Manager) RequestNodeGeoRefresh(id int64) error

RequestNodeGeoRefresh flags a node to re-download its geo databases on its next sync and wakes it so it happens promptly.

func (*Manager) RequestNodeLogs added in v1.0.0

func (m *Manager) RequestNodeLogs(id int64) ([]string, int64)

RequestNodeLogs marks that an operator is viewing a node's logs and wakes it, so the node includes its log tail on the next sync. Returns the currently-stored tail (may be from a previous fetch) for an immediate render.

func (*Manager) RequestNodeUpdate added in v1.0.0

func (m *Manager) RequestNodeUpdate(id int64) error

RequestNodeUpdate flags a node to self-update on its next sync, and wakes it so it happens promptly. Returns an error if the node doesn't exist.

func (*Manager) RequestNodeXrayRestart added in v1.4.0

func (m *Manager) RequestNodeXrayRestart(id int64) error

RequestNodeXrayRestart flags a node to bounce its Xray on the next sync and wakes it so it happens promptly. The panel can't reach into a node's process — this is the node-side twin of the master's own Xray restart button.

The request stays on record after it is sent, until the node's next sync proves Xray actually bounced (see ConfirmNodeXrayRestart). That is the whole point: the panel cannot restart a node, only ask, and an answer that always reads "готово" the instant it is asked is not an answer.

func (*Manager) RequestPlanPayment added in v0.4.0

func (m *Manager) RequestPlanPayment(ctx context.Context, userID, planID int64) (*model.PaymentOrder, string, error)

RequestPlanPayment opens a pending manual order for a paid plan and returns the payment instructions. To keep a spammed "Pay" button from piling up duplicate orders (and admin pings), it reuses the user's latest still-pending manual order for the same plan instead of creating another.

func (*Manager) RequestRegistration added in v1.0.0

func (m *Manager) RequestRegistration(ctx context.Context, chatID int64, name string) (ok bool, err error)

RequestRegistration records a moderated signup: no user is created — the request is held for an admin decision. Returns ok=false when the chat already has a pending request (the caller then tells the applicant it's still under review). The admin is prompted with approve/reject buttons (or a plain alert when the admin bot is off).

func (*Manager) ResetAdminPassword added in v0.14.0

func (m *Manager) ResetAdminPassword(actorID, targetID int64, password string) error

ResetAdminPassword assigns a new password to another admin — for when a colleague is locked out. Like a freshly created account it is gated on a change at first login, and every session that account had is revoked: whoever was using the old password is out.

func (*Manager) ResetStats

func (m *Manager) ResetStats() error

ResetStats clears the entire per-day traffic history.

func (*Manager) ResetTraffic

func (m *Manager) ResetTraffic(ctx context.Context, id int64) error

ResetTraffic zeroes a user's usage and re-enables them. The raw counters are re-baselined to the live Xray value so the next stats poll doesn't re-add the user's whole lifetime total back (see store.ResetTraffic).

func (*Manager) RestartXray added in v0.13.0

func (m *Manager) RestartXray() error

RestartXray restarts the Xray child from the config already on disk. Operator- initiated only: it drops every live VPN connection.

func (*Manager) RetryBroadcast added in v1.2.0

func (m *Manager) RetryBroadcast(id int64) (int, error)

RetryBroadcast re-queues the recipients that failed for a transient reason. Blocked chats are left alone — Telegram will refuse them again identically.

A cancelled run is refused outright. Cancelling does not clear the recipients that were still queued, so resuming one would not send "the failures again" — it would send the whole remainder of a message the operator has already stopped, from a button labelled as a retry of a handful.

func (*Manager) RotateSubToken added in v0.3.0

func (m *Manager) RotateSubToken(ctx context.Context, id int64) (*model.User, error)

RotateSubToken issues a new subscription URL token for a user. Protocol credentials stay the same — only the public /<sub_path>/<token> link changes, so the old URL stops working immediately. Triggers a user sync so any token-derived state is refreshed.

func (*Manager) SaveAdminEventPrefs added in v0.7.0

func (m *Manager) SaveAdminEventPrefs(prefs map[string]bool) error

SaveAdminEventPrefs persists the admin notification categories from the UI map. A key absent from the map (or false) disables that category.

func (*Manager) SaveBillingSettings added in v0.4.0

func (m *Manager) SaveBillingSettings(st *model.Settings) error

func (*Manager) SaveLocalBackup added in v0.13.0

func (m *Manager) SaveLocalBackup(expr string, keep int) error

SaveLocalBackup validates and persists the scheduled local-backup config: a 5-field cron expression in the operator timezone (empty = scheduling off) and how many archives to retain.

The cron is validated here rather than at fire time on purpose. The scheduler can only log an unparseable expression and skip, which looks exactly like "no backups were due" — so a typo would silently mean no backups at all until the day someone needed one.

func (*Manager) SavePaymentProvider added in v1.0.0

func (m *Manager) SavePaymentProvider(key string, enabled bool, cfg map[string]string) error

SavePaymentProvider validates and persists one provider's setup. A secret field left empty means "keep the stored value", so toggling a provider or editing its shop id never wipes its API key. Enabling a provider with a required field still missing is refused — a half-configured provider would just fail at checkout.

func (*Manager) SaveSubSettings

func (m *Manager) SaveSubSettings(st *model.Settings) error

SaveSubSettings validates and persists the subscription delivery settings. The subscription path must be URL-safe and must not shadow the secret panel path or any reserved panel/system segment.

func (*Manager) SaveTariffPlan added in v0.4.0

func (m *Manager) SaveTariffPlan(p *model.TariffPlan) error

func (*Manager) SaveTelegram added in v0.2.0

func (m *Manager) SaveTelegram(enabled bool, token, backupCron string) error

SaveTelegram validates and persists the Telegram bot configuration: the enable flag, bot token, and backup schedule as a 5-field cron expression (empty = no scheduled backups). The authorized chat set and the pending link code are managed separately (linking happens in the bot / via GenerateTelegramLinkCode).

func (*Manager) SaveTelegramConfig added in v1.2.0

func (m *Manager) SaveTelegramConfig(c TelegramConfig) error

SaveTelegramConfig validates all three bots BEFORE persisting any of them.

Saved one after another, a failure on the third left the first two committed while the caller reported failure — so the operator saw an error, the panel had changed, and (because the audit middleware skips a failed request) nothing recorded it. The third is the one that can fail for reasons outside anyone's control: it needs the support bot's @username, which comes from Telegram.

func (*Manager) SaveTelegramSupport added in v1.2.0

func (m *Manager) SaveTelegramSupport(enabled bool, token, username string, groupID int64, greeting string) error

SaveTelegramSupport validates and persists the support relay: its own bot token, the forum supergroup admins answer in, and the /start greeting. username is the bot's resolved @username — the caller looks it up (core deliberately doesn't talk to Telegram) and enabling without one is refused, because the user bot renders its support button only for a non-empty username and the operator would be left with support switched on and no visible way in.

func (*Manager) SaveTelegramUserBot added in v0.3.0

func (m *Manager) SaveTelegramUserBot(enabled bool, token, regMode, regCode string) error

SaveTelegramUserBot validates and persists the public user bot configuration: the enable flag, its (separate) bot token, the self-registration mode and (for the invite mode) the invite code. The token must differ from the admin bot's.

func (*Manager) SaveUserNotifyPrefs added in v1.2.0

func (m *Manager) SaveUserNotifyPrefs(prefs map[string]bool, expiringDays int) error

SaveUserNotifyPrefs persists the user-facing notification categories and how many days ahead the expiry warning goes out.

func (*Manager) SeedProxies

func (m *Manager) SeedProxies()

SeedProxies loads the proxy pool synchronously from current settings WITHOUT triggering a reconcile. Called once at startup, before the first reconcile, so Xray comes up a single time with the URL-sourced proxies already in place.

func (*Manager) SetACMETarget

func (m *Manager) SetACMETarget(target, email, provider, eabKID, eabHMAC string) error

SetACMETarget sets the ACME target (a domain OR an IP address), saves the chosen CA provider (provider = "letsencrypt" | "zerossl"), obtains a certificate, and reloads Xray. host and sni are both set to the target so the cert, the client link address and the SNI all match.

func (*Manager) SetAbuse added in v1.3.0

func (m *Manager) SetAbuse(s *abuse.Store)

SetAbuse wires the blocklist store in and configures it from the persisted settings (which categories are active + the custom list), loading whatever is already cached — so a panel that has run before starts matching at boot rather than staying blind until the first download lands.

func (*Manager) SetAbuseConfig added in v1.3.0

func (m *Manager) SetAbuseConfig(enabled bool, categories map[string]bool, custom string, alertMin int) error

SetAbuseConfig persists the blocklist config and reconfigures the live matcher.

func (*Manager) SetAdminModerationNotifier added in v1.0.0

func (m *Manager) SetAdminModerationNotifier(fn func(reqID int64, name, plan string))

SetAdminModerationNotifier registers a callback (the admin bot) that posts a signup awaiting moderation, with approve/reject buttons. Passing nil clears it.

func (*Manager) SetAdminNotifier added in v0.6.0

func (m *Manager) SetAdminNotifier(fn func(html string))

SetAdminNotifier registers a callback (the admin bot) that broadcasts a message to all authorized admin chats. Passing nil clears it.

func (*Manager) SetAdminRole added in v0.14.0

func (m *Manager) SetAdminRole(actorID, targetID int64, role string) error

SetAdminRole moves an account between roles.

func (*Manager) SetBroadcastStatus added in v1.2.0

func (m *Manager) SetBroadcastStatus(id int64, status string) error

SetBroadcastStatus applies an operator control (pause / resume / cancel). Terminal states are refused a second transition so a cancelled run can't be revived into sending the rest of a message the operator has already thought better of.

func (*Manager) SetConnGuard added in v1.5.0

func (m *Manager) SetConnGuard(wanted bool, lim connguard.Limits)

SetConnGuard records the operator's per-IP guard preference and the limits to use, so the Manager can re-apply the rules itself when the port set changes. Without this the guard would only ever match the ports that existed at boot.

func (*Manager) SetDecoyTemplate

func (m *Manager) SetDecoyTemplate(name string) error

SetDecoyTemplate persists the chosen masquerade template (caller swaps the live decoy handler).

func (*Manager) SetGeoRefresh added in v1.0.0

func (m *Manager) SetGeoRefresh(hours int) error

SetGeoRefresh persists the geo auto-refresh cadence (hours; 0 ⇒ never) and, if enabling with geo already stale, refreshes right away instead of waiting for the loop's next tick.

func (*Manager) SetGroupMembers added in v1.5.0

func (m *Manager) SetGroupMembers(groupID int64, userIDs []int64) error

SetGroupMembers replaces the users in a group — the group-side twin of SetUserGroups, so membership can be edited from either end.

func (*Manager) SetIPListRefresh added in v1.1.0

func (m *Manager) SetIPListRefresh(hours int) error

SetIPListRefresh persists the iplist auto-refresh cadence (hours; 0 ⇒ never), refreshing at once if enabling with the lists already stale.

func (*Manager) SetMasterLabel added in v1.0.0

func (m *Manager) SetMasterLabel(label string) error

SetMasterLabel sets the panel server's display name used in config labels.

func (*Manager) SetMasterProtocols added in v1.0.0

func (m *Manager) SetMasterProtocols(vless, hysteria, reality bool) error

SetMasterProtocols toggles the panel's own (master) protocols on/off. The connection DETAILS (ports, transport, REALITY donor, anti-DPI) stay global and are edited in the Подключения settings; only the on/off lives on the master server card, so the master toggles its protocols the same way a node does.

func (*Manager) SetMasterReality added in v1.0.0

func (m *Manager) SetMasterReality(dest string, regen bool) error

SetMasterReality sets the panel's own REALITY donor and optionally regenerates its keys, then reloads Xray. The donor is live-probed when it changes while REALITY is on (mirrors ApplyConnections).

func (*Manager) SetNodeACME added in v1.0.0

func (m *Manager) SetNodeACME(id int64, target, email, provider string) error

SetNodeACME sets a node's own domain (ACME target), e-mail and CA provider, then wakes the node so its agent re-issues the cert. The panel can't issue a remote node's cert — the node does that — so this only persists the config and (for ZeroSSL) fetches the EAB the node's agent needs.

func (*Manager) SetNodeAPIPathCallback added in v1.0.0

func (m *Manager) SetNodeAPIPathCallback(cb func(string))

SetNodeAPIPathCallback registers the live-swap hook (called by the router).

func (*Manager) SetNodeDNS added in v1.0.0

func (m *Manager) SetNodeDNS(id int64, dns *string) error

SetNodeDNS saves a node's own DNS override (nil ⇒ inherit the panel's) without touching routing/egress, and wakes the node so it pulls the new config. The DNS tab saves through here, independent of the routing tab.

func (*Manager) SetNodeEnabled added in v1.0.0

func (m *Manager) SetNodeEnabled(id int64, enabled bool) error

SetNodeEnabled toggles a node and wakes it (a disabled node is told to stop).

func (*Manager) SetNodeGeoRefresh added in v1.0.0

func (m *Manager) SetNodeGeoRefresh(id int64, hours int) error

SetNodeGeoRefresh sets a node's own geo auto-refresh cadence (hours; 0 ⇒ never) and wakes it so the new cadence reaches its agent (via NodeMeta) promptly.

func (*Manager) SetNodeReality added in v1.0.0

func (m *Manager) SetNodeReality(id int64, dest string, regen bool) error

SetNodeReality sets a node's own REALITY donor (empty ⇒ inherit the panel's) and, when regen is set, regenerates the node's REALITY keypair. Wakes the node so the new identity (and its share links) propagate.

func (*Manager) SetPanelName added in v0.5.0

func (m *Manager) SetPanelName(name string) error

SetPanelName validates and persists the panel display name (empty ⇒ default).

func (*Manager) SetPanelTheme added in v0.5.0

func (m *Manager) SetPanelTheme(t branding.Theme) error

SetPanelTheme validates and persists the colour theme (each field empty ⇒ the matching default applies).

func (*Manager) SetProxyMode

func (m *Manager) SetProxyMode(enabled bool, typ string, port int, user, pass string) error

SetProxyMode persists the forward-proxy inbound (proxy mode) and reloads Xray.

func (*Manager) SetResetPeriod

func (m *Manager) SetResetPeriod(ctx context.Context, id int64, period string) error

SetResetPeriod sets a user's automatic quota-reset period (none|daily|weekly| monthly|yearly), anchoring the cycle at now.

func (*Manager) SetTimezone

func (m *Manager) SetTimezone(name string) error

SetTimezone validates and persists the operator's IANA timezone, then updates the cached location so per-day stats re-bucket immediately.

func (*Manager) SetUserAutoDelete added in v0.15.0

func (m *Manager) SetUserAutoDelete(days int) error

SetUserAutoDelete configures how many days an expired user is kept before being deleted. 0 disables deletion entirely. The admin-audit row for the change is written by the HTTP layer (see server/audit.go), like every other setting.

func (*Manager) SetUserEnabled

func (m *Manager) SetUserEnabled(ctx context.Context, id int64, enabled bool) error

SetUserEnabled enables/disables a user (manual on/off) and reconciles so the proxy config drops or re-adds them.

func (*Manager) SetUserGroups added in v1.5.0

func (m *Manager) SetUserGroups(userID int64, groupIDs []int64) error

SetUserGroups replaces a user's group membership.

func (*Manager) SetUserLimits

func (m *Manager) SetUserLimits(ctx context.Context, id, dataLimit, expireAt int64, deviceLimit int) error

SetUserLimits updates a user's quota/expiry/device cap and re-enables them.

func (*Manager) SetUserNotifier added in v0.6.0

func (m *Manager) SetUserNotifier(fn func(chatID int64, html string))

SetUserNotifier registers a callback (the user bot) that pushes a message to a VPN user's Telegram chat. Passing nil clears it.

func (*Manager) SetXrayDNS

func (m *Manager) SetXrayDNS(dns string) error

SetXrayDNS persists the Xray DNS servers and reloads Xray with the new config.

func (*Manager) Settings

func (m *Manager) Settings() (*model.Settings, error)

Settings returns the current settings row (read-only handlers).

func (*Manager) StartBroadcast added in v1.2.0

func (m *Manager) StartBroadcast(id int64) error

StartBroadcast begins delivery of a freshly created broadcast.

func (*Manager) StartPlanPayment added in v0.6.0

func (m *Manager) StartPlanPayment(ctx context.Context, userID, planID int64, provider string) (*model.PaymentOrder, error)

StartPlanPayment creates an order plus a provider payment and returns the order with its hosted pay URL. provider may be "" when exactly one method is enabled. The payer is returned to Telegram after paying (the bot flow).

func (*Manager) StartPlanPaymentReturn added in v0.11.0

func (m *Manager) StartPlanPaymentReturn(ctx context.Context, userID, planID int64, provider, returnURL string) (*model.PaymentOrder, error)

StartPlanPaymentReturn is StartPlanPayment for the web subscription page: it sends the payer back to returnURL (the sub page) after a card payment instead of to Telegram. returnURL is used by hosted-form providers (YooKassa); CryptoBot ignores it.

func (*Manager) StartSysstat

func (m *Manager) StartSysstat(diskPath string)

StartSysstat begins sampling host metrics (CPU/RAM/disk/network) and the live VPN throughput for the dashboard. diskPath selects the filesystem reported under "disk".

func (*Manager) StatsByUser

func (m *Manager) StatsByUser(from, to string) ([]model.UserTotal, error)

StatsByUser returns per-user totals over the period.

func (*Manager) StatsSeries

func (m *Manager) StatsSeries(userID int64, from, to string) ([]model.DailyPoint, error)

StatsSeries returns per-day traffic between from/to (YYYY-MM-DD); userID 0 = all.

func (*Manager) Store

func (m *Manager) Store() *store.Store

Store exposes the underlying store for read-only handlers.

func (*Manager) SubscribeAppLogs

func (m *Manager) SubscribeAppLogs() (<-chan string, func())

SubscribeAppLogs returns a channel of new panel log lines and an unsubscribe.

func (*Manager) SubscribeXrayLogs

func (m *Manager) SubscribeXrayLogs() (<-chan string, func())

SubscribeXrayLogs returns a channel of new Xray log lines and an unsubscribe.

func (*Manager) Summary

func (m *Manager) Summary() (*Summary, error)

Summary computes the dashboard overview.

func (*Manager) SweepNodeAlerts added in v1.5.0

func (m *Manager) SweepNodeAlerts()

SweepNodeAlerts compares every node's current state against what admins were last told and sends the differences.

func (*Manager) SystemStatus

func (m *Manager) SystemStatus() (*SystemStatus, error)

SystemStatus assembles the dashboard payload from the host sampler, the Xray supervisor and the user summary.

func (*Manager) TLSStatus

func (m *Manager) TLSStatus() (*TLSStatus, error)

TLSStatus reports the current TLS settings and active certificate.

func (*Manager) TakeNodeGeoRefresh added in v1.0.0

func (m *Manager) TakeNodeGeoRefresh(id int64) bool

TakeNodeGeoRefresh consumes (and clears) a node's pending geo-refresh flag.

func (*Manager) TakeNodeUpdate added in v1.0.0

func (m *Manager) TakeNodeUpdate(id int64) bool

TakeNodeUpdate consumes (and clears) a node's pending self-update flag.

func (*Manager) TakeNodeXrayRestart added in v1.4.0

func (m *Manager) TakeNodeXrayRestart(id int64, reportedStart int64) bool

TakeNodeXrayRestart reports whether this node should be told to bounce Xray now, and marks the command as sent. reportedStart is the Xray start time the node just reported, remembered as the "before" the confirmation compares against.

It returns true exactly once per request: the node must not restart again on every poll while the panel is still waiting to hear that the first one landed.

func (*Manager) TelegramWebAppSDK added in v1.5.0

func (m *Manager) TelegramWebAppSDK() ([]byte, bool)

TelegramWebAppSDK returns a server-side cached copy of telegram.org's telegram-web-app.js so the subscription page can serve it from our own (reachable) origin. A fresh copy is returned as-is; a stale one is served immediately while a refresh runs behind it (stale-while-revalidate), so a page load never waits on a copy we already have.

A COLD cache fetches inline, so the first visitor still gets the real SDK rather than an empty file. That is the one place this can make a page wait, and it is bounded on both axes: telegramSDKBudget caps the single fetch, and a failure arms a telegramSDKRetryGap cooldown during which cold reads return immediately. So an unreachable telegram.org costs one bounded wait per cooldown, not one per page load — which is what keeps this from reintroducing the very hang the proxy exists to remove. ok=false means "serve an empty body"; the page treats a missing SDK as "not in Telegram" and renders normally.

func (*Manager) TestWebhook added in v0.10.0

func (m *Manager) TestWebhook(id int64) (int, error)

TestWebhook sends a synchronous "ping" delivery to one endpoint and returns the HTTP status, for the "Test" button in the settings UI. It does not retry.

func (*Manager) TrackVPNViewer

func (m *Manager) TrackVPNViewer() func()

TrackVPNViewer marks one active dashboard-stream subscriber for the life of the returned release func — call `defer mgr.TrackVPNViewer()()`. vpnSpeedLoop only samples Xray (forking `api statsquery` every 3s) while at least one viewer is connected, so an unattended panel costs nothing extra.

func (*Manager) TriggerReconcile

func (m *Manager) TriggerReconcile()

TriggerReconcile requests a FULL config reload (regenerate + restart Xray) for structural changes (protocols, routing, DNS, WARP, TLS, ports). Non-blocking; the reload happens shortly after so the triggering HTTP response flushes first.

func (*Manager) TriggerUserSync

func (m *Manager) TriggerUserSync()

TriggerUserSync requests a live user-set sync (add/remove users via the Xray API, no restart) for user-only changes — far cheaper than a full reload.

func (*Manager) UnlinkTelegramChat added in v0.2.0

func (m *Manager) UnlinkTelegramChat(id int64) error

UnlinkTelegramChat removes one authorized chat (revokes its bot access).

func (*Manager) UnlinkUserTelegram added in v0.13.0

func (m *Manager) UnlinkUserTelegram(ctx context.Context, id int64) error

UnlinkUserTelegram detaches a VPN user's Telegram chat.

func (*Manager) UpdateAdminCredentials

func (m *Manager) UpdateAdminCredentials(adminID int64, currentPassword, username, password, keepToken string) error

UpdateAdminCredentials changes the admin's login and/or password. Empty username or password fields are left unchanged. The current password must be supplied and is re-verified first — a stolen session cookie alone must not be enough to rewrite the credentials. On success every other session for this admin is revoked (the caller's keepToken survives), so a previously stolen cookie can't outlive the change.

func (*Manager) UpdateGroup added in v1.5.0

func (m *Manager) UpdateGroup(id int64, name string, grants []string) error

UpdateGroup validates and stores an edit (rename + grants).

func (*Manager) UpdateInbound added in v1.5.0

func (m *Manager) UpdateInbound(ctx context.Context, in model.Inbound) (*InboundView, error)

UpdateInbound validates and stores an edit. The server it belongs to is taken from the stored row, not the request: moving an inbound between servers would carry a port that is free on one box onto another where it may not be.

func (*Manager) UpdateNode added in v1.0.0

func (m *Manager) UpdateNode(id int64, e store.NodeEdit) error

UpdateNode edits a node and wakes it so config/link changes apply promptly.

func (*Manager) UserAbuse added in v1.3.0

func (m *Manager) UserAbuse(userID int64, limit int) ([]store.AbuseMatch, error)

UserAbuse returns one user's matches.

func (*Manager) UserEvents added in v0.13.0

func (m *Manager) UserEvents(userID int64, limit int, beforeID int64) ([]model.UserEvent, error)

UserEvents returns one user's audit trail, newest first (the «Журнал» modal).

func (*Manager) UserNotifyPrefs added in v1.2.0

func (m *Manager) UserNotifyPrefs() (map[string]bool, int)

UserNotifyPrefs returns the per-category on/off map plus the warning horizon, for the settings UI.

func (*Manager) ValidateBroadcast added in v1.2.0

func (m *Manager) ValidateBroadcast(b *model.Broadcast) error

ValidateBroadcast checks a composed message without storing it — used by the test send, so a preview is refused for the same reasons the real run would be rather than failing later with a raw Telegram error.

func (*Manager) WantNodeLogs added in v1.0.0

func (m *Manager) WantNodeLogs(id int64) bool

WantNodeLogs reports (and is used by the sync handler to set WantLogs) whether an operator is currently viewing this node's logs.

func (*Manager) XrayConfig

func (m *Manager) XrayConfig() ([]byte, error)

XrayConfig returns the live on-disk Xray config.json.

func (*Manager) XrayLogTail

func (m *Manager) XrayLogTail() []string

XrayLogTail returns the buffered recent Xray log lines.

func (*Manager) XrayStatus

func (m *Manager) XrayStatus() (running bool, startedAt int64)

XrayStatus reports whether Xray is running and the unix start time of the current process (which changes on every reload — clients poll it to detect when a config change has finished restarting Xray).

type NodeTraffic added in v1.2.0

type NodeTraffic struct {
	NodeID int64  `json:"node_id"` // 0 = the panel's own server
	Name   string `json:"name"`
	Up     int64  `json:"up"`
	Down   int64  `json:"down"`
}

NodeTraffic is one server's share of the traffic over a period.

type NodeView added in v1.0.0

type NodeView struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Host        string `json:"host"`
	Enabled     bool   `json:"enabled"`
	IsLocal     bool   `json:"is_local"`
	Online      bool   `json:"online"`
	Joined      bool   `json:"joined"`
	LastSeen    int64  `json:"last_seen"`
	NodeVersion string `json:"node_version"`
	XrayVersion string `json:"xray_version"`
	XrayRunning bool   `json:"xray_running"`
	VersionSkew bool   `json:"version_skew"` // running Xray differs from the pinned release
	// XrayRestart is the state of an operator-requested Xray bounce: "pending" while
	// the node has yet to prove it happened, then "done" or "timeout" briefly, then
	// "". Always "" for the master, whose restart is synchronous — nothing to wait for.
	XrayRestart     string `json:"xray_restart,omitempty"`
	VLESSEnabled    bool   `json:"vless_enabled"`
	HysteriaEnabled bool   `json:"hysteria_enabled"`
	RealityEnabled  bool   `json:"reality_enabled"`
	DecoyTemplate   string `json:"decoy_template"`
	// CertSelfSigned is what the node last reported about its live TLS cert: true ⇒
	// still on the self-signed fallback (ACME not obtained yet), false ⇒ a CA cert is
	// in place. Lets the node's Домен tab show the cert status like the master's.
	CertSelfSigned bool   `json:"cert_self_signed"`
	CertIssuer     string `json:"cert_issuer"`     // ≈ ACME provider (empty for the local node)
	CertExpiresAt  int64  `json:"cert_expires_at"` // unix; 0 ⇒ unknown
	// GeoRefreshHours is this server's own geo auto-refresh cadence (hours; 0 ⇒ never).
	GeoRefreshHours int   `json:"geo_refresh_hours"`
	TrafficUp       int64 `json:"traffic_up"`   // today, this node
	TrafficDown     int64 `json:"traffic_down"` // today, this node
	// Routing / XrayDNS carry the node's own override (nil ⇒ inherits the panel's),
	// so the per-node routing+DNS editor can prefill and show inherit vs custom. For
	// the local server (node 0) these carry the master's own routing/DNS so the same
	// editor edits the master.
	Routing *model.RoutingConfig `json:"routing"`
	XrayDNS *string              `json:"xray_dns"`
	// Egress backends (node's own, independent of the master; all off by default).
	// WARP is native to Xray once registered; Opera runs a helper on the node.
	WarpEnabled    bool   `json:"warp_enabled"`
	WarpRegistered bool   `json:"warp_registered"`
	OperaEnabled   bool   `json:"opera_enabled"`
	OperaCountry   string `json:"opera_country"`
	// REALITY identity (per-server). RealityDest is this server's own donor ("" on a
	// node ⇒ inherits the panel's); the public key/shortId/service are shown so the
	// operator can see them and regenerate. The private key is never exposed.
	RealityDest      string `json:"reality_dest"`
	RealityPublicKey string `json:"reality_public_key"`
	RealityShortID   string `json:"reality_short_id"`
	RealityPath      string `json:"reality_path"`
	JoinToken        string `json:"join_token,omitempty"` // only right after create/regen
	// MasterLabel is the master server's config-label name (local node only), so the
	// UI can edit it. Empty for remote nodes (they use their own Name).
	MasterLabel string `json:"master_label,omitempty"`
}

NodeView is one row for the Nodes UI: the node's identity and status plus its effective (override-resolved) protocol toggles and today's traffic. The local server appears as node 0 (IsLocal) so the UI lists every server uniformly.

type Summary

type Summary struct {
	Users        int   `json:"users"`
	EnabledUsers int   `json:"enabled_users"`
	OnlineUsers  int   `json:"online_users"` // connected within model.DeviceOnlineWindow
	TotalUp      int64 `json:"total_up"`
	TotalDown    int64 `json:"total_down"`
	TrafficToday int64 `json:"traffic_today"` // up+down for the current local-time day
	XrayRunning  bool  `json:"xray_running"`
	CertDaysLeft int   `json:"cert_days_left"`
}

Summary is the dashboard overview.

type SystemStatus

type SystemStatus struct {
	sysstat.Stats
	XrayRunning  bool   `json:"xray_running"`
	XrayUptime   int64  `json:"xray_uptime"` // seconds
	XrayVersion  string `json:"xray_version"`
	Goroutines   int    `json:"goroutines"`
	CPUCores     int    `json:"cpu_cores"`
	ProcMem      int64  `json:"proc_mem"` // panel process RSS
	VPNUp        int64  `json:"vpn_up"`   // VPN client throughput (bytes/sec)
	VPNDown      int64  `json:"vpn_down"`
	TotalUp      int64  `json:"total_up"`
	TotalDown    int64  `json:"total_down"`
	Users        int    `json:"users"`
	EnabledUsers int    `json:"enabled_users"`
	OnlineUsers  int    `json:"online_users"`
	TrafficToday int64  `json:"traffic_today"`
	CertDaysLeft int    `json:"cert_days_left"`
}

SystemStatus is the full dashboard payload: host resources plus Xray/panel state.

type TLSPaths

type TLSPaths struct {
	CertPath string
	KeyPath  string
	ACMEDir  string
}

TLSPaths are the on-disk locations the panel manages for TLS material.

type TLSStatus

type TLSStatus struct {
	Mode         string            `json:"mode"`
	Domain       string            `json:"domain"`
	SNI          string            `json:"sni"`
	ACMEEmail    string            `json:"acme_email"`
	ACMEProvider string            `json:"acme_provider"`
	Cert         *tlsutil.CertInfo `json:"cert"`
}

TLSStatus is the current TLS configuration plus active cert metadata.

type TelegramConfig added in v1.2.0

type TelegramConfig struct {
	Enabled     bool
	Token       string
	BackupCron  string
	UserEnabled bool
	UserToken   string
	UserRegMode string
	UserRegCode string

	SupportEnabled  bool
	SupportToken    string
	SupportUsername string
	SupportGroupID  int64
	SupportGreeting string
}

TelegramConfig is every Telegram bot's settings in one value, so they can be checked together and written together.

type ValidationError

type ValidationError struct{ Msg string }

ValidationError marks an error caused by bad operator input rather than a server fault. The server layer maps it to HTTP 400 (vs 500 for everything else); its message is operator-facing (Russian) and safe to show in the UI.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL