service

package
v3.9.8 Latest Latest
Warning

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

Go to latest
Published: Oct 1, 2026 License: GPL-3.0 Imports: 94 Imported by: 0

Documentation

Overview

Package service implements the panel's business-logic layer.

ClientService owns the lifecycle of VPN clients: creation, update, deletion, attach/detach to inbounds, bulk operations, group membership, traffic resets, and the paginated clients listing. Its surface is split across client_*.go files by responsibility (see each file's contents); they all belong to the same package, so the split is purely organizational. ClientService and InboundService are mutually dependent — most ClientService methods take an *InboundService and InboundService embeds a ClientService — which is why the client code lives in package service rather than a sub-package.

Package service provides business logic services for the 3x-ui web panel, including inbound/outbound management, user administration, settings, and Xray integration.

Index

Constants

View Source
const (
	DefaultSubClashUserAgentRegex     = `(?i)(clash|mihomo)`
	DefaultSubJsonUserAgentRegex      = ``
	DefaultRemarkTemplate             = "{{INBOUND}}-{{EMAIL}}|📊{{TRAFFIC_LEFT}}|⏳{{DAYS_LEFT}}D"
	DefaultSubExpiredTemplate         = "⛔ {{EMAIL}} | Expired: {{EXPIRE_DATE}}"
	DefaultSubTrafficDepletedTemplate = "🚫 {{EMAIL}} | Traffic Depleted | {{TRAFFIC_USED}}/{{TRAFFIC_TOTAL}}"
	DefaultTrustedProxyCIDRs          = "127.0.0.1/32,::1/128"
)
View Source
const (
	SubProfileModeNone    = "none"
	SubProfileModeBuiltin = "builtin"
	SubProfileModeCustom  = "custom"
)

Built-in profile links expose the subscription URL and require an explicit opt-in.

View Source
const (
	CoreTypeXray    = "xray"
	CoreTypeSingBox = "sing-box"
)
View Source
const PanelEgressInboundTag = "panel-egress"

PanelEgressInboundTag is the tag of the loopback SOCKS inbound injected into the generated config when a panel outbound is configured. The panel's own HTTP clients dial through it to egress via the chosen outbound.

Variables

View Source
var (
	// Failures deliberately carry no subscription details.
	ErrHappLinkUnavailable = errors.New("happ link unavailable")
	ErrHappSourceTooLong   = errors.New("happ subscription source exceeds 8192 bytes")
)
View Source
var DefaultRealityScanCandidatesCSV = strings.Join(defaultRealityScanCandidates, ",")

DefaultRealityScanCandidatesCSV is the shipped default for the realityScanCandidates setting (comma-separated host:port list).

View Source
var ErrClientNotInInbound = errors.New("client not found in inbound")

ErrClientNotInInbound is returned (wrapped) when a client cannot be located in an inbound's settings during deletion. Deletion treats it as non-fatal so the operation stays idempotent and tolerant of pre-existing data drift between the clients table and the inbound's settings JSON.

View Source
var ErrNodeMtlsTrustBundleInvalid = errors.New("nodeMtlsClientCAPem is not a valid certificate bundle")

ErrNodeMtlsTrustBundleInvalid separates a stored bundle that will not parse from a settings read that failed, which callers report differently.

View Source
var NodeMetricKeys = []string{"cpu", "mem", "netUp", "netDown"}

NodeMetricKeys lists the per-node metric names NodeHeartbeatJob writes.

View Source
var SystemMetricKeys = []string{
	"cpu", "mem", "swap", "netUp", "netDown", "pktUp", "pktDown", "diskRead", "diskWrite", "diskUsage", "tcpCount", "udpCount", "online", "load1", "load5", "load15",
}

SystemMetricKeys lists the metric names ServerService writes on every status sample. Exposed for documentation/test purposes; the controller validates incoming names against an allow-list.

View Source
var XrayMetricKeys = []string{
	"xrAlloc", "xrSys", "xrHeapObjects", "xrNumGC", "xrPauseNs",
}

XrayMetricKeys lists series sourced from xray's /debug/vars expvar endpoint.

Functions

func DesiredSudokuInstances added in v3.9.5

func DesiredSudokuInstances() ([]sudoku.Instance, error)

func DisableUnavailableSudokuInbounds added in v3.9.5

func DisableUnavailableSudokuInbounds() error

func EnsureDnsServerRouting

func EnsureDnsServerRouting(raw string) (string, error)

EnsureDnsServerRouting keeps a set of managed "direct" allow-rules — one per distinct port among any private/internal dns.servers addresses — in sync, positioned immediately before the panel's default geoip:private block rule.

Why this matters: Xray's own DNS client traffic is dispatched through the same routing table as proxied client traffic. If dns.servers points at a private IP (e.g. a self-hosted AdGuard Home / Pi-hole reachable on the same Docker network as Xray — a common self-hosted setup) and the panel's default private-IP block rule is active, Xray's own DNS lookups get silently dropped by that rule. Xray then falls back to dialing destinations by raw hostname once its internal DNS attempt times out (~4s), so proxied connections still work, just with a multi-second stall added to every new domain, with no error surfaced to the client or admin.

Each managed rule is scoped to its port (not just the IP), so the exception only reopens the DNS traffic that actually needs it rather than every port on the private host. On every save, all previously managed rules are stripped out and a fresh set is rebuilt from the current dns.servers config and reinserted right before the block rule (recomputing its index after the strip) — this corrects both content drift (dns.servers changed) and position drift (an admin dragged a managed rule below the block rule in the Routing tab, which would otherwise silently reintroduce the stall with nothing to notice or fix it). The rebuilt result is only written back if it actually differs from the input, so well-formed configs aren't churned on every save. Manually-authored rules are never touched — see managedDnsAllowRule.

func EnsureOnlinePresenceTracker added in v3.9.7

func EnsureOnlinePresenceTracker()

EnsureOnlinePresenceTracker keeps local/node presence available when sing-box is the selected core and no Xray process object exists to carry that state.

func EnsureStatsRouting

func EnsureStatsRouting(raw string) (string, error)

EnsureStatsRouting hoists the `api -> api` routing rule to the front of routing.rules so the stats query path is never starved by a catch-all rule the admin may have added or reordered above it.

Why this matters (#4113, #2818): an admin who adds a cascade outbound (e.g. vless to another server) and a routing rule sending all inbound traffic to it ends up sending the internal stats inbound's traffic to that cascade too, since rules are evaluated top-to-bottom and the catch-all matches first. The panel's gRPC stats query then can't reach the running xray instance, GetTraffic returns nothing, and every client appears offline with zero traffic even though the actual proxy path works fine.

The api inbound is special-cased internal infrastructure for the panel, not something the admin should ever route to a real outbound. Keeping its rule pinned at index 0 is the only correct configuration.

If the api rule is already at index 0 the input is returned unchanged. If it exists somewhere else it is moved. If it is missing entirely a default rule (`type=field, inboundTag=[api], outboundTag=api`) is inserted at the front. Other routing entries keep their relative order.

func EnsureSudokuCredentials added in v3.9.5

func EnsureSudokuCredentials(inboundID int) error

func FilterNodeSnapshot

func FilterNodeSnapshot(n *model.Node, snap *runtime.TrafficSnapshot)

func FriendlyProbeError

func FriendlyProbeError(msg string) string

func IsAllowedHistoryBucket

func IsAllowedHistoryBucket(bucketSeconds int) bool

IsAllowedHistoryBucket reports whether a bucket-seconds value is in the whitelist used by /server/history, /server/cpuHistory, /server/xrayMetricsHistory, /server/xrayObservatoryHistory, and /nodes/history.

func NodeEgressInboundTag

func NodeEgressInboundTag(nodeID int) string

NodeEgressInboundTag returns the loopback SOCKS inbound tag for a given node.

func ParseHiddifyBackup added in v3.9.6

func ParseHiddifyBackup(reader io.Reader) (*HiddifyBackup, HiddifyPreview, error)

func ParseInboundSettingsClients

func ParseInboundSettingsClients(settings string) ([]model.Client, error)

func PersistSystemMetrics

func PersistSystemMetrics() error

PersistSystemMetrics writes the host time-series to disk via a temp file + rename so a crash mid-write can't corrupt the previous snapshot. Called on a timer and at shutdown.

func RefreshSudokuCredentialsOnInbound added in v3.9.5

func RefreshSudokuCredentialsOnInbound(inbound *model.Inbound) error

func RefreshTelemtWebSubscriptionRoutes added in v3.9.6

func RefreshTelemtWebSubscriptionRoutes() error

RefreshTelemtWebSubscriptionRoutes updates a previously generated Telemt vhost when aliases are imported or after the panel is upgraded.

func RegisterSubLinkProvider

func RegisterSubLinkProvider(p SubLinkProvider)

func RemoveIndex

func RemoveIndex(s []any, index int) []any

RemoveIndex removes an element at the specified index from a slice. Returns a new slice with the element removed.

func ResetClientTrafficLiveSampler added in v3.9.7

func ResetClientTrafficLiveSampler()

ResetClientTrafficLiveSampler drops the live-only baseline. Call this while no websocket client is connected so the first viewer establishes a fresh baseline instead of receiving an artificial speed spike accumulated while the page was closed.

func RestoreSystemMetrics

func RestoreSystemMetrics()

RestoreSystemMetrics loads a previously persisted host time-series on startup. A missing file is not an error (first boot). A pre-tier flat snapshot is migrated by replaying its samples through the rollup.

func SanitizeHTTPURL

func SanitizeHTTPURL(raw string) (string, error)

SanitizeHTTPURL validates and normalizes an http(s) URL without resolving DNS. Use SanitizePublicHTTPURL at the point of an outbound request.

func SanitizePublicHTTPURL

func SanitizePublicHTTPURL(raw string, allowPrivate bool) (string, error)

SanitizePublicHTTPURL validates and normalizes an http(s) URL, then blocks private/internal targets unless the caller explicitly allows them.

func SetEventBus

func SetEventBus(b *eventbus.Bus)

SetEventBus assigns the global event bus used by applyObservatory to publish outbound health transitions. Must be called once during startup before any Sample tick runs.

func SetSingBoxDependencies

func SetSingBoxDependencies(inbound *InboundService, settings *SettingService)

SetSingBoxDependencies wires the panel services used by the sing-box config generator. It mirrors the existing Xray service lifecycle wiring.

func SetXrayProcessForTest

func SetXrayProcessForTest(p *xray.Process) (restore func())

SetXrayProcessForTest installs p as the running process and returns the restore func, so tests in other packages can observe online state. Never call it in production.

func StartTelemtConnectionMonitor added in v3.9.2

func StartTelemtConnectionMonitor()

func StartTrafficWriter

func StartTrafficWriter()

StartTrafficWriter spins up the serial writer goroutine. Safe to call again after StopTrafficWriter — each Start/Stop cycle gets fresh channels. The previous sync.Once-based implementation deadlocked after a SIGHUP-driven panel restart: Stop killed the consumer goroutine but Once prevented Start from spawning a new one, so every later submitTrafficWrite blocked forever on <-req.done with no consumer (including the AddTraffic call inside XrayService.GetXrayConfig that runs from startTask).

func StopTrafficWriter

func StopTrafficWriter()

StopTrafficWriter cancels the writer context and waits for the goroutine to drain any pending requests before returning. Resets the package state so a subsequent StartTrafficWriter can spawn a fresh consumer.

func SyncClientCoreCompatibility added in v3.9.5

func SyncClientCoreCompatibility(oldCore, newCore string) error

SyncClientCoreCompatibility synchronizes client enable state with a core switch. A client is incompatible when any local inbound it is attached to is unsupported by the target core. AutoDisabledByCore distinguishes this from a manual disable.

func TriggerFirewallSync added in v3.9.7

func TriggerFirewallSync()

func UnwrapXrayTemplateConfig

func UnwrapXrayTemplateConfig(raw string) string

UnwrapXrayTemplateConfig returns the raw xray config JSON from `raw`, peeling off any number of `{ "inboundTags": ..., "outboundTestUrl": ..., "xraySetting": <real config> }` response-shaped wrappers that may have ended up in the database.

How it got there: getXraySetting used to embed the raw DB value as `xraySetting` in its response without checking whether the stored value was already that exact response shape. If the frontend then saved it verbatim (the textarea is a round-trip of the JSON it was handed), the wrapper got persisted — and each subsequent save nested another layer, producing the blank Xray Settings page reported in issue #4059.

If `raw` does not look like a wrapper, it is returned unchanged.

func ValidateRegex

func ValidateRegex(pattern string) error

func XrayProcess

func XrayProcess() *xray.Process

XrayProcess returns the current Xray process instance (may be nil when Xray is not running). It exposes the lifecycle snapshot to callers outside this package (e.g. the tgbot subpackage).

Types

type AmneziaWGLogs

type AmneziaWGLogs struct {
	Peers   []PeerActivity `json:"peers"`
	Events  []string       `json:"events" example:"[\"2025/01/01 12:00:00 amneziawg: started interface awg1 for inbound 1\"]"`
	Running bool           `json:"running" example:"true"`
}

AmneziaWGLogs is what the overview's AmneziaWG log view renders: the live per-peer activity of every running embedded interface, plus the panel's own recent AmneziaWG lifecycle log lines that explain a peer being absent from Peers at all.

type BalancerStatus

type BalancerStatus struct {
	Tag      string   `json:"tag"`
	Running  bool     `json:"running"`
	Override string   `json:"override"`
	Selected []string `json:"selected"`
}

BalancerStatus is the live view of one balancer for the panel UI. Running is false when the balancer isn't present in the running core (e.g. xray is stopped or the balancer hasn't been saved/applied yet).

type BulkAdjustReport

type BulkAdjustReport struct {
	Email  string `json:"email"`
	Reason string `json:"reason"`
}

type BulkAdjustResult

type BulkAdjustResult struct {
	Adjusted int                `json:"adjusted"`
	Skipped  []BulkAdjustReport `json:"skipped,omitempty"`
}

BulkAdjustResult is returned by BulkAdjust to report how many clients were successfully updated and which were skipped (typically because the field being adjusted was unlimited for that client) or failed.

type BulkAttachResult

type BulkAttachResult struct {
	Attached []string `json:"attached"`
	Skipped  []string `json:"skipped"`
	Errors   []string `json:"errors"`
}

BulkAttachResult reports the outcome of a bulk attach across target inbounds.

type BulkCreateReport

type BulkCreateReport struct {
	Email  string `json:"email"`
	Reason string `json:"reason"`
}

type BulkCreateResult

type BulkCreateResult struct {
	Created int                `json:"created"`
	Skipped []BulkCreateReport `json:"skipped,omitempty"`
}

BulkCreateResult mirrors BulkAdjustResult for the create flow.

type BulkDelInboundReport

type BulkDelInboundReport struct {
	Id     int    `json:"id"`
	Reason string `json:"reason"`
}

type BulkDelInboundResult

type BulkDelInboundResult struct {
	Deleted int                    `json:"deleted"`
	Skipped []BulkDelInboundReport `json:"skipped,omitempty"`
}

type BulkDeleteReport

type BulkDeleteReport struct {
	Email  string `json:"email"`
	Reason string `json:"reason"`
}

type BulkDeleteResult

type BulkDeleteResult struct {
	Deleted int                `json:"deleted"`
	Skipped []BulkDeleteReport `json:"skipped,omitempty"`
}

BulkDeleteResult mirrors BulkAdjustResult: total deleted plus per-email skip reasons when an email could not be processed.

type BulkDetachResult

type BulkDetachResult struct {
	Detached []string `json:"detached"`
	Skipped  []string `json:"skipped"`
	Errors   []string `json:"errors"`
}

BulkDetachResult reports the outcome of a bulk detach across target inbounds.

type BulkSetEnableReport

type BulkSetEnableReport struct {
	Email  string `json:"email"`
	Reason string `json:"reason"`
}

type BulkSetEnableResult

type BulkSetEnableResult struct {
	Changed int                   `json:"changed"`
	Skipped []BulkSetEnableReport `json:"skipped,omitempty"`
}

type ClientCreatePayload

type ClientCreatePayload struct {
	Client     model.Client `json:"client"`
	InboundIds []int        `json:"inboundIds"`
	LimitHwid  int          `json:"-"`
}

func (ClientCreatePayload) MarshalJSON

func (p ClientCreatePayload) MarshalJSON() ([]byte, error)

func (*ClientCreatePayload) UnmarshalJSON

func (p *ClientCreatePayload) UnmarshalJSON(data []byte) error

type ClientHwidInfo

type ClientHwidInfo struct {
	Id          int    `json:"id"`
	FirstSeen   int64  `json:"firstSeen"`
	LastSeen    int64  `json:"lastSeen"`
	UserAgent   string `json:"userAgent"`
	DeviceOS    string `json:"deviceOs"`
	OsVersion   string `json:"osVersion"`
	DeviceModel string `json:"deviceModel"`
	Fingerprint string `json:"fingerprint"`
}

type ClientIpInfo

type ClientIpInfo struct {
	IP   string `json:"ip"`
	Time string `json:"time"`
	Node string `json:"node"`
}

ClientIpInfo is one IP shown in the panel's per-client IP log, labelled with the node it is connecting through ("" = this local panel).

type ClientPageParams

type ClientPageParams struct {
	Page     int    `form:"page"`
	PageSize int    `form:"pageSize"`
	Search   string `form:"search"`
	Filter   string `form:"filter"`
	Protocol string `form:"protocol"`
	Inbound  string `form:"inbound"`
	Sort     string `form:"sort"`
	Order    string `form:"order"`

	ExpiryFrom int64  `form:"expiryFrom"`
	ExpiryTo   int64  `form:"expiryTo"`
	UsageFrom  int64  `form:"usageFrom"`
	UsageTo    int64  `form:"usageTo"`
	AutoRenew  string `form:"autoRenew"`
	HasTgID    string `form:"hasTgId"`
	HasComment string `form:"hasComment"`
	Group      string `form:"group"`
}

ClientPageParams are the query params accepted by /panel/api/clients/list/paged. All fields are optional — the empty value means "no filter" / defaults.

Filter / Protocol / Inbound accept either a single value or a comma-separated list; matching is OR within a field and AND across fields. The numeric range fields treat 0 as "unset" on the lower bound and 0 (or negative) as "unbounded" on the upper bound.

type ClientPageResponse

type ClientPageResponse struct {
	Items    []ClientSlim   `json:"items"`
	Total    int            `json:"total" example:"2000"`
	Filtered int            `json:"filtered" example:"47"`
	Page     int            `json:"page" example:"1"`
	PageSize int            `json:"pageSize" example:"25"`
	Summary  ClientsSummary `json:"summary"`
	Groups   []string       `json:"groups" example:"[\"staff\",\"trial\"]"`
}

ClientPageResponse is the shape returned by ListPaged. `Total` is the row count in the DB; `Filtered` is the count after Search/Filter/Protocol were applied, before pagination. The page contains at most PageSize items. Summary is computed across the full DB row set so dashboard counters on the clients page stay stable as the user paginates/filters.

type ClientResetCycle

type ClientResetCycle struct {
	Email           string
	TrafficResetDay int
	Enable          bool
	Total           int64
	Used            int64
}

ClientResetCycle is the slice of a client the reset job needs: enough to know whether it is due, and whether its disable is the quota's doing or the operator's.

func (ClientResetCycle) Depleted

func (c ClientResetCycle) Depleted() bool

Depleted reports a client the quota switched off. A reset restores that one; a client disabled below its quota was switched off by hand and stays off.

type ClientService

type ClientService struct{}

func (*ClientService) AddInboundClient

func (s *ClientService) AddInboundClient(inboundSvc *InboundService, data *model.Inbound) (bool, error)

func (*ClientService) AddToGroup

func (s *ClientService) AddToGroup(emails []string, group string) (int, error)

func (*ClientService) ApplyInboundClientDelta

func (s *ClientService) ApplyInboundClientDelta(tx *gorm.DB, inboundId int, changed []model.Client, detachEmails []string) error

ApplyInboundClientDelta persists only the clients an edit actually changed plus the emails it detached, leaving every other link on the inbound alone — the whole point being that a one-client edit must not rewrite the inbound's entire membership set (#6252).

func (*ClientService) Attach

func (s *ClientService) Attach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error)

Attach applies the client to every requested inbound: one failing inbound no longer aborts the others, so the error can name several and needRestart holds.

func (*ClientService) AttachByEmail

func (s *ClientService) AttachByEmail(inboundSvc *InboundService, email string, inboundIds []int) (bool, error)

func (*ClientService) BulkAdjust

func (s *ClientService) BulkAdjust(inboundSvc *InboundService, emails []string, addDays int, addBytes int64, flow string, limitHwid *int, adTag string) (BulkAdjustResult, bool, error)

BulkAdjust shifts ExpiryTime by addDays (days) and TotalGB by addBytes for every email in the list. Clients whose corresponding field is unlimited (0) are skipped — bulk extend should not accidentally limit an unlimited client. addDays and addBytes may be negative. flow sets the XTLS flow, limitHwid the max registered devices (0 = unlimited) and adTag the MTProto sponsor channel; "none" clears flow or adTag.

Like BulkDelete, the work is grouped by inbound so each inbound's settings JSON is parsed and written exactly once regardless of how many target emails it contains.

func (*ClientService) BulkAttach

func (s *ClientService) BulkAttach(inboundSvc *InboundService, emails []string, inboundIds []int) (*BulkAttachResult, bool, error)

BulkAttach attaches the given existing clients (by email) to each target inbound, reusing their identity (email/UUID/password/subId) and a shared traffic row. It adds all clients to a target in a single AddInboundClient call, and reports clients already present on a target as skipped.

func (*ClientService) BulkCreate

func (s *ClientService) BulkCreate(inboundSvc *InboundService, payloads []ClientCreatePayload) (BulkCreateResult, bool, error)

func (*ClientService) BulkDelete

func (s *ClientService) BulkDelete(inboundSvc *InboundService, emails []string, keepTraffic bool) (BulkDeleteResult, bool, error)

BulkDelete removes every client in the list in one optimized pass. Instead of running the full single-delete pipeline N times (which would re-read, re-parse, and re-write each inbound's settings JSON for every email), it groups emails by inbound and performs a single read-modify-write per inbound. Per-row DB cleanups are also batched with IN-clause queries at the end. Errors on a particular email are recorded in the Skipped list and processing continues for the rest.

func (*ClientService) BulkDetach

func (s *ClientService) BulkDetach(inboundSvc *InboundService, emails []string, inboundIds []int) (*BulkDetachResult, bool, error)

BulkDetach detaches the given existing clients (by email) from each target inbound. (email, inbound) pairs where the client is not currently attached are silently skipped at the inbound level; emails that aren't attached to any of the requested inbounds are reported under skipped. ClientRecord rows are kept even when they become orphaned (matches single-client detach semantics); callers should use bulkDelete for full removal.

func (*ClientService) BulkResetTraffic

func (s *ClientService) BulkResetTraffic(inboundSvc *InboundService, emails []string) (int, error)

func (*ClientService) BulkSetEnable

func (s *ClientService) BulkSetEnable(inboundSvc *InboundService, emails []string, enable bool) (BulkSetEnableResult, bool, error)

func (*ClientService) CheckIsEnabledByEmail

func (s *ClientService) CheckIsEnabledByEmail(inboundSvc *InboundService, clientEmail string) (bool, error)

func (*ClientService) ClearClientHwids

func (s *ClientService) ClearClientHwids(email string) error

func (*ClientService) Create

func (s *ClientService) Create(inboundSvc *InboundService, payload *ClientCreatePayload) (bool, error)

Create applies the client to every requested inbound: one failing inbound no longer aborts the others, so the error can name several and needRestart holds.

func (*ClientService) CreateGroup

func (s *ClientService) CreateGroup(name string) error

func (*ClientService) CreateOne

func (s *ClientService) CreateOne(inboundSvc *InboundService, inboundId int, client model.Client) (bool, error)

func (*ClientService) DelDepleted

func (s *ClientService) DelDepleted(inboundSvc *InboundService) (int, bool, error)

func (*ClientService) DelInboundClientByEmail

func (s *ClientService) DelInboundClientByEmail(inboundSvc *InboundService, inboundId int, email string, keepTraffic bool, fullDelete bool) (bool, error)

func (*ClientService) Delete

func (s *ClientService) Delete(inboundSvc *InboundService, id int, keepTraffic bool) (bool, error)

func (*ClientService) DeleteByEmail

func (s *ClientService) DeleteByEmail(inboundSvc *InboundService, email string, keepTraffic bool) (bool, error)

func (*ClientService) DeleteClientHwid

func (s *ClientService) DeleteClientHwid(email string, id int) error

DeleteClientHwid removes one device, scoped to the client's sub_id: ids are a global auto-increment, so an id outside this subscription won't match.

func (*ClientService) DeleteGroup

func (s *ClientService) DeleteGroup(name string) (int, error)

func (*ClientService) DeleteOrphans

func (s *ClientService) DeleteOrphans() (int, error)

DeleteOrphans removes every client that is not attached to any inbound, together with its traffic rows, IP log, and external links. It mirrors the cleanup the single-client Delete performs, batched into one transaction. Returns the number of clients deleted.

func (*ClientService) Detach

func (s *ClientService) Detach(inboundSvc *InboundService, id int, inboundIds []int) (bool, error)

func (*ClientService) DetachByEmail

func (s *ClientService) DetachByEmail(inboundSvc *InboundService, inboundId int, email string) (bool, error)

func (*ClientService) DetachByEmailMany

func (s *ClientService) DetachByEmailMany(inboundSvc *InboundService, email string, inboundIds []int) (bool, error)

func (*ClientService) DetachInbound

func (s *ClientService) DetachInbound(tx *gorm.DB, inboundId int) error

func (*ClientService) EffectiveFlow

func (s *ClientService) EffectiveFlow(tx *gorm.DB, recordId int) (string, error)

EffectiveFlow returns the client's flow from the first flow-capable inbound it is attached to (lowest inbound_id with a non-empty flow_override). The canonical clients.Flow column is unreliable for multi-inbound clients: a non-flow inbound (Hysteria, WS, gRPC, …) carries an empty flow and, when its SyncInbound runs last, overwrites the column to "" even though a VLESS Reality inbound stored a real flow. The per-inbound flow_override is always correct, so derive the display flow from it (order-independent). See issue #4792.

func (*ClientService) EffectiveFlowsByEmails

func (s *ClientService) EffectiveFlowsByEmails(tx *gorm.DB, emails []string) (map[string]string, error)

EffectiveFlowsByEmails resolves the intended flow (non-empty flow_override, lowest inbound_id first — same rule as EffectiveFlow) for many clients in one query, keyed by email. Emails absent from the result carry no flow anywhere. Batched so flow restoration on an inbound with many clients is O(1) queries instead of O(clients). Used to restore a stripped flow onto an inbound that has just become flow-eligible.

func (*ClientService) EmailsByGroup

func (s *ClientService) EmailsByGroup(name string) ([]string, error)

func (*ClientService) EnforceHwidForSubID

func (s *ClientService) EnforceHwidForSubID(subID string, req HwidRequest) (HwidGateResult, error)

func (*ClientService) ExportAll

func (s *ClientService) ExportAll() ([]ClientCreatePayload, error)

ExportAll returns every client in the same {client, inboundIds} shape that /add and /bulkCreate accept, so an exported file round-trips straight back through Import. Clients with no inbound attachment are included with an empty inboundIds list so an export taken before DeleteOrphans can restore them.

func (*ClientService) ExportHiddifyBackup added in v3.9.7

func (s *ClientService) ExportHiddifyBackup(settingSvc *SettingService) ([]byte, HiddifyExportStats, error)

ExportHiddifyBackup creates a minimal Hiddify-compatible backup containing panel clients plus the currently preferred legacy proxy_path_client alias. It deliberately does not export unrelated panel settings or credentials.

func (*ClientService) GetByID

func (s *ClientService) GetByID(id int) (*model.ClientRecord, error)

func (*ClientService) GetClientsByTrafficReset

func (s *ClientService) GetClientsByTrafficReset(period string) ([]ClientResetCycle, error)

GetClientsByTrafficReset returns the clients whose own reset cycle matches the period, independent of the cycle configured on the inbounds they belong to.

func (*ClientService) GetExternalLinksForRecord

func (s *ClientService) GetExternalLinksForRecord(id int) ([]model.ClientExternalLink, error)

func (*ClientService) GetInboundIdsForEmail

func (s *ClientService) GetInboundIdsForEmail(tx *gorm.DB, email string) ([]int, error)

func (*ClientService) GetInboundIdsForRecord

func (s *ClientService) GetInboundIdsForRecord(id int) ([]int, error)

func (*ClientService) GetRecordByEmail

func (s *ClientService) GetRecordByEmail(tx *gorm.DB, email string) (*model.ClientRecord, error)

func (*ClientService) GetRecordsByTgID

func (s *ClientService) GetRecordsByTgID(tgId int64) ([]*model.ClientRecord, error)

func (*ClientService) HasPendingNode

func (s *ClientService) HasPendingNode(inboundSvc *InboundService, email string) bool

func (*ClientService) HwidSlotStatusForSubID

func (s *ClientService) HwidSlotStatusForSubID(subID string) (status HwidSlotStatus, found bool, err error)

HwidSlotStatusForSubID is SELECT-only: it must never write client_hwids or last_seen. Enabled-clients scope mirrors the gate, so limit == limit enforced.

func (*ClientService) ImportClients

func (s *ClientService) ImportClients(inboundSvc *InboundService, items []ClientCreatePayload) (BulkCreateResult, bool, error)

ImportClients recreates clients from an exported list. Items that carry inboundIds go through the normal BulkCreate path (added to every inbound and pushed to xray); items with no inboundIds are restored as bare records so an orphan-inclusive export round-trips. Existing emails are never overwritten — they are reported in Skipped. The boolean reports whether xray needs a restart.

func (*ClientService) List

func (s *ClientService) List() ([]ClientWithAttachments, error)

func (*ClientService) ListClientHwids

func (s *ClientService) ListClientHwids(email string) ([]ClientHwidInfo, error)

func (*ClientService) ListForInbound

func (s *ClientService) ListForInbound(tx *gorm.DB, inboundId int) ([]model.Client, error)

func (*ClientService) ListForInboundBySubId

func (s *ClientService) ListForInboundBySubId(tx *gorm.DB, inboundId int, subId string) ([]model.Client, error)

ListForInboundBySubId is ListForInbound narrowed to one subscription id — both filter columns are indexed, so the subscription server resolves a subscriber's clients without touching the inbound's settings JSON.

func (*ClientService) ListForInbounds added in v3.9.5

func (s *ClientService) ListForInbounds(tx *gorm.DB, inboundIDs []int) (map[int][]model.Client, error)

ListForInbounds loads linked clients in batches for config generation.

func (*ClientService) ListForInboundsBySubId added in v3.9.5

func (s *ClientService) ListForInboundsBySubId(tx *gorm.DB, inboundIDs []int, subId string) (map[int][]model.Client, error)

ListForInboundsBySubId loads only one subscriber's linked clients in batches.

func (*ClientService) ListGroups

func (s *ClientService) ListGroups() ([]GroupSummary, error)

func (*ClientService) ListPaged

func (s *ClientService) ListPaged(inboundSvc *InboundService, settingSvc *SettingService, params ClientPageParams) (*ClientPageResponse, error)

ListPaged returns one page of clients together with the counts the clients page header needs. Every predicate runs in SQL, so the cost tracks the page size rather than the number of clients on the panel.

func (*ClientService) ReapSyncOrphans

func (s *ClientService) ReapSyncOrphans() (int, error)

ReapSyncOrphans deletes the clients the node-snapshot sweep marked and no later merge reclaimed. It is the only path that hard-deletes for that sweep.

func (*ClientService) RemoveFromGroup

func (s *ClientService) RemoveFromGroup(emails []string) (int, error)

func (*ClientService) RenameGroup

func (s *ClientService) RenameGroup(oldName, newName string) (int, error)

func (*ClientService) ResetAllClientTraffics

func (s *ClientService) ResetAllClientTraffics(inboundSvc *InboundService, id int) error

func (*ClientService) ResetAllTraffics

func (s *ClientService) ResetAllTraffics() (bool, error)

func (*ClientService) ResetClientExpiryTimeByEmail

func (s *ClientService) ResetClientExpiryTimeByEmail(inboundSvc *InboundService, clientEmail string, expiry_time int64) (bool, error)

func (*ClientService) ResetClientIpLimitByEmail

func (s *ClientService) ResetClientIpLimitByEmail(inboundSvc *InboundService, clientEmail string, count int) (bool, error)

func (*ClientService) ResetClientTrafficLimitByEmail

func (s *ClientService) ResetClientTrafficLimitByEmail(inboundSvc *InboundService, clientEmail string, totalGB int) (bool, error)

func (*ClientService) ResetGroupTraffic

func (s *ClientService) ResetGroupTraffic(name string) error

func (*ClientService) ResetTrafficByEmail

func (s *ClientService) ResetTrafficByEmail(inboundSvc *InboundService, email string) (bool, error)

func (*ClientService) SetClientEnableByEmail

func (s *ClientService) SetClientEnableByEmail(inboundSvc *InboundService, clientEmail string, enable bool) (bool, bool, error)

func (*ClientService) SetClientTelegramUserID

func (s *ClientService) SetClientTelegramUserID(inboundSvc *InboundService, trafficId int, tgId int64) (bool, error)

func (*ClientService) SetExternalLinksByEmail

func (s *ClientService) SetExternalLinksByEmail(email string, inputs []ExternalLinkInput) error

func (*ClientService) SetExternalLinksForRecord

func (s *ClientService) SetExternalLinksForRecord(id int, inputs []ExternalLinkInput) error

SetExternalLinksForRecord replaces a client's entire external-link set.

func (*ClientService) SyncInbound

func (s *ClientService) SyncInbound(tx *gorm.DB, inboundId int, clients []model.Client) error

SyncInbound makes the inbound's client records and links match clients exactly: links for clients no longer in the set are removed.

func (*ClientService) ToggleClientEnableByEmail

func (s *ClientService) ToggleClientEnableByEmail(inboundSvc *InboundService, clientEmail string) (bool, bool, error)

func (*ClientService) TunnelAllowedIPsByInbound

func (s *ClientService) TunnelAllowedIPsByInbound(inboundSvc *InboundService, email string, inboundIds []int) (map[int]string, error)

TunnelAllowedIPsByInbound returns, for each given WireGuard/AmneziaWG inbound id, the real AllowedIPs this email currently has on that specific inbound's own settings JSON -- joined comma-separated, matching the form value shape a single AllowedIPs field already uses. Non-tunnel inbounds and ids the email isn't actually attached to are simply absent from the result (not an error): callers use this to seed a per-protocol display field, and ClientRecord's own single AllowedIPs column can't tell two different protocol addresses apart, which is exactly the gap this closes.

func (*ClientService) Update

func (s *ClientService) Update(inboundSvc *InboundService, id int, updated model.Client, limitHwid int, inboundFilter ...int) (bool, error)

func (*ClientService) UpdateByEmail

func (s *ClientService) UpdateByEmail(inboundSvc *InboundService, email string, updated model.Client, limitHwid int, inboundFilter ...int) (bool, error)

func (*ClientService) UpdateInboundClient

func (s *ClientService) UpdateInboundClient(inboundSvc *InboundService, data *model.Inbound, oldEmail string) (bool, error)

type ClientSlim

type ClientSlim struct {
	Email      string              `json:"email" example:"alice@example.com"`
	SubID      string              `json:"subId" example:"abcd1234"`
	Enable     bool                `json:"enable" example:"true"`
	TotalGB    int64               `json:"totalGB" example:"53687091200"`
	ExpiryTime int64               `json:"expiryTime" example:"1735689600000"`
	LimitIP    int                 `json:"limitIp" example:"0"`
	LimitHwid  int                 `json:"limitHwid" example:"0"`
	Reset      int                 `json:"reset" example:"0"`
	ResetDay   int                 `json:"resetDay" example:"0"`
	ResetMax   int                 `json:"resetMax" example:"0"`
	Group      string              `json:"group,omitempty" example:"staff"`
	Comment    string              `json:"comment,omitempty" example:"Primary device"`
	InboundIds []int               `json:"inboundIds" example:"[3,5]"`
	Traffic    *xray.ClientTraffic `json:"traffic,omitempty"`
	CreatedAt  int64               `json:"createdAt" example:"1735000000000"`
	UpdatedAt  int64               `json:"updatedAt" example:"1735100000000"`
}

ClientSlim is the row-shape used by the clients page. It drops fields the table never reads (UUID, password, auth, flow, security, reverse, tgId) so the list payload stays compact even when the panel manages thousands of clients. Modals that need the full record still call /get/:email.

type ClientWithAttachments

type ClientWithAttachments struct {
	model.ClientRecord
	InboundIds []int               `json:"inboundIds"`
	Traffic    *xray.ClientTraffic `json:"traffic,omitempty"`
}

func (ClientWithAttachments) MarshalJSON

func (c ClientWithAttachments) MarshalJSON() ([]byte, error)

MarshalJSON is required because model.ClientRecord defines its own MarshalJSON. Go promotes the embedded method to the outer struct, so without this the encoder would call ClientRecord.MarshalJSON for the whole value and silently drop InboundIds and Traffic from the API response.

type ClientsSummary

type ClientsSummary struct {
	Total         int      `json:"total" example:"2000"`
	Active        int      `json:"active" example:"1850"`
	OnlineCount   int      `json:"onlineCount" example:"1"`
	DepletedCount int      `json:"depletedCount" example:"0"`
	ExpiringCount int      `json:"expiringCount" example:"0"`
	DeactiveCount int      `json:"deactiveCount" example:"150"`
	Online        []string `json:"online" example:"[\"alice@example.com\"]"`
	Depleted      []string `json:"depleted" example:"[]"`
	Expiring      []string `json:"expiring" example:"[]"`
	Deactive      []string `json:"deactive" example:"[\"bob@example.com\"]"`
}

ClientsSummary collects per-bucket counts plus the matching email lists so the clients page can render the dashboard stat cards and their hover popovers without shipping the full client array. The counters are exact; the lists stop at clientSummaryEmailCap entries and only back the popovers.

type CopyClientsResult

type CopyClientsResult struct {
	Added   []string `json:"added"`
	Skipped []string `json:"skipped"`
	Errors  []string `json:"errors"`
}

type ExternalLinkInput

type ExternalLinkInput struct {
	Kind       string `json:"kind"`
	Value      string `json:"value"`
	Remark     string `json:"remark"`
	Enable     *bool  `json:"enable"`
	ExpiryTime int64  `json:"expiryTime"`
	NamePrefix string `json:"namePrefix"`
}

ExternalLinkInput is one row from the client form's Links tab.

type Fail2banStatus

type Fail2banStatus struct {
	Enabled   bool `json:"enabled"`
	Installed bool `json:"installed"`
	Usable    bool `json:"usable"`
	Windows   bool `json:"windows"`
}

Fail2banStatus tells the frontend whether the per-client IP limit can actually be enforced. Enforcement depends on fail2ban, so a limit set without it would silently do nothing.

type FallbackInput

type FallbackInput struct {
	ChildId   int    `json:"childId"`
	Name      string `json:"name"`
	Alpn      string `json:"alpn"`
	Path      string `json:"path"`
	Dest      string `json:"dest"`
	Xver      int    `json:"xver"`
	SortOrder int    `json:"sortOrder"`
}

FallbackInput is the payload shape POSTed by the inbound form.

type FallbackService

type FallbackService struct{}

func (*FallbackService) BuildFallbacksJSON

func (s *FallbackService) BuildFallbacksJSON(tx *gorm.DB, masterId int) ([]map[string]any, error)

func (*FallbackService) GetByMaster

func (s *FallbackService) GetByMaster(masterId int) ([]model.InboundFallback, error)

GetByMaster returns every fallback rule attached to the master inbound.

func (*FallbackService) GetParentForChild

func (s *FallbackService) GetParentForChild(childId int) (*model.InboundFallback, error)

GetParentForChild finds the first fallback rule that points at childId. Used by client-link generation: when a child inbound is attached as a fallback, its client links should advertise the master's address+port and TLS instead of the child's loopback listen.

func (*FallbackService) SetByMaster

func (s *FallbackService) SetByMaster(masterId int, items []FallbackInput) error

SetByMaster replaces the master's entire fallback list atomically.

type FirewallManagedStatus added in v3.9.7

type FirewallManagedStatus struct {
	Supported   bool                     `json:"supported"`
	Backend     string                   `json:"backend"`
	Enabled     bool                     `json:"enabled"`
	AutoSync    bool                     `json:"autoSync"`
	Rules       []FirewallRule           `json:"rules"`
	ManualRules []FirewallManualRuleView `json:"manualRules"`
	Message     string                   `json:"message,omitempty"`
}

type FirewallManualRule added in v3.9.7

type FirewallManualRule struct {
	Port     int    `json:"port"`
	Protocol string `json:"protocol"`
}

type FirewallManualRuleView added in v3.9.7

type FirewallManualRuleView struct {
	Port     int    `json:"port"`
	Protocol string `json:"protocol"`
	Label    string `json:"label,omitempty"`
}

type FirewallRule added in v3.9.7

type FirewallRule struct {
	Port      int    `json:"port,omitempty"`
	PortRange string `json:"portRange,omitempty"`
	Protocol  string `json:"protocol"`
	Source    string `json:"source"`
	Label     string `json:"label"`
	Owned     bool   `json:"owned"`
	Exists    bool   `json:"exists"`
}

type FirewallService added in v3.9.7

type FirewallService struct{}

func (*FirewallService) AddManagedManualRule added in v3.9.7

func (s *FirewallService) AddManagedManualRule(ctx context.Context, port int, protocol, label string, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) AddManagedManualRuleSafe added in v3.9.7

func (s *FirewallService) AddManagedManualRuleSafe(ctx context.Context, port int, protocol, label string, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) AddManualRule added in v3.9.7

func (s *FirewallService) AddManualRule(ctx context.Context, port int, protocol string, safetyPort int) (FirewallStatus, error)

func (*FirewallService) DeleteManagedManualRule added in v3.9.7

func (s *FirewallService) DeleteManagedManualRule(ctx context.Context, port int, protocol string, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) DeleteManagedManualRuleSafe added in v3.9.7

func (s *FirewallService) DeleteManagedManualRuleSafe(ctx context.Context, port int, protocol string, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) DeleteManualRule added in v3.9.7

func (s *FirewallService) DeleteManualRule(ctx context.Context, port int, protocol string, safetyPort int) (FirewallStatus, error)

func (*FirewallService) GetManagedStatus added in v3.9.7

func (s *FirewallService) GetManagedStatus(ctx context.Context, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) GetManagedStatusSafe added in v3.9.7

func (s *FirewallService) GetManagedStatusSafe(ctx context.Context, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) GetStatus added in v3.9.7

func (s *FirewallService) GetStatus(ctx context.Context, safetyPort int) (FirewallStatus, error)

func (*FirewallService) ManagedPingEnabled added in v3.9.7

func (s *FirewallService) ManagedPingEnabled() (bool, error)

func (*FirewallService) MarkControlInitialized added in v3.9.7

func (s *FirewallService) MarkControlInitialized() error

func (*FirewallService) MigrateManagedBackendIfNeeded added in v3.9.7

func (s *FirewallService) MigrateManagedBackendIfNeeded(ctx context.Context) error

MigrateManagedBackendIfNeeded removes a 3x-ui-owned native nftables/iptables layer when an administrator subsequently enables UFW or firewalld. Running both filtering layers at once could make the stricter stale layer override the newly enabled system firewall.

func (*FirewallService) ReconcileManagedPingState added in v3.9.7

func (s *FirewallService) ReconcileManagedPingState(ctx context.Context, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) RememberSafetyPort added in v3.9.7

func (s *FirewallService) RememberSafetyPort(port int) error

RememberSafetyPort persists the externally visible panel port discovered on an authenticated firewall-management request. Do not replace a previously learned external reverse-proxy port with the panel's internal listen port: doing so would let the next background reconcile close the public entrypoint.

func (*FirewallService) SetAutoSync added in v3.9.7

func (s *FirewallService) SetAutoSync(ctx context.Context, enabled bool, safetyPort int) (FirewallStatus, error)

func (*FirewallService) SetAutoSyncPreference added in v3.9.7

func (s *FirewallService) SetAutoSyncPreference(ctx context.Context, enabled bool, safetyPort int) (FirewallStatus, error)

SetAutoSyncPreference changes automatic inbound reconciliation. Turning it off deliberately freezes the rules currently present instead of immediately removing every 3x-ui-owned inbound rule; the operator may still use Sync now or manual rules while automatic reconciliation is disabled.

func (*FirewallService) SetEnabled added in v3.9.7

func (s *FirewallService) SetEnabled(ctx context.Context, enabled bool, safetyPort int) (FirewallStatus, error)

func (*FirewallService) SetManagedAutoSyncPreference added in v3.9.7

func (s *FirewallService) SetManagedAutoSyncPreference(ctx context.Context, enabled bool, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) SetManagedAutoSyncPreferenceSafe added in v3.9.7

func (s *FirewallService) SetManagedAutoSyncPreferenceSafe(ctx context.Context, enabled bool, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) SetManagedEnabled added in v3.9.7

func (s *FirewallService) SetManagedEnabled(ctx context.Context, enabled bool, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) SetManagedEnabledSafe added in v3.9.7

func (s *FirewallService) SetManagedEnabledSafe(ctx context.Context, enabled bool, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) SetManagedPingEnabledSafe added in v3.9.7

func (s *FirewallService) SetManagedPingEnabledSafe(ctx context.Context, enabled bool, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) StartAutoSync added in v3.9.7

func (s *FirewallService) StartAutoSync()

StartAutoSync keeps firewall rules aligned with enabled local inbounds even when changes arrive through imports, API calls, or node synchronization. Inbound API mutations trigger an immediate coalesced reconcile; a periodic five-second pass remains as drift repair for imports and future mutation paths and keeps the managed iptables jump behind newly added admin rules.

func (*FirewallService) Sync added in v3.9.7

func (s *FirewallService) Sync(ctx context.Context, safetyPort int) (FirewallStatus, error)

func (*FirewallService) SyncIfEnabled added in v3.9.7

func (s *FirewallService) SyncIfEnabled(ctx context.Context) error

func (*FirewallService) SyncManaged added in v3.9.7

func (s *FirewallService) SyncManaged(ctx context.Context, safetyPort int) (FirewallManagedStatus, error)

func (*FirewallService) SyncManagedSafe added in v3.9.7

func (s *FirewallService) SyncManagedSafe(ctx context.Context, safetyPort int) (FirewallManagedStatus, error)

type FirewallStatus added in v3.9.7

type FirewallStatus struct {
	Supported   bool                 `json:"supported"`
	Backend     string               `json:"backend"`
	Enabled     bool                 `json:"enabled"`
	AutoSync    bool                 `json:"autoSync"`
	Rules       []FirewallRule       `json:"rules"`
	ManualRules []FirewallManualRule `json:"manualRules"`
	Message     string               `json:"message,omitempty"`
}

type GeodataService

type GeodataService struct{}

GeodataService browses the geosite/geoip databases Xray resolves its geosite:/geoip: routing tokens against.

func (*GeodataService) Categories

func (s *GeodataService) Categories(file, query string, offset, limit int) (geodata.GeoCategoryPage, error)

Categories returns one page of a database's categories.

func (*GeodataService) Entries

func (s *GeodataService) Entries(file, code, query string, offset, limit int) (geodata.GeoEntryPage, error)

Entries returns one page of the rules inside a category.

func (*GeodataService) Files

func (s *GeodataService) Files() ([]geodata.GeoFile, error)

Files lists the databases available in the Xray asset folder.

func (*GeodataService) Validate

func (s *GeodataService) Validate(isIP bool, tokens []string) []GeodataTokenIssue

Validate reports which of the given routing tokens do not resolve against the databases on disk. Plain domains and CIDRs are left alone — only tokens that name a database are looked up.

type GeodataSource

type GeodataSource struct {
	URL  string `json:"url"`
	File string `json:"file"`
}

GeodataSource identifies a file Xray downloads through its geodata configuration.

func StandardGeodataSources

func StandardGeodataSources() []GeodataSource

StandardGeodataSources derives the panel presets from the geofile update allowlist.

type GeodataTokenIssue

type GeodataTokenIssue struct {
	Token  string `json:"token" example:"geosite:blabla"`
	Reason string `json:"reason" example:"categoryMissing"`
	File   string `json:"file,omitempty" example:"geosite.dat"`
	Code   string `json:"code,omitempty" example:"blabla"`
}

GeodataTokenIssue reports a routing token the running core would reject, or would silently match nothing against.

type GroupSummary

type GroupSummary struct {
	Name        string `json:"name"`
	ClientCount int    `json:"clientCount"`
	TrafficUsed int64  `json:"trafficUsed"`
	Up          int64  `json:"up"`
	Down        int64  `json:"down"`
}

type HappLinkGenerator

type HappLinkGenerator interface {
	Generate(context.Context, int, string) (HappLinkResult, error)
}

type HappLinkResult

type HappLinkResult struct {
	EncryptedLink string `json:"encryptedLink" example:"happ://crypt5/example"`
}

type HappService

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

HappService generates one local encrypted link per action and does not retain results.

func NewHappService

func NewHappService(clientService *ClientService, settingService *SettingService) *HappService

func (*HappService) Generate

func (s *HappService) Generate(ctx context.Context, clientID int, host string) (HappLinkResult, error)

type HeartbeatPatch

type HeartbeatPatch struct {
	Status           string
	LastHeartbeat    int64
	LatencyMs        int
	XrayVersion      string
	SingBoxVersion   string
	SingBoxInstalled bool
	SingBoxKnown     bool
	CoreType         string
	RunningCore      string
	PanelVersion     string
	Guid             string
	CpuPct           float64
	MemPct           float64
	UptimeSecs       uint64
	// NetUp/NetDown are the node's current interface throughput (bytes/sec),
	// summed over non-virtual interfaces, read from its status response.
	NetUp     uint64
	NetDown   uint64
	LastError string
	// XrayState and XrayError come from the remote /panel/api/server/status when the
	// panel API is reachable. They allow distinguishing panel connectivity from
	// Xray core health on the node.
	XrayState    string
	XrayError    string
	SingBoxState string
	SingBoxError string
}

func (HeartbeatPatch) ToUI

func (p HeartbeatPatch) ToUI(ok bool) ProbeResultUI

type HiddifyBackup added in v3.9.6

type HiddifyBackup struct {
	Users   []hiddifyUser `json:"users"`
	Proxies []struct {
		Enable    bool   `json:"enable"`
		Proto     string `json:"proto"`
		Transport string `json:"transport"`
		L3        string `json:"l3"`
		CDN       string `json:"cdn"`
	} `json:"proxies"`
	Domains  []hiddifyDomain `json:"domains"`
	HConfigs []hiddifyConfig `json:"hconfigs"`
	// contains filtered or unexported fields
}

HiddifyBackup is the legacy JSON export produced by Hiddify Panel. Only the users section is required for migration. Other sections are optional because Hiddify backups/restores may contain selected groups only.

func (*HiddifyBackup) HiddifyClients added in v3.9.6

func (b *HiddifyBackup) HiddifyClients() ([]ClientCreatePayload, error)

HiddifyClients preserves each user's UUID as the VPN credential and sub ID. Used traffic is subtracted from the allowance because Hiddify's counter is per user, while the new traffic records start at zero.

func (*HiddifyBackup) HiddifyClientsSelected added in v3.9.7

func (b *HiddifyBackup) HiddifyClientsSelected(selected []string) ([]ClientCreatePayload, error)

HiddifyClientsSelected maps only the chosen UUIDs. A nil selection preserves the old API behavior (all users); an explicit empty selection is rejected.

func (*HiddifyBackup) HiddifyLegacySubscriptionAlias added in v3.9.6

func (b *HiddifyBackup) HiddifyLegacySubscriptionAlias() (HiddifyLegacySubscriptionAlias, error)

HiddifyLegacySubscriptionAlias returns the public path used by Hiddify for links like https://domain/<proxy_path_client>/<UUID>/. Domains are retained only as migration metadata; authorization of a legacy URL is path/UUID based.

func (*HiddifyBackup) UnmarshalJSON added in v3.9.6

func (b *HiddifyBackup) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts both Hiddify's common [{key,value}] settings export and backups that serialize hconfigs as a plain object. The legacy URL path is discovered separately from the complete backup.

type HiddifyExportStats added in v3.9.7

type HiddifyExportStats struct {
	Exported int
	Skipped  int
}

type HiddifyLegacySubscriptionAlias added in v3.9.6

type HiddifyLegacySubscriptionAlias struct {
	Path    string   `json:"path"`
	Domains []string `json:"domains,omitempty"`
}

type HiddifyPreview added in v3.9.6

type HiddifyPreview struct {
	Users    int                  `json:"users"`
	UserList []HiddifyPreviewUser `json:"userList"`
	Warnings []string             `json:"warnings"`
}

type HiddifyPreviewUser added in v3.9.7

type HiddifyPreviewUser struct {
	UUID    string `json:"uuid"`
	Name    string `json:"name"`
	Comment string `json:"comment,omitempty"`
}

type HostService

type HostService struct{}

func (*HostService) AddHostGroup

func (s *HostService) AddHostGroup(req *entity.HostGroup) ([]*model.Host, error)

func (*HostService) DeleteHostGroup

func (s *HostService) DeleteHostGroup(groupId string) error

func (*HostService) DeleteHostsGroup

func (s *HostService) DeleteHostsGroup(groupIds []string) error

func (*HostService) GetAllTags

func (s *HostService) GetAllTags() ([]string, error)

func (*HostService) GetHostGroup

func (s *HostService) GetHostGroup(groupId string) (*entity.HostGroup, error)

func (*HostService) GetHosts

func (s *HostService) GetHosts() ([]*entity.HostGroup, error)

func (*HostService) GetHostsByInbound

func (s *HostService) GetHostsByInbound(inboundId int) ([]*entity.HostGroup, error)

func (*HostService) ReorderHostGroups

func (s *HostService) ReorderHostGroups(groupIds []string) error

func (*HostService) SetHostGroupEnable

func (s *HostService) SetHostGroupEnable(groupId string, enable bool) error

func (*HostService) SetHostsGroupEnable

func (s *HostService) SetHostsGroupEnable(groupIds []string, enable bool) error

func (*HostService) UpdateHostGroup

func (s *HostService) UpdateHostGroup(groupId string, req *entity.HostGroup) ([]*model.Host, error)

type HwidGateResult

type HwidGateResult struct {
	Allowed           bool
	Active            bool
	NotSupported      bool
	MaxDevicesReached bool
	LimitReached      bool
	Limit             int
	Registered        int
}

type HwidRequest

type HwidRequest struct {
	Hwid        string
	UserAgent   string
	DeviceOS    string
	OsVersion   string
	DeviceModel string
}

type HwidSlotStatus

type HwidSlotStatus struct {
	Active     bool `json:"active" example:"true"`
	Limit      int  `json:"limit" example:"2"`
	Registered int  `json:"registered" example:"1"`
	Remaining  int  `json:"remaining" example:"1"`
	Full       bool `json:"full" example:"false"`
}

HwidSlotStatus is the aggregate device-slot view exposed to subscribers: counters only, no hwid value or hash, no email, no device metadata.

type InboundOption

type InboundOption struct {
	Id             int    `json:"id" example:"1"`
	Remark         string `json:"remark" example:"VLESS-443"`
	Tag            string `json:"tag" example:"in-443-tcp"`
	Protocol       string `json:"protocol" example:"vless"`
	Port           int    `json:"port" example:"443"`
	Enable         bool   `json:"enable" example:"true"`
	Network        string `json:"network,omitempty"`
	Security       string `json:"security,omitempty"`
	TlsFlowCapable bool   `json:"tlsFlowCapable" example:"true"`
	SsMethod       string `json:"ssMethod"`
	WgPublicKey    string `json:"wgPublicKey,omitempty"`
	WgMtu          int    `json:"wgMtu,omitempty"`
	WgDns          string `json:"wgDns,omitempty"`
	MtprotoDomain  string `json:"mtprotoDomain,omitempty"`
	// AwgServer carries the full AmneziaWG server block (keys, subnet,
	// obfuscation params) so the clients page can render a downloadable
	// per-client .conf without a second round trip.
	AwgServer  *amneziawg.ServerSettings `json:"awgServer,omitempty"`
	TuicServer *tuic.TuicServerSettings  `json:"tuicServer,omitempty"`
	// Hosting node; nil for this panel's own inbounds. Lets the clients
	// page map a node filter onto inbound IDs (#4997).
	NodeId *int `json:"nodeId,omitempty"`
	// Share-host resolution inputs, mirroring the subscription's
	// resolveInboundAddress so the clients page renders a node-managed WireGuard
	// Endpoint that points at the node, not the master panel. NodeAddress is the
	// hosting node's externally reachable address (empty for this panel's own
	// inbounds); Listen and ShareAddrStrategy/ShareAddr feed the same
	// node→listen→custom fallback the share/QR links already use.
	NodeAddress       string `json:"nodeAddress,omitempty"`
	Listen            string `json:"listen,omitempty"`
	ShareAddr         string `json:"shareAddr,omitempty"`
	ShareAddrStrategy string `json:"shareAddrStrategy,omitempty"`
}

type InboundService

type InboundService struct {

	// FromNodeSync marks a master push: the row was validated where the operator
	// acted, and a node that refuses it only falls out of sync.
	FromNodeSync bool
	// contains filtered or unexported fields
}

func (*InboundService) AcceptGlobalTraffic

func (s *InboundService) AcceptGlobalTraffic(masterGuid string, traffics []*xray.ClientTraffic) error

AcceptGlobalTraffic ingests a master panel's aggregated per-client usage into client_global_traffics, keyed by (masterGuid, email). The numbers are display/enforcement inputs only — client_traffics keeps this panel's local-only counters, so the pushing master's (and any other master's) delta accounting over our snapshot stays correct.

Rows are overwritten, not max-merged: a reset on the master propagates here on its next push. Emails this panel doesn't host are dropped.

func (*InboundService) AddClientStat

func (s *InboundService) AddClientStat(tx *gorm.DB, inboundId int, client *model.Client) error

AddClientStat inserts a per-client accounting row, or refreshes the config-derived columns on an email conflict. Xray reports traffic per email, so the surviving row also acts as the shared accumulator for inbounds that re-use the same identity — every call for that identity (one per attached inbound) carries the same enable/expiry/reset/total, so re-asserting them here is idempotent for that legitimate case.

The conflict path matters on its own for a second reason: an inbound delete detaches its clients (InboundService.DelInbound) without deleting their client_traffics row, by design — mirroring ClientService.Detach, which intentionally leaves a fully-detached client's row in place so a later Attach can resume it with its accumulated traffic intact. If that same email is instead reused for a freshly (re)created client, the new config's enable/expiry/reset/total must win over whatever the orphaned row still holds; DoNothing left them stale indefinitely (#5958).

up/down are deliberately excluded from the refresh: they are the accumulated traffic totals, and zeroing them here would erase real usage every time an existing, actively-used client is attached to one more inbound. One tradeoff this does not resolve: a genuinely new client that happens to reuse an orphaned email still inherits that row's leftover up/down, since nothing at this call site can tell the two cases apart.

func (*InboundService) AddInbound

func (s *InboundService) AddInbound(inbound *model.Inbound) (*model.Inbound, bool, error)

AddInbound creates a new inbound configuration. It validates port uniqueness, client email uniqueness, and required fields, then saves the inbound to the database and optionally adds it to the running Xray instance. Returns the created inbound, whether Xray needs restart, and any error.

func (*InboundService) AddInboundPeer added in v3.9.2

func (s *InboundService) AddInboundPeer(data *model.Inbound) (bool, error)

func (*InboundService) AddTraffic

func (s *InboundService) AddTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) (needRestart bool, clientsDisabled bool, err error)

func (*InboundService) AddVKTurnProxyClient added in v3.9.2

func (s *InboundService) AddVKTurnProxyClient(data *model.Inbound) (bool, error)

func (*InboundService) AddVKTurnProxyClientDirect added in v3.9.2

func (s *InboundService) AddVKTurnProxyClientDirect(inboundID int, client *VKTurnProxyClient, requestHost string) (*VKTurnProxyClientCreateResult, bool, error)

func (*InboundService) AnyNodePending

func (s *InboundService) AnyNodePending(inboundIds []int) bool

func (*InboundService) BuildVKTurnProxyInboundTraffics added in v3.9.2

func (s *InboundService) BuildVKTurnProxyInboundTraffics(clientTraffics []*xray.ClientTraffic) ([]*xray.Traffic, error)

BuildVKTurnProxyInboundTraffics keeps VK TURN inbound totals visible in the existing traffic UI. It deliberately does not consume or modify any WireGuard-specific statistics API.

func (*InboundService) BumpClientsLastOnline

func (s *InboundService) BumpClientsLastOnline(emails []string) error

BumpClientsLastOnline sets client_traffics.last_online to now for the given emails. Used in online-API mode for clients that hold a live connection but moved no bytes this poll — the traffic path (addClientTraffic) only bumps last_online on a non-zero delta, so idle-but-connected clients would otherwise show a stale "last online" while being reported online.

func (*InboundService) ClearClientIps

func (s *InboundService) ClearClientIps(clientEmail string) error

func (*InboundService) ClearNodeOnlineClients

func (s *InboundService) ClearNodeOnlineClients(nodeID int)

func (*InboundService) CommitXrayTraffic added in v3.9.7

func (s *InboundService) CommitXrayTraffic(inboundTraffics []*xray.Traffic, clientTraffics []*xray.ClientTraffic) error

CommitXrayTraffic persists inbound, client and outbound traffic in one DB transaction, giving one durable acknowledgement point for the Xray delta.

func (*InboundService) CopyInboundClients

func (s *InboundService) CopyInboundClients(targetInboundID int, sourceInboundID int, clientEmails []string, flow string) (*CopyClientsResult, bool, error)

func (*InboundService) CountClientTraffics

func (s *InboundService) CountClientTraffics() (int64, error)

func (*InboundService) CreateVKTurnProxyManagedClientConfig added in v3.9.2

func (s *InboundService) CreateVKTurnProxyManagedClientConfig(inboundTag, clientID, clientName string) (*VKTurnProxyManagedWGConfig, error)

CreateVKTurnProxyManagedClientConfig adds (or reuses) a managed client on the vk-turn-proxy inbound with the given tag and returns its auto-provisioned wg peer plus the forward wireguard inbound's server key. This is the embedded (3x-ui-hosted relay) counterpart of CreateWireguardClient: instead of a raw wg peer, the client becomes a first-class VK TURN inbound entry. An empty tag selects the first vk-turn-proxy inbound.

func (*InboundService) DelClientIPs

func (s *InboundService) DelClientIPs(tx *gorm.DB, email string) error

func (*InboundService) DelClientStat

func (s *InboundService) DelClientStat(tx *gorm.DB, email string) error

func (*InboundService) DelDepletedClients

func (s *InboundService) DelDepletedClients(id int) (err error)

func (*InboundService) DelInbound

func (s *InboundService) DelInbound(id int) (bool, error)

func (*InboundService) DelInboundPeer added in v3.9.2

func (s *InboundService) DelInboundPeer(inboundId int, peerIndex int) (bool, error)

func (*InboundService) DelInbounds

func (s *InboundService) DelInbounds(ids []int) (BulkDelInboundResult, bool, error)

DelInbounds removes every inbound in the list, reusing the single-delete path per id. Failures are recorded in Skipped and processing continues for the rest; the aggregated needRestart is returned so the caller restarts xray at most once.

func (*InboundService) DelVKTurnProxyClient added in v3.9.2

func (s *InboundService) DelVKTurnProxyClient(inboundID int, clientID string) (bool, error)

func (*InboundService) DeleteNodeClientIpsByGuid

func (s *InboundService) DeleteNodeClientIpsByGuid(guid string) error

DeleteNodeClientIpsByGuid removes all attribution rows for a guid (e.g. when a node is deleted) so its IPs stop being reported and counted.

func (*InboundService) DesiredAmneziaWGInstances

func (s *InboundService) DesiredAmneziaWGInstances() ([]amneziawg.Instance, error)

DesiredAmneziaWGInstances derives the AmneziaWG interfaces this panel should be running: one instance per enabled local AmneziaWG inbound, serving only the peers of clients that are both enabled in the inbound settings and not depletion-disabled in client_traffics. That is the same effective peer set buildInboundForLocalRuntime pushes on interactive edits, so the reconcile job and the push path agree on one fingerprint — see DesiredMtprotoInstances, which this mirrors exactly.

func (*InboundService) DesiredExternalVPNInstances added in v3.9.5

func (s *InboundService) DesiredExternalVPNInstances() ([]externalvpn.Instance, error)

func (*InboundService) DesiredMieruInstances added in v3.9.4

func (s *InboundService) DesiredMieruInstances() ([]mieru.Instance, error)

func (*InboundService) DesiredMtprotoInstances

func (s *InboundService) DesiredMtprotoInstances() ([]mtproto.Instance, error)

DesiredMtprotoInstances derives the mtg sidecar configs this panel should be running: one instance per enabled local mtproto inbound, serving only the secrets of clients that are both enabled in the inbound settings and not depletion-disabled in client_traffics. That is the same effective client set buildInboundForLocalRuntime pushes on interactive edits, so the reconcile job and the push paths agree on one fingerprint — a disagreement would surface as a needless mtg restart, and a job that read only the raw settings would keep serving depleted clients until an unrelated restart. Inbounds whose every secret is filtered away are omitted so Reconcile stops their sidecar.

func (*InboundService) DesiredTuicInstances

func (s *InboundService) DesiredTuicInstances() ([]tuic.Instance, error)

func (*InboundService) EmailsByInbound

func (s *InboundService) EmailsByInbound(inboundId int) ([]string, error)

EmailsByInbound returns the list of client emails currently configured on an inbound's settings.clients[]. Used by the "delete all clients" flow on the inbounds page, which then feeds the list into ClientService.BulkDelete.

func (*InboundService) ExportAllVKTurnProxyClients added in v3.9.2

func (s *InboundService) ExportAllVKTurnProxyClients(inboundID int, requestHost string) ([]VKTurnProxyExportedClient, error)

func (*InboundService) ExportVKTurnProxyClient added in v3.9.2

func (s *InboundService) ExportVKTurnProxyClient(inboundID int, clientID string, requestHost string) (string, error)

func (*InboundService) ExportVKTurnProxyClientForEndpoint added in v3.9.4

func (s *InboundService) ExportVKTurnProxyClientForEndpoint(inboundID int, clientID string, endpointHost string, endpointPort int) (string, error)

ExportVKTurnProxyClientForEndpoint exports one client using an explicit public Host endpoint. Subscription Host rows use this instead of requestHost so the binary TURN config dials the configured address/port rather than the inbound bind or panel host.

func (*InboundService) FilterAndSortClientEmails

func (s *InboundService) FilterAndSortClientEmails(emails []string) ([]string, []string, error)

func (*InboundService) GetActiveClientTraffics

func (s *InboundService) GetActiveClientTraffics(emails []string) ([]*xray.ClientTraffic, error)

func (*InboundService) GetActiveInboundsByGuid

func (s *InboundService) GetActiveInboundsByGuid() map[string][]string

GetActiveInboundsByGuid returns the inbound tags that carried traffic within the grace window, keyed by the panelGuid of the node that physically hosts each inbound. A GUID missing from the map means "don't gate" for that node's inbounds (old-build node or no active-inbound signal).

func (s *InboundService) GetAllClientLinks(host string, email string) ([]string, error)

func (*InboundService) GetAllClientTraffics

func (s *InboundService) GetAllClientTraffics() ([]*xray.ClientTraffic, error)

GetAllClientTraffics returns the full set of client_traffics rows so the websocket broadcasters can ship a complete snapshot every cycle. A pure delta path silently dropped the per-client section whenever no client moved bytes in the cycle or a node sync failed, leaving client rows in the UI stuck at stale numbers — so small installs broadcast this snapshot, and only above the traffic job's snapshot threshold (where the marshaled snapshot would exceed the hub's payload cap and be dropped wholesale) does the job fall back to active-row deltas.

func (*InboundService) GetAllEmails

func (s *InboundService) GetAllEmails() ([]string, error)

func (*InboundService) GetAllInboundClientIps

func (s *InboundService) GetAllInboundClientIps() ([]model.InboundClientIps, error)
func (s *InboundService) GetAllInboundLinks(host string, userId int) ([]string, error)

func (*InboundService) GetAllInbounds

func (s *InboundService) GetAllInbounds() ([]*model.Inbound, error)

GetAllInbounds retrieves all inbounds with client stats.

func (*InboundService) GetAmneziaWGDiagnostics

func (s *InboundService) GetAmneziaWGDiagnostics(id int) (amneziawgnet.Diagnostics, error)

GetAmneziaWGDiagnostics returns a live diagnostics snapshot for inbound id: interface up/down, listen port, and per-client handshake/traffic state, read entirely from data amneziawgnet.Manager already tracks -- gathering it can never itself change anything. Returns an error only when id doesn't name an AmneziaWG inbound at all; an inbound that simply isn't running right now (disabled, no enabled clients, or reconcile hasn't caught up yet) comes back as amneziawgnet.Diagnostics{} (Running=false), not an error, since that's a normal state an admin might specifically be checking for.

func (*InboundService) GetClientByEmail

func (s *InboundService) GetClientByEmail(clientEmail string) (*xray.ClientTraffic, *model.Client, error)

func (*InboundService) GetClientInboundByEmail

func (s *InboundService) GetClientInboundByEmail(email string) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error)

func (*InboundService) GetClientInboundByTrafficID

func (s *InboundService) GetClientInboundByTrafficID(trafficId int) (traffic *xray.ClientTraffic, inbound *model.Inbound, err error)

func (*InboundService) GetClientIpNodeAttribution

func (s *InboundService) GetClientIpNodeAttribution(email string) (map[string]string, error)

GetClientIpNodeAttribution returns, for one client email, a map of IP -> the guid that most recently observed it (within the stale window). Used to label each IP in the panel with the node it is connecting to.

func (*InboundService) GetClientIpsByGuid

func (s *InboundService) GetClientIpsByGuid() (map[string]map[string][]model.ClientIpEntry, error)

GetClientIpsByGuid returns this panel's full attribution subtree (guid -> email -> fresh IPs), dropping stale entries. It is what the clientIpsByGuid endpoint serves to a parent panel.

func (*InboundService) GetClientIpsWithNodes

func (s *InboundService) GetClientIpsWithNodes(email string) ([]ClientIpInfo, error)

GetClientIpsWithNodes returns a client's recorded IPs (from the flat inbound_client_ips display set) annotated with the node each IP is on, using the per-node attribution table. Local IPs (and any IP without attribution) carry an empty Node.

func (*InboundService) GetClientReverseTags

func (s *InboundService) GetClientReverseTags() (string, error)

func (*InboundService) GetClientTrafficByEmail

func (s *InboundService) GetClientTrafficByEmail(email string) (traffic *xray.ClientTraffic, err error)

func (*InboundService) GetClientTrafficTgBot

func (s *InboundService) GetClientTrafficTgBot(tgId int64) ([]*xray.ClientTraffic, error)

func (*InboundService) GetClients

func (s *InboundService) GetClients(inbound *model.Inbound) ([]model.Client, error)

func (*InboundService) GetClientsBySubId

func (s *InboundService) GetClientsBySubId(inboundId int, subId string) ([]model.Client, error)

GetClientsBySubId returns the inbound's clients with the given subscription id, resolved from the normalized clients tables (the same source the running Xray users are built from) instead of parsing the settings JSON blob.

func (*InboundService) GetClientsForInboundsBySubId added in v3.9.5

func (s *InboundService) GetClientsForInboundsBySubId(inboundIDs []int, subId string) (map[int][]model.Client, error)

func (*InboundService) GetClientsLastOnline

func (s *InboundService) GetClientsLastOnline() (map[string]int64, error)

func (*InboundService) GetInbound

func (s *InboundService) GetInbound(id int) (*model.Inbound, error)

func (*InboundService) GetInboundClientIps

func (s *InboundService) GetInboundClientIps(clientEmail string) (string, error)

func (*InboundService) GetInboundDetail

func (s *InboundService) GetInboundDetail(id int) (*model.Inbound, error)

func (*InboundService) GetInboundOptions

func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)

func (*InboundService) GetInboundTags

func (s *InboundService) GetInboundTags() (string, error)

func (*InboundService) GetInbounds

func (s *InboundService) GetInbounds(userId int) ([]*model.Inbound, error)

GetInbounds retrieves all inbounds for a specific user with client stats.

func (*InboundService) GetInboundsByTrafficReset

func (s *InboundService) GetInboundsByTrafficReset(period string) ([]*model.Inbound, error)

func (*InboundService) GetInboundsSlim

func (s *InboundService) GetInboundsSlim(userId int) ([]*model.Inbound, error)

GetInboundsSlim returns the same list of inbounds as GetInbounds but strips every per-client field other than email / enable / comment from settings.clients and skips UUID/SubId enrichment on ClientStats. The inbounds page only needs those three to roll up client counts and render badges, so this trims tens of bytes per client (UUID, password, flow, security, totalGB, expiryTime, limitIp, tgId, ...) which adds up fast on installs with thousands of clients.

Full client data is still available through GET /panel/api/inbounds/get/:id for the edit/info/qr/export/clone flows that need it.

func (*InboundService) GetInboundsTrafficSummary

func (s *InboundService) GetInboundsTrafficSummary() ([]InboundTrafficSummary, error)

func (*InboundService) GetNodeClientTraffics

func (s *InboundService) GetNodeClientTraffics(nodeID int) ([]*xray.ClientTraffic, error)

GetNodeClientTraffics returns this panel's aggregated traffic rows for the clients known to live on the given node (those with a delta baseline) — the payload for Remote.PushGlobalClientTraffics. The rows carry the global overlay so a mid-chain panel forwards the widest view it has seen, not just its own aggregate.

func (*InboundService) GetNodeInboundClientIps

func (s *InboundService) GetNodeInboundClientIps(nodeID int) ([]model.InboundClientIps, error)

GetNodeInboundClientIps returns the IP rows of the clients nodeID hosts: a node's IP-limit job reads no other row, so pushing the rest only made it echo them back.

func (*InboundService) GetNodeInboundTrafficTotals

func (s *InboundService) GetNodeInboundTrafficTotals() (map[string][2]int64, error)

GetNodeInboundTrafficTotals returns the current cumulative up/down for every node-hosted inbound, keyed by tag. The node sync diffs successive snapshots of this to derive per-inbound speed for the dashboard — node inbounds have no local Xray poll to produce live deltas the way local inbounds do.

func (*InboundService) GetOnlineClients

func (s *InboundService) GetOnlineClients() []string

func (*InboundService) GetOnlineClientsByGuid

func (s *InboundService) GetOnlineClientsByGuid() map[string][]string

GetOnlineClientsByGuid returns online emails keyed by the panelGuid of the node that physically hosts each set: this panel's own clients under its own GUID, plus every node in the tree under its GUID (#4983). Replaces the old node-id keying so a client three hops down is attributed to its real node, not the intermediate one it was synced through.

func (s *InboundService) GetSubLinks(host, subId string) ([]string, error)

func (*InboundService) GetVKTurnProxyClientTraffic added in v3.9.2

func (s *InboundService) GetVKTurnProxyClientTraffic(inboundID int, clientID string) (*xray.ClientTraffic, error)

func (*InboundService) GetVKTurnProxyPeerOptions added in v3.9.2

func (s *InboundService) GetVKTurnProxyPeerOptions(inboundID int) (*VKTurnProxyPeerOptionsResponse, error)

func (*InboundService) ListClientsForInbound

func (s *InboundService) ListClientsForInbound(inboundId int) ([]model.Client, error)

ListClientsForInbound returns every client attached to the inbound from the normalized clients tables — the same source the running Xray config uses — instead of parsing the embedded settings JSON, which can hold a stale UUID after a client identity change (#6436).

func (*InboundService) MergeClientIpsByGuid

func (s *InboundService) MergeClientIpsByGuid(node *model.Node, trees map[string]map[string][]model.ClientIpEntry) error

MergeClientIpsByGuid folds a node's guid-keyed attribution report (its own panelGuid subtree plus any descendants) into the local table, preserving which physical node each IP is on across a chain. When node is non-nil and its own panelGuid is ambiguous (shared with another node or the master — a cloned server), the node's own subtree is remapped to its node-unique key so two clones don't collapse into one attribution row; descendant subtrees keep their distinct GUIDs. A nil node merges the report verbatim.

func (*InboundService) MergeInboundClientIps

func (s *InboundService) MergeInboundClientIps(incomingIps []model.InboundClientIps) error

MergeInboundClientIps folds client IPs synced from another node into the local inbound_client_ips table without double-counting an IP seen on multiple nodes and without resurrecting stale entries. Existing rows are updated in place; brand-new clients (typically node-only clients with no local row) are created with a fresh local id.

func (*InboundService) MigrateDB

func (s *InboundService) MigrateDB()

func (*InboundService) MigrationBackfillVKTurnProxyClientTraffics added in v3.9.2

func (s *InboundService) MigrationBackfillVKTurnProxyClientTraffics()

func (*InboundService) MigrationRemoveOrphanedTraffics

func (s *InboundService) MigrationRemoveOrphanedTraffics()

func (*InboundService) MigrationRequirements

func (s *InboundService) MigrationRequirements() (err error)

func (*InboundService) MigrationRestoreVisionFlow

func (s *InboundService) MigrationRestoreVisionFlow()

MigrationRestoreVisionFlow repairs VLESS inbounds whose clients lost their XTLS Vision flow because the inbound was not flow-eligible when the client was written (e.g. an XHTTP inbound whose vlessenc encryption was enabled only later). For each now-eligible inbound it restores flow=xtls-rprx-vision on clients whose intended flow (their flow_override on a sibling inbound) is Vision. Idempotent: once a client carries the flow it is skipped, so this is a no-op on healthy installs and on subsequent boots.

func (*InboundService) NodeIsPending

func (s *InboundService) NodeIsPending(nodeID *int) bool

func (*InboundService) PruneStaleClientIps

func (s *InboundService) PruneStaleClientIps() error

PruneStaleClientIps enforces clientIpStaleAfterSeconds for rows the online scan no longer rewrites: an offline client's addresses must still expire.

func (*InboundService) ReconcileNode

func (s *InboundService) ReconcileNode(ctx context.Context, rt *runtime.Remote, n *model.Node) error

ReconcileNode pushes every inbound and sweeps undesired remote tags even when individual operations fail, returning the failures joined: one inbound the node rejects (e.g. a legacy protocol failing validation, #5685) must not stall the rest of the node's config — or, via syncOne, its traffic sync.

func (*InboundService) ReconcileVKTurnProxyClientPeers added in v3.9.2

func (s *InboundService) ReconcileVKTurnProxyClientPeers() error

ReconcileVKTurnProxyClientPeers brings each managed client peer in the target wireguard inbound in line with the client's EFFECTIVE active state: the inbound is enabled, the client is enabled in settings, and its traffic row is still enabled (not depleted/expired). Depleted or expired clients have their peer pulled so traffic limits and expiry actually cut access; clients that become active again (e.g. after a traffic reset) get their peer restored. The wireguard inbound is only rewritten when the peer set actually changes, so the 5s reconcile loop stays a no-op in steady state.

func (*InboundService) RecordLocalClientIps

func (s *InboundService) RecordLocalClientIps(panelGuid string, observed map[string][]model.ClientIpEntry) error

RecordLocalClientIps stores this panel's own Xray observations under its panelGuid. Called by check_client_ip_job each scan with the live per-email IPs the local core reported.

func (*InboundService) RefreshLocalOnlineClients

func (s *InboundService) RefreshLocalOnlineClients(activeEmails, activeInboundTags []string)

RefreshLocalOnlineClients folds the emails and inbound tags active on this panel's own xray this poll into the local online/active sets, applying the grace window and pruning stale entries. Pass nil to only prune. See xray.Process for why the local sets are kept separate from the shared last_online column.

func (*InboundService) Reorder added in v3.9.5

func (s *InboundService) Reorder(userID int, ids []int) error

func (*InboundService) ResetAllTraffics

func (s *InboundService) ResetAllTraffics() error

func (*InboundService) ResetClientTraffic

func (s *InboundService) ResetClientTraffic(id int, clientEmail string) (needRestart bool, err error)

func (*InboundService) ResetClientTrafficByEmail

func (s *InboundService) ResetClientTrafficByEmail(clientEmail string) error

func (*InboundService) ResetInboundTraffic

func (s *InboundService) ResetInboundTraffic(id int) error

func (*InboundService) RetainSyncedNodeOnlineClients

func (s *InboundService) RetainSyncedNodeOnlineClients(nodes []*model.Node)

RetainSyncedNodeOnlineClients keeps online clients only for nodes the traffic sync still fetches; a node missing from nodes was deleted.

func (*InboundService) SampleClientTrafficDeltas added in v3.9.7

func (s *InboundService) SampleClientTrafficDeltas() ([]*xray.ClientTraffic, []*xray.ClientTraffic, error)

SampleClientTrafficDeltas returns per-client deltas normalized to the UI's fixed five-second speed window, plus the matching absolute rows since the previous sample. The database is the aggregation boundary: any current or future traffic collector that updates client_traffics automatically becomes visible on the clients page.

func (*InboundService) SearchClientTraffic

func (s *InboundService) SearchClientTraffic(query string) (traffic *xray.ClientTraffic, err error)

func (*InboundService) SearchInbounds

func (s *InboundService) SearchInbounds(query string) ([]*model.Inbound, error)

func (*InboundService) SetInboundEnable

func (s *InboundService) SetInboundEnable(id int, enable bool) (bool, error)

func (*InboundService) SetInboundSubSortIndex

func (s *InboundService) SetInboundSubSortIndex(id int, index int) error

SetInboundSubSortIndex changes only the subscription sort order, so a reorder cannot carry a stale settings/client payload over another edit.

func (*InboundService) SetNodeOnlineTree

func (s *InboundService) SetNodeOnlineTree(nodeID int, tree map[string][]string)

func (*InboundService) SetRemoteTraffic

func (s *InboundService) SetRemoteTraffic(nodeID int, snap *runtime.TrafficSnapshot, dirty, justPushed bool) (bool, error)

SetRemoteTraffic merges a node snapshot. justPushed marks the tick whose config push just landed, whose snapshot may still predate it (#6228).

func (*InboundService) SetVKTurnProxyClientEnable added in v3.9.2

func (s *InboundService) SetVKTurnProxyClientEnable(inboundID int, clientID string, enable bool) (bool, error)

func (*InboundService) SnapshotHasUnadoptedInbounds

func (s *InboundService) SnapshotHasUnadoptedInbounds(nodeID int, snap *runtime.TrafficSnapshot) (bool, error)

SnapshotHasUnadoptedInbounds reports whether the snapshot carries a tag with no central row yet, i.e. the next merge would adopt a new inbound.

func (*InboundService) UpdateClientIPs

func (s *InboundService) UpdateClientIPs(tx *gorm.DB, oldEmail string, newEmail string) error

func (*InboundService) UpdateClientStat

func (s *InboundService) UpdateClientStat(tx *gorm.DB, email string, client *model.Client) error

func (*InboundService) UpdateClientTrafficByEmail

func (s *InboundService) UpdateClientTrafficByEmail(email string, upload int64, download int64) error

func (*InboundService) UpdateInbound

func (s *InboundService) UpdateInbound(inbound *model.Inbound) (*model.Inbound, bool, error)

func (*InboundService) UpdateInboundPeer added in v3.9.2

func (s *InboundService) UpdateInboundPeer(data *model.Inbound, peerIndex int) (bool, error)

func (*InboundService) UpdateVKTurnProxyClient added in v3.9.2

func (s *InboundService) UpdateVKTurnProxyClient(data *model.Inbound, clientID string) (bool, error)

type InboundTrafficSummary

type InboundTrafficSummary struct {
	Id     int   `json:"id" example:"1"`
	Up     int64 `json:"up" example:"1048576"`
	Down   int64 `json:"down" example:"2097152"`
	Total  int64 `json:"total" example:"10737418240"`
	Enable bool  `json:"enable" example:"true"`
}

type LogEntry

type LogEntry struct {
	DateTime    time.Time `json:"DateTime" example:"2025-01-01T12:00:00Z"`
	FromAddress string    `json:"FromAddress" example:"192.0.2.10:54321"`
	ToAddress   string    `json:"ToAddress" example:"example.com:443"`
	Inbound     string    `json:"Inbound" example:"inbound-443"`
	Outbound    string    `json:"Outbound" example:"direct"`
	Email       string    `json:"Email" example:"alice@example.com"`
	Event       int       `json:"Event" example:"0"`
}

type MLDSA65Response

type MLDSA65Response struct {
	Seed   string `json:"seed" example:"mldsa65-seed"`
	Verify string `json:"verify" example:"mldsa65-verify"`
}

type MLKEM768Response

type MLKEM768Response struct {
	Seed   string `json:"seed" example:"mlkem768-seed"`
	Client string `json:"client" example:"mlkem768-client"`
}

type MetricSample

type MetricSample struct {
	T int64   `json:"t"`
	V float64 `json:"v"`
}

MetricSample is one point of any time-series we keep in memory. The frontend deserializes both keys, so they must stay short.

type NewUUIDResponse

type NewUUIDResponse struct {
	UUID string `json:"uuid" example:"550e8400-e29b-41d4-a716-446655440000"`
}

type NodeMutationRequest

type NodeMutationRequest struct {
	Id                  int      `json:"id" form:"id"`
	Name                string   `json:"name" form:"name" validate:"required"`
	Remark              string   `json:"remark" form:"remark"`
	Scheme              string   `json:"scheme" form:"scheme" validate:"omitempty,oneof=http https"`
	Address             string   `json:"address" form:"address" validate:"required"`
	Port                int      `json:"port" form:"port" validate:"gte=1,lte=65535"`
	BasePath            string   `json:"basePath" form:"basePath"`
	ApiToken            *string  `json:"apiToken,omitempty" form:"apiToken"`
	ClearApiToken       bool     `json:"clearApiToken,omitempty" form:"clearApiToken"`
	Enable              bool     `json:"enable" form:"enable"`
	AllowPrivateAddress bool     `json:"allowPrivateAddress" form:"allowPrivateAddress"`
	TlsVerifyMode       string   `json:"tlsVerifyMode" form:"tlsVerifyMode" validate:"omitempty,oneof=verify skip pin mtls"`
	PinnedCertSha256    string   `json:"pinnedCertSha256" form:"pinnedCertSha256"`
	InboundSyncMode     string   `json:"inboundSyncMode" form:"inboundSyncMode" validate:"omitempty,oneof=all selected"`
	InboundTags         []string `json:"inboundTags" form:"inboundTags"`
	OutboundTag         string   `json:"outboundTag" form:"outboundTag"`
}

NodeMutationRequest is the node write/probe contract. ApiToken is accepted only as input. On update, nil means keep the stored token; replacement and clearing are explicit and mutually exclusive.

type NodeService

type NodeService struct{}

func (*NodeService) AggregateNodeMetric

func (s *NodeService) AggregateNodeMetric(id int, metric string, bucketSeconds int, maxPoints int) []map[string]any

func (*NodeService) ClearDescendants

func (s *NodeService) ClearDescendants(nodeID int)

ClearDescendants drops a node's cached sub-node summaries (its probe failed).

func (*NodeService) ClearNodeDirty

func (s *NodeService) ClearNodeDirty(id int, dirtyAt int64) error

func (*NodeService) Create

func (s *NodeService) Create(n *model.Node) error

func (*NodeService) CreateFromRequest

func (s *NodeService) CreateFromRequest(req *NodeMutationRequest) (*NodeView, error)

func (*NodeService) Delete

func (s *NodeService) Delete(id int) error

func (*NodeService) EnsureInboundTagAllowed

func (s *NodeService) EnsureInboundTagAllowed(nodeID int, tag string) error

EnsureInboundTagAllowed adds a panel-managed inbound's tag to the node's selection when the node syncs in "selected" mode. Without it, the next traffic sync would filter the tag out of the snapshot and the orphan sweep would silently delete the central row the panel just created or renamed. Tags are only ever added (never removed): on a rename the node may keep reporting the old tag until the remote update lands, and a leftover entry that matches nothing is harmless.

func (*NodeService) EnsureInboundTagAllowedTx

func (s *NodeService) EnsureInboundTagAllowedTx(tx *gorm.DB, nodeID int, tag string) error

func (*NodeService) FetchCertFingerprint

func (s *NodeService) FetchCertFingerprint(ctx context.Context, n *model.Node) (string, error)

FetchCertFingerprint connects to the node over HTTPS without verifying the certificate and returns the leaf certificate's SHA-256 as base64, so the UI can offer a "fetch and pin current certificate" action.

func (*NodeService) GetAll

func (s *NodeService) GetAll() ([]*model.Node, error)

func (*NodeService) GetById

func (s *NodeService) GetById(id int) (*model.Node, error)

func (*NodeService) GetNodeTree

func (s *NodeService) GetNodeTree() ([]*model.Node, error)

GetNodeTree returns the direct nodes plus any transitive sub-nodes learned from them, with per-GUID counts so each node shows only the inbounds/online it physically hosts (#4983). Direct nodes carry the master's own GUID as ParentGuid; a transitive node carries its parent node's GUID. Transitive nodes are read-only projections (Id == 0). Used by the Nodes page and the heartbeat broadcast — never for probing/syncing, which stay on GetAll.

func (*NodeService) GetNodeTreeView

func (s *NodeService) GetNodeTreeView() ([]*NodeView, error)

func (*NodeService) GetRemoteInboundOptions

func (s *NodeService) GetRemoteInboundOptions(ctx context.Context, n *model.Node) ([]runtime.RemoteInboundOption, error)

func (*NodeService) GetViewById

func (s *NodeService) GetViewById(id int) (*NodeView, error)

func (*NodeService) GetWebCertFiles

func (s *NodeService) GetWebCertFiles(id int) (*runtime.WebCertFiles, error)

GetWebCertFiles asks a node for its own web TLS certificate/key file paths, used by "Set Cert from Panel" so a node-assigned inbound gets paths that exist on the node rather than the central panel. See issue #4854.

func (*NodeService) IsNodePending

func (s *NodeService) IsNodePending(id int) bool

IsNodePending reports whether a save targeting this node was deferred because the node is unreachable right now — offline or disabled — so the edit only reaches it on the next reconcile. It deliberately ignores config_dirty: that flag is set on EVERY node-backed edit as the reconcile self-heal marker, including edits pushed live to an online node, so keying the user-facing "saved, node offline, will sync" toast off it fired the warning on every save to a perfectly healthy online node.

func (*NodeService) LocalDescendants

func (s *NodeService) LocalDescendants() ([]model.NodeSummary, error)

LocalDescendants returns this panel's read-only summaries of the nodes it directly manages, so a parent panel can surface them as transitive sub-nodes (#4983). Only nodes with a known GUID are included — a stable identity is required to attribute them one hop up. Not recursive: each panel reports its own direct nodes, and a master walks one level via each direct node's endpoint, which covers the Node1 -> Node2 -> Node3 case.

func (*NodeService) MarkNodeDirty

func (s *NodeService) MarkNodeDirty(id int) error

func (*NodeService) MarkNodeDirtyTx

func (s *NodeService) MarkNodeDirtyTx(tx *gorm.DB, id int) error

func (*NodeService) MarkNodeInboundsAdopted

func (s *NodeService) MarkNodeInboundsAdopted(id int) error

func (*NodeService) MigrateNodeTokensToActiveKey

func (s *NodeService) MigrateNodeTokensToActiveKey() (int, int, error)

MigrateNodeTokensToActiveKey uses compare-and-swap to avoid clobbering live changes. Current-key rows are skipped; changed and skipped counts are returned.

func (*NodeService) NodeExists

func (s *NodeService) NodeExists(id int) (bool, error)

NodeExists reports whether a node with the given id exists on this panel. Used to drop stale, cross-panel node references on inbound import. A Count query distinguishes "no such node" (count 0, no error) from a real DB error.

func (*NodeService) NodeFromRequestForCertificate

func (s *NodeService) NodeFromRequestForCertificate(req *NodeMutationRequest) (*model.Node, error)

func (*NodeService) NodeMtlsCaCert

func (s *NodeService) NodeMtlsCaCert() (string, error)

NodeMtlsCaCert returns the PEM of this panel's node-auth CA certificate (the public half) to copy into a node's mTLS trust setting, minting the CA and the master client cert on first call so the panel is ready to present a client certificate to mtls nodes.

func (*NodeService) NodeSyncState

func (s *NodeService) NodeSyncState(id int) (enabled bool, status string, dirty bool, dirtyAt int64, err error)

func (*NodeService) Probe

func (s *NodeService) Probe(ctx context.Context, n *model.Node) (HeartbeatPatch, error)

func (*NodeService) ProbeWithOutbound

func (s *NodeService) ProbeWithOutbound(ctx context.Context, n *model.Node, outboundTag string) (HeartbeatPatch, error)

func (*NodeService) RefreshDescendants

func (s *NodeService) RefreshDescendants(ctx context.Context, n *model.Node)

RefreshDescendants pulls a direct node's published sub-node summaries and caches them keyed by node id. Best-effort: a fetch error keeps the last good set (the node may be briefly unreachable). Called from the heartbeat job.

func (*NodeService) ReloadMasterMtlsClient

func (s *NodeService) ReloadMasterMtlsClient() error

ReloadMasterMtlsClient validates the master credential currently stored by the panel and drops cached mTLS connection pools. This makes an intentional out-of-process credential rotation take effect without restarting x-ui (and therefore without stopping the xray child process in the same service).

func (*NodeService) Reorder added in v3.9.5

func (s *NodeService) Reorder(ids []int) error

func (*NodeService) RetainEnabledNodeDescendants

func (s *NodeService) RetainEnabledNodeDescendants(nodes []*model.Node)

RetainEnabledNodeDescendants drops sub-nodes learned from nodes the heartbeat no longer probes: disabled ones it skips, deleted ones missing from nodes.

func (*NodeService) RuntimeNodeFromRequest

func (s *NodeService) RuntimeNodeFromRequest(id int, req *NodeMutationRequest) (*model.Node, error)

func (*NodeService) SetEnable

func (s *NodeService) SetEnable(id int, enable bool) error

func (*NodeService) SetNodeMtlsTrustCA

func (s *NodeService) SetNodeMtlsTrustCA(caPem string) error

SetNodeMtlsTrustCA stores the CA certificate bundle trusted for incoming node-API clients. An empty value clears it; changes apply after restart.

func (*NodeService) Update

func (s *NodeService) Update(id int, in *model.Node) error

func (*NodeService) UpdateFromRequest

func (s *NodeService) UpdateFromRequest(id int, req *NodeMutationRequest) error

func (*NodeService) UpdateHeartbeat

func (s *NodeService) UpdateHeartbeat(id int, p HeartbeatPatch) error

func (*NodeService) UpdatePanels

func (s *NodeService) UpdatePanels(ids []int, dev bool) ([]NodeUpdateResult, error)

UpdatePanels triggers the official self-updater on each given node. Only enabled, online nodes are eligible — an offline node can't be reached, so it is reported as skipped rather than silently dropped.

type NodeUpdateResult

type NodeUpdateResult struct {
	Id    int    `json:"id"`
	Name  string `json:"name"`
	OK    bool   `json:"ok"`
	Error string `json:"error,omitempty"`
}

NodeUpdateResult reports the outcome of triggering a panel self-update on one node so the UI can show per-node success/failure for a bulk request.

type NodeView

type NodeView struct {
	Id                  int      `json:"id" example:"1"`
	Name                string   `json:"name" example:"edge-1"`
	Remark              string   `json:"remark" example:"Primary edge"`
	Scheme              string   `json:"scheme" example:"https"`
	Address             string   `json:"address" example:"node.example.com"`
	Port                int      `json:"port" example:"2053"`
	BasePath            string   `json:"basePath" example:"/"`
	HasApiToken         bool     `json:"hasApiToken" example:"true"`
	Enable              bool     `json:"enable" example:"true"`
	AllowPrivateAddress bool     `json:"allowPrivateAddress" example:"false"`
	TlsVerifyMode       string   `json:"tlsVerifyMode" example:"verify"`
	PinnedCertSha256    string   `json:"pinnedCertSha256" example:""`
	InboundSyncMode     string   `json:"inboundSyncMode" example:"all"`
	InboundTags         []string `json:"inboundTags" example:"[\"in-443-tcp\"]"`
	OutboundTag         string   `json:"outboundTag" example:"direct"`
	Guid                string   `json:"guid" example:"node-guid"`
	Status              string   `json:"status" example:"online"`
	LastHeartbeat       int64    `json:"lastHeartbeat" example:"1700000000"`
	LatencyMs           int      `json:"latencyMs" example:"42"`
	XrayVersion         string   `json:"xrayVersion" example:"25.10.31"`
	SingBoxVersion      string   `json:"singboxVersion"`
	SingBoxInstalled    bool     `json:"singboxInstalled"`
	SingBoxKnown        bool     `json:"singboxKnown"`
	CoreType            string   `json:"coreType"`
	RunningCore         string   `json:"runningCore"`
	PanelVersion        string   `json:"panelVersion" example:"v3.x.x"`
	CpuPct              float64  `json:"cpuPct" example:"12.5"`
	MemPct              float64  `json:"memPct" example:"45.2"`
	UptimeSecs          uint64   `json:"uptimeSecs" example:"86400"`
	NetUp               uint64   `json:"netUp" example:"2097152"`
	NetDown             uint64   `json:"netDown" example:"1048576"`
	LastError           string   `json:"lastError" example:""`
	XrayState           string   `json:"xrayState" example:"running"`
	XrayError           string   `json:"xrayError" example:""`
	SingBoxState        string   `json:"singboxState" example:"running"`
	SingBoxError        string   `json:"singboxError" example:""`
	ConfigDirty         bool     `json:"configDirty" example:"false"`
	ConfigDirtyAt       int64    `json:"configDirtyAt" example:"0"`
	InboundCount        int      `json:"inboundCount" example:"3"`
	ClientCount         int      `json:"clientCount" example:"25"`
	OnlineCount         int      `json:"onlineCount" example:"5"`
	ActiveCount         int      `json:"activeCount" example:"20"`
	DisabledCount       int      `json:"disabledCount" example:"2"`
	DepletedCount       int      `json:"depletedCount" example:"1"`
	ParentGuid          string   `json:"parentGuid,omitempty" example:""`
	Transitive          bool     `json:"transitive,omitempty" example:"false"`
	CreatedAt           int64    `json:"createdAt" example:"1700000000"`
	UpdatedAt           int64    `json:"updatedAt" example:"1700003600"`
}

NodeView is the browser/API read contract for nodes. Credentials are write-only: responses expose only whether a node has a token configured.

type ObsTagSnapshot

type ObsTagSnapshot struct {
	Tag          string `json:"tag"`
	Alive        bool   `json:"alive"`
	Delay        int64  `json:"delay"`
	LastSeenTime int64  `json:"lastSeenTime"`
	LastTryTime  int64  `json:"lastTryTime"`
	UpdatedAt    int64  `json:"updatedAt"`
}

type OutboundSubscriptionService

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

OutboundSubscriptionService manages remote outbound subscriptions.

func NewOutboundSubscriptionService

func NewOutboundSubscriptionService() *OutboundSubscriptionService

NewOutboundSubscriptionService returns a service for managing outbound subscriptions.

func (*OutboundSubscriptionService) AllActiveOutboundTags

func (s *OutboundSubscriptionService) AllActiveOutboundTags() ([]string, error)

AllActiveOutboundTags returns only the tags of active subscription outbounds. Useful for populating balancer / routing selectors without shipping full objects.

func (*OutboundSubscriptionService) AllActiveOutbounds

func (s *OutboundSubscriptionService) AllActiveOutbounds() ([]any, error)

AllActiveOutbounds returns the concatenation of the last-fetched outbounds for every enabled subscription. This is the set that should be merged into the final Xray config. Order: subscription creation order (by id asc) so that later subscriptions can shadow earlier ones if the admin uses colliding prefixes (last writer wins inside xray, but we try to keep tags unique).

func (*OutboundSubscriptionService) Create

func (s *OutboundSubscriptionService) Create(remark, rawURL, tagPrefix, userAgent string, enabled bool, updateInterval int, allowPrivate, prepend, allowInsecure bool) (*model.OutboundSubscription, error)

func (*OutboundSubscriptionService) Delete

func (s *OutboundSubscriptionService) Delete(id int) error

Delete removes a subscription.

func (*OutboundSubscriptionService) Get

Get returns a single subscription by id.

func (*OutboundSubscriptionService) GetLastOutbounds

func (s *OutboundSubscriptionService) GetLastOutbounds(id int) ([]any, error)

GetLastOutbounds returns the last successfully fetched outbounds for a subscription (as raw interface slice ready for JSON merge). Returns nil slice when none.

func (*OutboundSubscriptionService) List

List returns all subscriptions (newest first).

func (*OutboundSubscriptionService) Move

func (s *OutboundSubscriptionService) Move(id int, up bool) error

Move shifts a subscription one step up or down in the priority order and re-normalizes all priorities to a 0..n-1 sequence.

func (*OutboundSubscriptionService) Refresh

func (s *OutboundSubscriptionService) Refresh(id int) ([]any, error)

Refresh fetches the subscription URL, parses the links, assigns stable tags, persists the results, and returns the generated outbounds.

func (*OutboundSubscriptionService) RefreshAllEnabled

func (s *OutboundSubscriptionService) RefreshAllEnabled() (int, error)

RefreshAllEnabled fetches every enabled subscription whose due time has passed (lastUpdated + updateInterval <= now). It returns the number of subscriptions that were actually refreshed.

func (*OutboundSubscriptionService) Update

func (s *OutboundSubscriptionService) Update(id int, remark, rawURL, tagPrefix, userAgent string, enabled bool, updateInterval int, allowPrivate, prepend, allowInsecure bool) error

Update updates editable fields.

type PeerActivity

type PeerActivity struct {
	Interface  string `json:"interface" example:"awg1"`
	Tag        string `json:"tag" example:"inbound-51820"`
	InboundId  int    `json:"inboundId" example:"1"`
	Email      string `json:"email" example:"peer@example.com"`
	Endpoint   string `json:"endpoint" example:"203.0.113.9:51820"`
	AllowedIPs string `json:"allowedIPs" example:"10.8.1.2/32"`
	// Handshake is unix milliseconds, 0 when the peer has never connected.
	Handshake int64 `json:"handshake" example:"1735732800000"`
	Up        int64 `json:"up" example:"1048576"`
	Down      int64 `json:"down" example:"4194304"`
	Online    bool  `json:"online" example:"true"`
}

PeerActivity is one peer's live embedded-Device-reported state, the counterpart of an Xray access-log entry: a tunnel logs no requests, only handshakes and bytes.

type ProbeResultUI

type ProbeResultUI struct {
	Status           string  `json:"status" example:"online"`
	LatencyMs        int     `json:"latencyMs" example:"42"`
	XrayVersion      string  `json:"xrayVersion" example:"25.10.31"`
	SingBoxVersion   string  `json:"singboxVersion"`
	SingBoxInstalled bool    `json:"singboxInstalled"`
	SingBoxKnown     bool    `json:"singboxKnown"`
	CoreType         string  `json:"coreType"`
	RunningCore      string  `json:"runningCore"`
	PanelVersion     string  `json:"panelVersion" example:"v3.x.x"`
	CpuPct           float64 `json:"cpuPct" example:"12.5"`
	MemPct           float64 `json:"memPct" example:"45.2"`
	UptimeSecs       uint64  `json:"uptimeSecs" example:"86400"`
	Error            string  `json:"error"`
	// XrayState/XrayError are populated on successful probes even when the node's
	// Xray core is not healthy. The UI uses them for a distinct "panel ok, xray failed" indicator.
	XrayState    string `json:"xrayState"`
	XrayError    string `json:"xrayError"`
	SingBoxState string `json:"singboxState"`
	SingBoxError string `json:"singboxError"`
}

type ProcessState

type ProcessState string

ProcessState represents the current state of a system process.

const (
	Running ProcessState = "running" // Process is running normally
	Stop    ProcessState = "stop"    // Process is stopped
	Error   ProcessState = "error"   // Process is in error state
)

Process state constants

type RealityScanResult

type RealityScanResult struct {
	Target   string `json:"target" example:"www.cloudflare.com:443"`
	Host     string `json:"host" example:"www.cloudflare.com"`
	IP       string `json:"ip" example:"104.16.124.96"`
	Port     int    `json:"port" example:"443"`
	Feasible bool   `json:"feasible" example:"true"`
	// PrivateTarget marks a target that resolves to a loopback/private/link-local
	// address: blocked before the probe unless the caller opted in, then flagged.
	PrivateTarget bool   `json:"privateTarget" example:"false"`
	TLS13         bool   `json:"tls13" example:"true"`
	TLSVersion    string `json:"tlsVersion" example:"1.3"`
	H2            bool   `json:"h2" example:"true"`
	ALPN          string `json:"alpn" example:"h2"`
	X25519        bool   `json:"x25519" example:"true"`
	CurveID       string `json:"curveID" example:"X25519"`
	CertValid     bool   `json:"certValid" example:"true"`
	// CertChainValid ignores the name: a trusted chain presented for other names
	// still has serverNames the panel can offer instead of the failing SNI.
	CertChainValid bool `json:"certChainValid" example:"true"`
	// CertChainBytes is the sum of DER lengths of the presented peer chain.
	// xray-core ML-DSA-65 REALITY needs >= 3500 bytes (constant lives in xray-core).
	CertChainBytes int      `json:"certChainBytes" example:"3427"`
	CertSubject    string   `json:"certSubject" example:"cloudflare.com"`
	CertIssuer     string   `json:"certIssuer" example:"Google Trust Services"`
	NotAfter       string   `json:"notAfter" example:"2026-08-01T00:00:00Z"`
	ServerNames    []string `json:"serverNames"`
	LatencyMs      int      `json:"latencyMs" example:"180"`
	Reason         string   `json:"reason" example:""`
}

type Release

type Release struct {
	TagName         string `json:"tag_name"`         // The tag name of the release
	Body            string `json:"body"`             // The release notes; the dev channel reads its commit from here
	TargetCommitish string `json:"target_commitish"` // The branch/commit the tag points at
	Prerelease      bool   `json:"prerelease"`       // Whether this is a pre-release
}

Release represents information about a software release from GitHub.

type RoutingPresetDetail added in v3.9.5

type RoutingPresetDetail struct {
	RoutingPresetView
	Rules []json.RawMessage `json:"rules"`
}

type RoutingPresetInput added in v3.9.5

type RoutingPresetInput struct {
	Name        string            `json:"name"`
	Description string            `json:"description"`
	Rules       []json.RawMessage `json:"rules"`
}

type RoutingPresetService added in v3.9.5

type RoutingPresetService struct{}

func (*RoutingPresetService) Delete added in v3.9.5

func (s *RoutingPresetService) Delete(userID, id int) error

func (*RoutingPresetService) Get added in v3.9.5

func (s *RoutingPresetService) Get(userID, id int) (*RoutingPresetDetail, error)

func (*RoutingPresetService) List added in v3.9.5

func (s *RoutingPresetService) List(userID int) ([]RoutingPresetView, error)

func (*RoutingPresetService) Save added in v3.9.5

type RoutingPresetView added in v3.9.5

type RoutingPresetView struct {
	Id          int    `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	RuleCount   int    `json:"ruleCount"`
	CreatedAt   int64  `json:"createdAt"`
	UpdatedAt   int64  `json:"updatedAt"`
}

type SecretClears

type SecretClears struct {
	TgBotToken      bool
	LdapPassword    bool
	SmtpPassword    bool
	DiscordBotToken bool
}

SecretClears marks redacted secrets the user explicitly emptied. Without a flag, a blank submitted secret means "unchanged" (the field is always served blank to the browser) and the stored value is preserved.

type ServerService

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

ServerService provides business logic for server monitoring and management. It handles system status collection, IP detection, and application statistics.

func (*ServerService) AggregateCpuHistory

func (s *ServerService) AggregateCpuHistory(bucketSeconds int, maxPoints int) []map[string]any

AggregateCpuHistory returns up to maxPoints averaged buckets of size bucketSeconds. Kept for back-compat with the original /panel/api/server/cpuHistory/:bucket route; the response key is "cpu" (not "v") so legacy consumers parse unchanged.

func (*ServerService) AggregateSystemMetric

func (s *ServerService) AggregateSystemMetric(metric string, bucketSeconds int, maxPoints int) []map[string]any

AggregateSystemMetric returns up to maxPoints averaged buckets for any known system metric (see SystemMetricKeys). Output points have keys {"t": unixSec, "v": value}; the caller decides how to format the value.

func (*ServerService) AppendCpuSample

func (s *ServerService) AppendCpuSample(t time.Time, v float64)

AppendCpuSample is preserved for callers that only have the CPU number. New callers should prefer AppendStatusSample which writes the full set.

func (*ServerService) AppendStatusSample

func (s *ServerService) AppendStatusSample(t time.Time, status *Status)

AppendStatusSample writes one tick of every metric we keep — CPU, memory percent, network throughput (bytes/s), online client count, and the three load averages. Called by RefreshStatus on the same @2s cadence as AppendCpuSample, so all series stay aligned.

func (*ServerService) AutoUpdateGeofiles added in v3.9.5

func (s *ServerService) AutoUpdateGeofiles() error

AutoUpdateGeofiles refreshes the standard geodata set only while Xray is already running. The scheduled updater must never turn a deliberately stopped core back on just because a new geosite/geoip release appeared.

func (*ServerService) BackupFilename

func (s *ServerService) BackupFilename(requestHost string) string

BackupFilename returns the filename for a database backup, named after the panel's address so a downloaded or Telegram-sent backup identifies the server it came from, followed by the current date and time (_YYYY-MM-DD_HHMMSS) so files accumulated in Telegram chat history group by server then sort chronologically and same-day backups stay distinct. requestHost is the browser's address: the getDb handler passes c.Request.Host so a panel download is named after whatever address the user reached the panel with, no Listen Domain needed. The Telegram bot has no request and passes "", falling back to the configured Listen Domain (webDomain) and then the public IP. The extension is .dump on PostgreSQL and .db on SQLite; the base falls back to "x-ui" when no address is known.

func (*ServerService) CurrentStatus

func (s *ServerService) CurrentStatus() *Status

func (*ServerService) GetAmneziaWGLogs

func (s *ServerService) GetAmneziaWGLogs(count string, filter string) *AmneziaWGLogs

GetAmneziaWGLogs returns at most count peer rows and count event lines, optionally narrowed to rows whose text contains filter (case-insensitive), mirroring GetXrayLogs' own count+filter contract.

func (*ServerService) GetCertHash

func (s *ServerService) GetCertHash(certFile string, certContent string) ([]string, error)

GetCertHash parses a certificate (from a file path or inline PEM/DER content) and returns the hex-encoded SHA-256 over each certificate's raw DER — the value xray-core's pinnedPeerCertSha256 (pcs) expects. Lets the panel fill the pinned-cert field from the inbound's own certificate without the user computing the hash by hand.

func (*ServerService) GetConfigJson

func (s *ServerService) GetConfigJson() (any, error)

func (*ServerService) GetDb

func (s *ServerService) GetDb() ([]byte, error)

func (*ServerService) GetDefaultLogOutboundTags

func (s *ServerService) GetDefaultLogOutboundTags() (freedoms, blackholes []string)

GetDefaultLogOutboundTags scans the default Xray config for freedom and blackhole outbound tags so /getXrayLogs can colour-code log lines without the controller re-doing the JSON walk. Falls back to the historical "direct"/"blocked" defaults when the config can't be read.

func (*ServerService) GetFail2banStatus

func (s *ServerService) GetFail2banStatus() Fail2banStatus

func (*ServerService) GetLogs

func (s *ServerService) GetLogs(count string, level string, syslog string) []string

func (*ServerService) GetMigration

func (s *ServerService) GetMigration() ([]byte, string, error)

GetMigration produces a cross-engine migration file plus its filename: on a SQLite panel it returns a portable .dump (SQL text), and on a PostgreSQL panel it returns a .db SQLite database built from the live data. Either output can then seed a panel running on the other backend.

func (*ServerService) GetNewEchCert

func (s *ServerService) GetNewEchCert(sni string) (any, error)

func (*ServerService) GetNewUUID

func (s *ServerService) GetNewUUID() (*NewUUIDResponse, error)

func (*ServerService) GetNewVlessEnc

func (s *ServerService) GetNewVlessEnc() (any, error)

func (*ServerService) GetNewX25519Cert

func (s *ServerService) GetNewX25519Cert() (any, error)

func (*ServerService) GetNewmldsa65

func (s *ServerService) GetNewmldsa65() (*MLDSA65Response, error)

func (*ServerService) GetNewmlkem768

func (s *ServerService) GetNewmlkem768() (*MLKEM768Response, error)

func (*ServerService) GetRemoteCertHash

func (s *ServerService) GetRemoteCertHash(server string) ([]string, error)

GetRemoteCertHash opens a uTLS (Chrome fingerprint) handshake to a remote endpoint and returns the hex-encoded SHA-256 of its leaf certificate — the value to put in pinnedPeerCertSha256 (pcs) when pinning a server whose certificate file you don't hold (a CDN front, a REALITY dest, an external proxy). A native handshake replaces the old `xray tls ping` subprocess so the real dial/handshake failure (connection refused, timeout, …) surfaces verbatim. `server` may be host or host:port; the port defaults to 443.

func (*ServerService) GetStatus

func (s *ServerService) GetStatus(lastStatus *Status) *Status

func (*ServerService) GetVKTurnProxyLogs added in v3.9.2

func (s *ServerService) GetVKTurnProxyLogs(count string, level string) []string

func (*ServerService) GetVKTurnProxyStatus added in v3.9.2

func (s *ServerService) GetVKTurnProxyStatus() VKTurnProxyRuntimeStatus

func (*ServerService) GetVKTurnProxyVersions added in v3.9.2

func (s *ServerService) GetVKTurnProxyVersions() ([]string, error)

func (*ServerService) GetXrayLogs

func (s *ServerService) GetXrayLogs(
	count string,
	filter string,
	showDirect string,
	showBlocked string,
	showProxy string,
	freedoms []string,
	blackholes []string,
) []LogEntry

func (*ServerService) GetXrayVersions

func (s *ServerService) GetXrayVersions() ([]string, error)

func (*ServerService) GetXrayVersionsCached

func (s *ServerService) GetXrayVersionsCached() ([]string, error)

GetXrayVersionsCached wraps GetXrayVersions with a TTL cache. On fetch failure we serve the last successful list (if any) so the UI doesn't go blank during a GitHub API hiccup; if there's no cache at all the underlying error is surfaced.

func (*ServerService) ImportDB

func (s *ServerService) ImportDB(file multipart.File, keepHostSettings bool) error

func (*ServerService) IsValidGeofileName

func (s *ServerService) IsValidGeofileName(filename string) bool

IsValidGeofileName validates that the filename is safe for geofile operations. It checks for path traversal attempts and ensures the filename contains only safe characters.

func (*ServerService) LastStatus

func (s *ServerService) LastStatus() *Status

LastStatus returns the most recent Status snapshot collected by RefreshStatus. Safe for concurrent readers.

func (*ServerService) RefreshStatus

func (s *ServerService) RefreshStatus() *Status

RefreshStatus collects a new system snapshot, stores it as LastStatus, and appends it to the system-metrics time series. Returns the new snapshot (may be nil if collection failed). Called by the background ticker; the caller is responsible for any side effects (websocket broadcast, xray metrics sample).

func (*ServerService) RestartVKTurnProxyService added in v3.9.2

func (s *ServerService) RestartVKTurnProxyService() error

func (*ServerService) RestartXrayService

func (s *ServerService) RestartXrayService() error

func (*ServerService) ScanRealityTarget

func (s *ServerService) ScanRealityTarget(target string, sni string, xver int, allowPrivate bool) (*RealityScanResult, error)

ScanRealityTarget probes one operator-supplied target. An empty sni falls back to the target host; allowPrivate lifts the SSRF guard for this probe only.

func (*ServerService) ScanRealityTargets

func (s *ServerService) ScanRealityTargets(targetsCSV string) ([]*RealityScanResult, error)

func (*ServerService) StartVKTurnProxyService added in v3.9.2

func (s *ServerService) StartVKTurnProxyService() error

func (*ServerService) StopVKTurnProxyService added in v3.9.2

func (s *ServerService) StopVKTurnProxyService() error

func (*ServerService) StopXrayService

func (s *ServerService) StopXrayService() error

func (*ServerService) UpdateGeofile

func (s *ServerService) UpdateGeofile(fileName string) error

func (*ServerService) UpdateVKTurnProxy added in v3.9.2

func (s *ServerService) UpdateVKTurnProxy(version string) error

func (*ServerService) UpdateXray

func (s *ServerService) UpdateXray(version string) error

func (*ServerService) UploadVKTurnProxyBinary added in v3.9.2

func (s *ServerService) UploadVKTurnProxyBinary(file multipart.File) error

type SettingService

type SettingService struct{}

SettingService provides business logic for application settings management. It handles configuration storage, retrieval, and validation for all system settings.

func (*SettingService) AddHiddifyLegacySubscriptionAlias added in v3.9.6

func (s *SettingService) AddHiddifyLegacySubscriptionAlias(value HiddifyLegacySubscriptionAlias) error

func (*SettingService) BuildSubURIBase

func (s *SettingService) BuildSubURIBase(host string) string

BuildSubURIBase is shared by GetDefaultSettings (the panel's Client Information page) and the subscription page so both render subscription URLs identically.

func (*SettingService) EnsureMasterClientCert

func (s *SettingService) EnsureMasterClientCert() (crypto.CertKeyPEM, error)

EnsureMasterClientCert returns the client certificate this panel presents when calling its nodes over mTLS, issuing it from the node CA on first use and reusing the stored pair thereafter.

func (*SettingService) EnsureNodeMtlsCA

func (s *SettingService) EnsureNodeMtlsCA() (crypto.CertKeyPEM, error)

EnsureNodeMtlsCA returns this panel's node-auth CA, minting and persisting it on first use and reusing the stored pair thereafter. The CA private key never leaves the panel.

func (*SettingService) GetAccessLogEnable

func (s *SettingService) GetAccessLogEnable() (bool, error)

GetAccessLogEnable reports whether an Xray access log is configured. Used by the UI for features that genuinely read the log file (the xray log viewer) — distinct from IP limiting, which works without it.

func (*SettingService) GetAllSetting

func (s *SettingService) GetAllSetting() (*entity.AllSetting, error)

func (*SettingService) GetAllSettingView

func (s *SettingService) GetAllSettingView() (*entity.AllSettingView, error)

func (*SettingService) GetBasePath

func (s *SettingService) GetBasePath() (string, error)

func (*SettingService) GetCertFile

func (s *SettingService) GetCertFile() (string, error)

func (*SettingService) GetCoreType

func (s *SettingService) GetCoreType() (string, error)

func (*SettingService) GetDatepicker

func (s *SettingService) GetDatepicker() (string, error)

func (*SettingService) GetDefaultSettings

func (s *SettingService) GetDefaultSettings(host string) (any, error)

func (*SettingService) GetDefaultXrayConfig

func (s *SettingService) GetDefaultXrayConfig() (any, error)

func (*SettingService) GetDevChannelEnable

func (s *SettingService) GetDevChannelEnable() (bool, error)

GetDevChannelEnable reports whether the panel self-update tracks the rolling per-commit dev release instead of the latest stable tag.

func (*SettingService) GetDiscordAdminIds

func (s *SettingService) GetDiscordAdminIds() (string, error)

func (*SettingService) GetDiscordBotBackup

func (s *SettingService) GetDiscordBotBackup() (bool, error)

func (*SettingService) GetDiscordBotEnable

func (s *SettingService) GetDiscordBotEnable() (bool, error)

func (*SettingService) GetDiscordBotToken

func (s *SettingService) GetDiscordBotToken() (string, error)

func (*SettingService) GetDiscordChannelId

func (s *SettingService) GetDiscordChannelId() (string, error)

func (*SettingService) GetDiscordCpu

func (s *SettingService) GetDiscordCpu() (int, error)

func (*SettingService) GetDiscordEnabledEvents

func (s *SettingService) GetDiscordEnabledEvents() (string, error)

func (*SettingService) GetDiscordLang

func (s *SettingService) GetDiscordLang() (string, error)

func (*SettingService) GetDiscordMemory

func (s *SettingService) GetDiscordMemory() (int, error)

func (*SettingService) GetDiscordRunTime

func (s *SettingService) GetDiscordRunTime() (string, error)

func (*SettingService) GetExpireDiff

func (s *SettingService) GetExpireDiff() (int, error)

func (*SettingService) GetExternalTrafficInformEnable

func (s *SettingService) GetExternalTrafficInformEnable() (bool, error)

func (*SettingService) GetExternalTrafficInformURI

func (s *SettingService) GetExternalTrafficInformURI() (string, error)

func (*SettingService) GetFactoryDefaults

func (s *SettingService) GetFactoryDefaults() map[string]string

GetFactoryDefaults returns the shipped default value per setting, keyed by the AllSetting json field name. Unlike GetDefaultSettings (which reports current effective values), this is defaultValueMap projected through the AllSetting field set: only keys that exist as an AllSetting json tag are returned, minus the credential fields in factoryDefaultSecretKeys. Keys with no AllSetting field (secret, panelGuid, the node mTLS material, xrayTemplateConfig) are excluded structurally rather than by deny-list.

func (*SettingService) GetHappLinkEnable

func (s *SettingService) GetHappLinkEnable() (bool, error)

func (*SettingService) GetHiddifyLegacySubscriptionAliases added in v3.9.6

func (s *SettingService) GetHiddifyLegacySubscriptionAliases() ([]HiddifyLegacySubscriptionAlias, error)

func (*SettingService) GetHiddifySubscriptionURIs added in v3.9.7

func (s *SettingService) GetHiddifySubscriptionURIs() (map[string]string, error)

func (*SettingService) GetIpLimitAllowlist

func (s *SettingService) GetIpLimitAllowlist() (string, error)

GetIpLimitAllowlist returns the operator's trusted addresses and networks, which the IP limit neither counts nor bans.

func (*SettingService) GetIpLimitEnable

func (s *SettingService) GetIpLimitEnable() (bool, error)

GetIpLimitEnable reports whether the IP-limit feature is available. Always true since the panel enforces limits via the core's online-stats API; on an older core the job falls back to access-log parsing and warns there when the log is missing, so the UI no longer hides the field behind that condition.

func (*SettingService) GetKeyFile

func (s *SettingService) GetKeyFile() (string, error)

func (*SettingService) GetLdapAutoCreate

func (s *SettingService) GetLdapAutoCreate() (bool, error)

func (*SettingService) GetLdapAutoDelete

func (s *SettingService) GetLdapAutoDelete() (bool, error)

func (*SettingService) GetLdapBaseDN

func (s *SettingService) GetLdapBaseDN() (string, error)

func (*SettingService) GetLdapBindDN

func (s *SettingService) GetLdapBindDN() (string, error)

func (*SettingService) GetLdapDefaultExpiryDays

func (s *SettingService) GetLdapDefaultExpiryDays() (int, error)

func (*SettingService) GetLdapDefaultLimitIP

func (s *SettingService) GetLdapDefaultLimitIP() (int, error)

func (*SettingService) GetLdapDefaultTotalGB

func (s *SettingService) GetLdapDefaultTotalGB() (int, error)

func (*SettingService) GetLdapEnable

func (s *SettingService) GetLdapEnable() (bool, error)

GetLdapEnable returns whether LDAP is enabled.

func (*SettingService) GetLdapFlagField

func (s *SettingService) GetLdapFlagField() (string, error)

func (*SettingService) GetLdapHost

func (s *SettingService) GetLdapHost() (string, error)

func (*SettingService) GetLdapInboundTags

func (s *SettingService) GetLdapInboundTags() (string, error)

func (*SettingService) GetLdapInsecureSkipVerify

func (s *SettingService) GetLdapInsecureSkipVerify() (bool, error)

func (*SettingService) GetLdapInvertFlag

func (s *SettingService) GetLdapInvertFlag() (bool, error)

func (*SettingService) GetLdapPassword

func (s *SettingService) GetLdapPassword() (string, error)

func (*SettingService) GetLdapPort

func (s *SettingService) GetLdapPort() (int, error)

func (*SettingService) GetLdapSyncCron

func (s *SettingService) GetLdapSyncCron() (string, error)

func (*SettingService) GetLdapTruthyValues

func (s *SettingService) GetLdapTruthyValues() (string, error)

func (*SettingService) GetLdapUseTLS

func (s *SettingService) GetLdapUseTLS() (bool, error)

func (*SettingService) GetLdapUserAttr

func (s *SettingService) GetLdapUserAttr() (string, error)

func (*SettingService) GetLdapUserFilter

func (s *SettingService) GetLdapUserFilter() (string, error)

func (*SettingService) GetLdapVlessField

func (s *SettingService) GetLdapVlessField() (string, error)

func (*SettingService) GetListen

func (s *SettingService) GetListen() (string, error)

func (*SettingService) GetNord

func (s *SettingService) GetNord() (string, error)

func (*SettingService) GetOutboundDownThreshold

func (s *SettingService) GetOutboundDownThreshold() (int, error)

GetOutboundDownThreshold returns how many consecutive failed observatory probes an outbound must accumulate before an outbound.down notification is emitted. 1 preserves the legacy "notify on the first failed probe" behaviour.

func (*SettingService) GetPageSize

func (s *SettingService) GetPageSize() (int, error)

func (*SettingService) GetPanelGuid

func (s *SettingService) GetPanelGuid() (string, error)

GetPanelGuid returns this panel's stable self-identifier, persisting a freshly generated UUID on first read. It is the globally stable node identity used to attribute online clients and inbounds to the physical node that hosts them across a chain of nodes (#4983), where per-panel autoincrement node ids are meaningless one hop away.

func (*SettingService) GetPanelOutbound

func (s *SettingService) GetPanelOutbound() (string, error)

GetPanelOutbound returns the Xray outbound tag the panel's own outbound requests (version checks, Telegram, subscription fetches) are routed through.

func (*SettingService) GetPia

func (s *SettingService) GetPia() (string, error)

func (*SettingService) GetPort

func (s *SettingService) GetPort() (int, error)

func (*SettingService) GetRealityScanCandidates

func (s *SettingService) GetRealityScanCandidates() (string, error)

func (*SettingService) GetRemarkTemplate

func (s *SettingService) GetRemarkTemplate() (string, error)

func (*SettingService) GetRestartXrayOnClientDisable

func (s *SettingService) GetRestartXrayOnClientDisable() (bool, error)

func (*SettingService) GetSecret

func (s *SettingService) GetSecret() ([]byte, error)

func (*SettingService) GetSessionMaxAge

func (s *SettingService) GetSessionMaxAge() (int, error)

func (*SettingService) GetSingBoxConfigTemplate added in v3.9.2

func (s *SettingService) GetSingBoxConfigTemplate() (string, error)

func (*SettingService) GetSmtpCpu

func (s *SettingService) GetSmtpCpu() (int, error)

func (*SettingService) GetSmtpEnable

func (s *SettingService) GetSmtpEnable() (bool, error)

func (*SettingService) GetSmtpEnabledEvents

func (s *SettingService) GetSmtpEnabledEvents() (string, error)

func (*SettingService) GetSmtpEncryptionType

func (s *SettingService) GetSmtpEncryptionType() (string, error)

func (*SettingService) GetSmtpFrom

func (s *SettingService) GetSmtpFrom() (string, error)

func (*SettingService) GetSmtpFromName

func (s *SettingService) GetSmtpFromName() (string, error)

func (*SettingService) GetSmtpHost

func (s *SettingService) GetSmtpHost() (string, error)

func (*SettingService) GetSmtpMemory

func (s *SettingService) GetSmtpMemory() (int, error)

func (*SettingService) GetSmtpPassword

func (s *SettingService) GetSmtpPassword() (string, error)

func (*SettingService) GetSmtpPort

func (s *SettingService) GetSmtpPort() (int, error)

func (*SettingService) GetSmtpTo

func (s *SettingService) GetSmtpTo() (string, error)

func (*SettingService) GetSmtpUsername

func (s *SettingService) GetSmtpUsername() (string, error)

func (*SettingService) GetSubAnnounce

func (s *SettingService) GetSubAnnounce() (string, error)

func (*SettingService) GetSubCalendarExpireInclusive

func (s *SettingService) GetSubCalendarExpireInclusive() (bool, error)

func (*SettingService) GetSubCertFile

func (s *SettingService) GetSubCertFile() (string, error)

func (*SettingService) GetSubClashAutoDetect

func (s *SettingService) GetSubClashAutoDetect() (bool, error)

func (*SettingService) GetSubClashEnable

func (s *SettingService) GetSubClashEnable() (bool, error)

func (*SettingService) GetSubClashEnableRouting

func (s *SettingService) GetSubClashEnableRouting() (bool, error)

func (*SettingService) GetSubClashPath

func (s *SettingService) GetSubClashPath() (string, error)

func (*SettingService) GetSubClashRules

func (s *SettingService) GetSubClashRules() (string, error)

func (*SettingService) GetSubClashURI

func (s *SettingService) GetSubClashURI() (string, error)

func (*SettingService) GetSubClashUserAgentRegex

func (s *SettingService) GetSubClashUserAgentRegex() (string, error)

func (*SettingService) GetSubDomain

func (s *SettingService) GetSubDomain() (string, error)

func (*SettingService) GetSubEnable

func (s *SettingService) GetSubEnable() (bool, error)

func (*SettingService) GetSubEnableRouting

func (s *SettingService) GetSubEnableRouting() (bool, error)

func (*SettingService) GetSubEncrypt

func (s *SettingService) GetSubEncrypt() (bool, error)

func (*SettingService) GetSubExpiredTemplate

func (s *SettingService) GetSubExpiredTemplate() (string, error)

func (*SettingService) GetSubHappAlwaysHwid

func (s *SettingService) GetSubHappAlwaysHwid() (bool, error)

func (*SettingService) GetSubHappAutoConnect

func (s *SettingService) GetSubHappAutoConnect() (bool, error)

func (*SettingService) GetSubHappAutoConnectType

func (s *SettingService) GetSubHappAutoConnectType() (string, error)

func (*SettingService) GetSubHappAutoDetect

func (s *SettingService) GetSubHappAutoDetect() (bool, error)

func (*SettingService) GetSubHappColorProfile

func (s *SettingService) GetSubHappColorProfile() (string, error)

func (*SettingService) GetSubHappExcludeApns

func (s *SettingService) GetSubHappExcludeApns() (bool, error)

func (*SettingService) GetSubHappExcludeRoutes

func (s *SettingService) GetSubHappExcludeRoutes() (string, error)

func (*SettingService) GetSubHappFallbackUrl

func (s *SettingService) GetSubHappFallbackUrl() (string, error)

func (*SettingService) GetSubHappNewUrl

func (s *SettingService) GetSubHappNewUrl() (string, error)

func (*SettingService) GetSubHappNoLimit

func (s *SettingService) GetSubHappNoLimit() (bool, error)

func (*SettingService) GetSubHappNotificationExpire

func (s *SettingService) GetSubHappNotificationExpire() (bool, error)

func (*SettingService) GetSubHappPerAppList

func (s *SettingService) GetSubHappPerAppList() (string, error)

func (*SettingService) GetSubHappPerAppMode

func (s *SettingService) GetSubHappPerAppMode() (string, error)

func (*SettingService) GetSubHappPingType

func (s *SettingService) GetSubHappPingType() (string, error)

func (*SettingService) GetSubHappProviderId

func (s *SettingService) GetSubHappProviderId() (string, error)

func (*SettingService) GetSubHappSubExpire

func (s *SettingService) GetSubHappSubExpire() (bool, error)
func (s *SettingService) GetSubHappSubExpireButtonLink() (string, error)
func (s *SettingService) GetSubHappSubInfoButtonLink() (string, error)

func (*SettingService) GetSubHappSubInfoButtonText

func (s *SettingService) GetSubHappSubInfoButtonText() (string, error)

func (*SettingService) GetSubHappSubInfoColor

func (s *SettingService) GetSubHappSubInfoColor() (string, error)

func (*SettingService) GetSubHappSubInfoText

func (s *SettingService) GetSubHappSubInfoText() (string, error)

func (*SettingService) GetSubHappTunMode

func (s *SettingService) GetSubHappTunMode() (string, error)

func (*SettingService) GetSubHappTunType

func (s *SettingService) GetSubHappTunType() (string, error)

func (*SettingService) GetSubHideSettings

func (s *SettingService) GetSubHideSettings() (bool, error)

func (*SettingService) GetSubIncyEnableRouting

func (s *SettingService) GetSubIncyEnableRouting() (bool, error)

func (*SettingService) GetSubIncyRoutingRules

func (s *SettingService) GetSubIncyRoutingRules() (string, error)

func (*SettingService) GetSubInfoNodeEnable

func (s *SettingService) GetSubInfoNodeEnable() (bool, error)

func (*SettingService) GetSubJsonAlwaysArray

func (s *SettingService) GetSubJsonAlwaysArray() (bool, error)

func (*SettingService) GetSubJsonAutoDetect

func (s *SettingService) GetSubJsonAutoDetect() (bool, error)

func (*SettingService) GetSubJsonDns

func (s *SettingService) GetSubJsonDns() (string, error)

func (*SettingService) GetSubJsonEnable

func (s *SettingService) GetSubJsonEnable() (bool, error)

func (*SettingService) GetSubJsonFinalMask

func (s *SettingService) GetSubJsonFinalMask() (string, error)

func (*SettingService) GetSubJsonMux

func (s *SettingService) GetSubJsonMux() (string, error)

func (*SettingService) GetSubJsonObservatory

func (s *SettingService) GetSubJsonObservatory() (string, error)

func (*SettingService) GetSubJsonPath

func (s *SettingService) GetSubJsonPath() (string, error)

func (*SettingService) GetSubJsonRoutingRules

func (s *SettingService) GetSubJsonRoutingRules() (string, error)

func (*SettingService) GetSubJsonRules

func (s *SettingService) GetSubJsonRules() (string, error)

func (*SettingService) GetSubJsonURI

func (s *SettingService) GetSubJsonURI() (string, error)

func (*SettingService) GetSubJsonUserAgentRegex

func (s *SettingService) GetSubJsonUserAgentRegex() (string, error)

func (*SettingService) GetSubKeyFile

func (s *SettingService) GetSubKeyFile() (string, error)

func (*SettingService) GetSubListen

func (s *SettingService) GetSubListen() (string, error)

func (*SettingService) GetSubPath

func (s *SettingService) GetSubPath() (string, error)

func (*SettingService) GetSubPort

func (s *SettingService) GetSubPort() (int, error)

func (*SettingService) GetSubProfileMode

func (s *SettingService) GetSubProfileMode() (string, error)

func (*SettingService) GetSubProfileUrl

func (s *SettingService) GetSubProfileUrl() (string, error)

func (*SettingService) GetSubRoutingRules

func (s *SettingService) GetSubRoutingRules() (string, error)
func (s *SettingService) GetSubShowIdentityOnAllLinks() (bool, error)

func (*SettingService) GetSubSupportUrl

func (s *SettingService) GetSubSupportUrl() (string, error)

func (*SettingService) GetSubThemeDir

func (s *SettingService) GetSubThemeDir() (string, error)

func (*SettingService) GetSubTitle

func (s *SettingService) GetSubTitle() (string, error)

func (*SettingService) GetSubTrafficDepletedTemplate

func (s *SettingService) GetSubTrafficDepletedTemplate() (string, error)

func (*SettingService) GetSubURI

func (s *SettingService) GetSubURI() (string, error)

func (*SettingService) GetSubUpdates

func (s *SettingService) GetSubUpdates() (string, error)

func (*SettingService) GetTelemtSubscriptionProxyEnable added in v3.9.8

func (s *SettingService) GetTelemtSubscriptionProxyEnable() (bool, error)

GetTelemtSubscriptionProxyEnable reports whether personal Telemt proxies should be created and exposed for subscriptions.

func (*SettingService) GetTgBotAPIServer

func (s *SettingService) GetTgBotAPIServer() (string, error)

func (*SettingService) GetTgBotBackup

func (s *SettingService) GetTgBotBackup() (bool, error)

func (*SettingService) GetTgBotChatId

func (s *SettingService) GetTgBotChatId() (string, error)

func (*SettingService) GetTgBotProxy

func (s *SettingService) GetTgBotProxy() (string, error)

func (*SettingService) GetTgBotToken

func (s *SettingService) GetTgBotToken() (string, error)

func (*SettingService) GetTgCpu

func (s *SettingService) GetTgCpu() (int, error)

func (*SettingService) GetTgEnabledEvents

func (s *SettingService) GetTgEnabledEvents() (string, error)

func (*SettingService) GetTgLang

func (s *SettingService) GetTgLang() (string, error)

func (*SettingService) GetTgMemory

func (s *SettingService) GetTgMemory() (int, error)

func (*SettingService) GetTgbotEnabled

func (s *SettingService) GetTgbotEnabled() (bool, error)

func (*SettingService) GetTgbotRuntime

func (s *SettingService) GetTgbotRuntime() (string, error)

func (*SettingService) GetTimeLocation

func (s *SettingService) GetTimeLocation() (*time.Location, error)

func (*SettingService) GetTrafficDiff

func (s *SettingService) GetTrafficDiff() (int, error)

func (*SettingService) GetTrustedProxyCIDRs

func (s *SettingService) GetTrustedProxyCIDRs() (string, error)

func (*SettingService) GetTwoFactorEnable

func (s *SettingService) GetTwoFactorEnable() (bool, error)

func (*SettingService) GetTwoFactorToken

func (s *SettingService) GetTwoFactorToken() (string, error)

func (*SettingService) GetVKTurnProxyManualStop added in v3.9.2

func (s *SettingService) GetVKTurnProxyManualStop() (bool, error)

func (*SettingService) GetVKTurnProxyReleaseTag added in v3.9.2

func (s *SettingService) GetVKTurnProxyReleaseTag() (string, error)

func (*SettingService) GetWarp

func (s *SettingService) GetWarp() (string, error)

func (*SettingService) GetWarpLastUpdate

func (s *SettingService) GetWarpLastUpdate() (int64, error)

func (*SettingService) GetWebDomain

func (s *SettingService) GetWebDomain() (string, error)

func (*SettingService) GetXrayConfigTemplate

func (s *SettingService) GetXrayConfigTemplate() (string, error)

func (*SettingService) GetXrayOutboundTestUrl

func (s *SettingService) GetXrayOutboundTestUrl() (string, error)

func (*SettingService) HiddifySubscriptionURI added in v3.9.6

func (s *SettingService) HiddifySubscriptionURI(alias HiddifyLegacySubscriptionAlias, publicOrigin string) (string, error)

HiddifySubscriptionURI uses the path from the backup and the public origin chosen by the operator. An empty origin reuses the current subscription origin, then falls back to the first domain present in the backup.

func (*SettingService) IsHiddifySubscriptionPath added in v3.9.7

func (s *SettingService) IsHiddifySubscriptionPath(subID, requestPath string) bool

func (*SettingService) LoadMasterClientCert

func (s *SettingService) LoadMasterClientCert() (crypto.CertKeyPEM, error)

LoadMasterClientCert returns only the already-persisted credential. It never mints or changes CA/client settings.

func (*SettingService) MigrateHiddifySubscriptionURLs added in v3.9.7

func (s *SettingService) MigrateHiddifySubscriptionURLs() (int, error)

MigrateHiddifySubscriptionURLs undoes older imports that stored the Hiddify path in the global subURI. Only records matching the old importer identity receive the legacy URL; everyone else returns to the normal listener URL.

func (*SettingService) NewProxiedHTTPClient

func (s *SettingService) NewProxiedHTTPClient(timeout time.Duration) *http.Client

NewProxiedHTTPClient returns an HTTP client that routes the panel's own outbound requests through the configured panel outbound (via the loopback SOCKS bridge in the running Xray). When the feature is off or the bridge is unavailable it falls back to a direct client.

func (*SettingService) NodeEgressProxyURL

func (s *SettingService) NodeEgressProxyURL(nodeID int) string

func (*SettingService) NodeMtlsClientCAPool

func (s *SettingService) NodeMtlsClientCAPool() (*x509.CertPool, error)

NodeMtlsClientCAPool builds the trust pool used as the panel listener's ClientCAs for incoming node-API client certificates. It returns (nil, nil) when no trust CA is configured, so mTLS stays off and the listener behaves exactly as before.

func (*SettingService) PanelEgressProxyURL

func (s *SettingService) PanelEgressProxyURL() string

PanelEgressProxyURL resolves the loopback SOCKS bridge that the generated config exposes when a panel outbound is configured (see injectPanelEgress). It returns "" — meaning a direct connection — when the feature is off or the bridge is not present in the running core yet.

func (*SettingService) ResetSettings

func (s *SettingService) ResetSettings() error

func (*SettingService) SaveHiddifySubscriptionURL added in v3.9.6

func (s *SettingService) SaveHiddifySubscriptionURL(alias HiddifyLegacySubscriptionAlias, uri string, items []ClientCreatePayload) error

func (*SettingService) SetBasePath

func (s *SettingService) SetBasePath(basePath string) error

func (*SettingService) SetCertFile

func (s *SettingService) SetCertFile(webCertFile string) error

func (*SettingService) SetCoreType

func (s *SettingService) SetCoreType(coreType string) error

func (*SettingService) SetDevChannelEnable

func (s *SettingService) SetDevChannelEnable(value bool) error

func (*SettingService) SetDiscordAdminIds

func (s *SettingService) SetDiscordAdminIds(value string) error

func (*SettingService) SetDiscordBotBackup

func (s *SettingService) SetDiscordBotBackup(value bool) error

func (*SettingService) SetDiscordBotEnable

func (s *SettingService) SetDiscordBotEnable(value bool) error

func (*SettingService) SetDiscordBotToken

func (s *SettingService) SetDiscordBotToken(value string) error

func (*SettingService) SetDiscordChannelId

func (s *SettingService) SetDiscordChannelId(value string) error

func (*SettingService) SetDiscordCpu

func (s *SettingService) SetDiscordCpu(value int) error

func (*SettingService) SetDiscordEnabledEvents

func (s *SettingService) SetDiscordEnabledEvents(events string) error

func (*SettingService) SetDiscordLang

func (s *SettingService) SetDiscordLang(value string) error

func (*SettingService) SetDiscordMemory

func (s *SettingService) SetDiscordMemory(value int) error

func (*SettingService) SetDiscordRunTime

func (s *SettingService) SetDiscordRunTime(value string) error

func (*SettingService) SetExternalTrafficInformEnable

func (s *SettingService) SetExternalTrafficInformEnable(value bool) error

func (*SettingService) SetExternalTrafficInformURI

func (s *SettingService) SetExternalTrafficInformURI(InformURI string) error

func (*SettingService) SetKeyFile

func (s *SettingService) SetKeyFile(webKeyFile string) error

func (*SettingService) SetListen

func (s *SettingService) SetListen(ip string) error

func (*SettingService) SetNord

func (s *SettingService) SetNord(data string) error

func (*SettingService) SetOutboundDownThreshold

func (s *SettingService) SetOutboundDownThreshold(value int) error

func (*SettingService) SetPanelOutbound

func (s *SettingService) SetPanelOutbound(tag string) error

func (*SettingService) SetPia

func (s *SettingService) SetPia(data string) error

func (*SettingService) SetPort

func (s *SettingService) SetPort(port int) error

func (*SettingService) SetRestartXrayOnClientDisable

func (s *SettingService) SetRestartXrayOnClientDisable(value bool) error

func (*SettingService) SetSingBoxConfigTemplate added in v3.9.2

func (s *SettingService) SetSingBoxConfigTemplate(value string) error

func (*SettingService) SetSmtpCpu

func (s *SettingService) SetSmtpCpu(value int) error

func (*SettingService) SetSmtpEnable

func (s *SettingService) SetSmtpEnable(value bool) error

func (*SettingService) SetSmtpEnabledEvents

func (s *SettingService) SetSmtpEnabledEvents(events string) error

func (*SettingService) SetSmtpEncryptionType

func (s *SettingService) SetSmtpEncryptionType(value string) error

func (*SettingService) SetSmtpFrom

func (s *SettingService) SetSmtpFrom(value string) error

func (*SettingService) SetSmtpFromName

func (s *SettingService) SetSmtpFromName(value string) error

func (*SettingService) SetSmtpHost

func (s *SettingService) SetSmtpHost(value string) error

func (*SettingService) SetSmtpMemory

func (s *SettingService) SetSmtpMemory(value int) error

func (*SettingService) SetSmtpPassword

func (s *SettingService) SetSmtpPassword(value string) error

func (*SettingService) SetSmtpPort

func (s *SettingService) SetSmtpPort(value int) error

func (*SettingService) SetSmtpTo

func (s *SettingService) SetSmtpTo(value string) error

func (*SettingService) SetSmtpUsername

func (s *SettingService) SetSmtpUsername(value string) error

func (*SettingService) SetSubCertFile

func (s *SettingService) SetSubCertFile(subCertFile string) error

func (*SettingService) SetSubKeyFile

func (s *SettingService) SetSubKeyFile(subKeyFile string) error

func (*SettingService) SetTelemtSubscriptionProxyEnable added in v3.9.8

func (s *SettingService) SetTelemtSubscriptionProxyEnable(enabled bool) error

SetTelemtSubscriptionProxyEnable persists the administrator's preference. The Telemt controller reconciles all existing subscription users before the new value is committed, so saved subscription pages follow this setting too.

func (*SettingService) SetTgBotAPIServer

func (s *SettingService) SetTgBotAPIServer(token string) error

func (*SettingService) SetTgBotChatId

func (s *SettingService) SetTgBotChatId(chatIds string) error

func (*SettingService) SetTgBotProxy

func (s *SettingService) SetTgBotProxy(token string) error

func (*SettingService) SetTgBotToken

func (s *SettingService) SetTgBotToken(token string) error

func (*SettingService) SetTgEnabledEvents

func (s *SettingService) SetTgEnabledEvents(events string) error

func (*SettingService) SetTgMemory

func (s *SettingService) SetTgMemory(value int) error

func (*SettingService) SetTgbotEnabled

func (s *SettingService) SetTgbotEnabled(value bool) error

func (*SettingService) SetTgbotRuntime

func (s *SettingService) SetTgbotRuntime(time string) error

func (*SettingService) SetTwoFactorEnable

func (s *SettingService) SetTwoFactorEnable(value bool) error

func (*SettingService) SetTwoFactorToken

func (s *SettingService) SetTwoFactorToken(value string) error

func (*SettingService) SetVKTurnProxyManualStop added in v3.9.2

func (s *SettingService) SetVKTurnProxyManualStop(value bool) error

func (*SettingService) SetVKTurnProxyReleaseTag added in v3.9.2

func (s *SettingService) SetVKTurnProxyReleaseTag(value string) error

func (*SettingService) SetWarp

func (s *SettingService) SetWarp(data string) error

func (*SettingService) SetWarpLastUpdate

func (s *SettingService) SetWarpLastUpdate(val int64) error

func (*SettingService) SetWarpUpdateInterval

func (s *SettingService) SetWarpUpdateInterval(val int) error

func (*SettingService) SetXrayOutboundTestUrl

func (s *SettingService) SetXrayOutboundTestUrl(url string) error

func (*SettingService) UpdateAllSetting

func (s *SettingService) UpdateAllSetting(allSetting *entity.AllSetting, clears SecretClears) error

func (*SettingService) UpdateSecret

func (s *SettingService) UpdateSecret(key string, value string) error

func (*SettingService) VerifyTwoFactorCode

func (s *SettingService) VerifyTwoFactorCode(code string) error

type SingBoxEditorSnapshot added in v3.9.2

type SingBoxEditorSnapshot struct {
	Config          map[string]any `json:"config"`
	Running         bool           `json:"running"`
	Version         string         `json:"version"`
	ConfigPath      string         `json:"configPath"`
	ConfigSource    string         `json:"configSource"`
	ConfigModified  string         `json:"configModified"`
	ManagedSections []string       `json:"managedSections"`
}

type SingBoxService

type SingBoxService struct{}

func (*SingBoxService) ApplyPendingRestart added in v3.9.8

func (s *SingBoxService) ApplyPendingRestart(ctx context.Context)

ApplyPendingRestart reloads sing-box only while it is the selected, currently running core. If sing-box is absent/stopped there is no process to reload; a later explicit start writes the current config.

func (*SingBoxService) BinaryPath

func (s *SingBoxService) BinaryPath() string

func (*SingBoxService) CachedVersion

func (s *SingBoxService) CachedVersion(ctx context.Context) (string, error)

func (*SingBoxService) ConnectionCount

func (s *SingBoxService) ConnectionCount(ctx context.Context) (int, error)

func (*SingBoxService) DidCrash added in v3.9.8

func (s *SingBoxService) DidCrash() bool

DidCrash distinguishes an unexpected process exit from the two normal stopped states: the operator explicitly stopped sing-box, or sing-box is not installed on this panel/node at all.

func (*SingBoxService) DisconnectClientIPs

func (s *SingBoxService) DisconnectClientIPs(ctx context.Context, email string, ips []string) error

func (*SingBoxService) GetConfig

func (s *SingBoxService) GetConfig() (*singbox.Config, error)

func (*SingBoxService) GetEditorConfig added in v3.9.2

func (s *SingBoxService) GetEditorConfig(ctx context.Context) (*SingBoxEditorSnapshot, error)

func (*SingBoxService) GetLogs

func (s *SingBoxService) GetLogs(count string, filter string) []LogEntry

GetLogs returns sing-box process log lines in the same shape used by the existing core log viewer. sing-box writes its structured runtime log to the panel-managed log file configured in Config.Log.output.

func (*SingBoxService) InstallLatest

func (s *SingBoxService) InstallLatest(ctx context.Context) (string, error)

func (*SingBoxService) InstallVersion

func (s *SingBoxService) InstallVersion(ctx context.Context, version string) (string, error)

func (*SingBoxService) Installed added in v3.9.8

func (s *SingBoxService) Installed() bool

Installed reports whether the configured sing-box binary exists as a regular filesystem entry. It deliberately avoids executing the binary, so status/watchdog checks stay cheap when sing-box is not installed.

func (*SingBoxService) IsNeedRestartAndSetFalse added in v3.9.8

func (s *SingBoxService) IsNeedRestartAndSetFalse() bool

IsNeedRestartAndSetFalse consumes the pending sing-box restart flag.

func (*SingBoxService) IsRunning

func (s *SingBoxService) IsRunning() bool

func (*SingBoxService) LastError

func (s *SingBoxService) LastError() error

func (*SingBoxService) ListVersions

func (s *SingBoxService) ListVersions(ctx context.Context) ([]singbox.ReleaseVersion, error)

func (*SingBoxService) OnlineClientIPs

func (s *SingBoxService) OnlineClientIPs(ctx context.Context) (map[string]map[string]struct{}, error)

func (*SingBoxService) OnlinePresence added in v3.9.8

func (s *SingBoxService) OnlinePresence(ctx context.Context) (map[string]map[string]struct{}, []string, error)

OnlinePresence returns the current sing-box users/IPs and active inbound tags from one native API snapshot. Keeping them in the same snapshot makes the node's online user and inbound indicators describe the same instant.

func (*SingBoxService) PollTraffic

func (s *SingBoxService) PollTraffic(ctx context.Context) error

func (*SingBoxService) ProcessConfigPath

func (s *SingBoxService) ProcessConfigPath() string

func (*SingBoxService) ResetTemplate added in v3.9.2

func (s *SingBoxService) ResetTemplate(ctx context.Context) error

func (*SingBoxService) Restart

func (s *SingBoxService) Restart(ctx context.Context) error

func (*SingBoxService) SaveTemplate added in v3.9.2

func (s *SingBoxService) SaveTemplate(ctx context.Context, raw string) error

func (*SingBoxService) SetToNeedRestart added in v3.9.8

func (s *SingBoxService) SetToNeedRestart()

SetToNeedRestart marks the selected sing-box runtime config as stale. Inbound/client controllers use the same deferred-restart model as Xray so a burst of node-sync mutations is collapsed into one core reload.

func (*SingBoxService) Start

func (s *SingBoxService) Start(ctx context.Context) error

func (*SingBoxService) Stop

func (s *SingBoxService) Stop(ctx context.Context) error

func (*SingBoxService) Uninstall

func (s *SingBoxService) Uninstall(ctx context.Context) error

func (*SingBoxService) Validate

func (s *SingBoxService) Validate(ctx context.Context) error

func (*SingBoxService) Version

func (s *SingBoxService) Version(ctx context.Context) (string, error)

func (*SingBoxService) WriteConfig

func (s *SingBoxService) WriteConfig() error

type Status

type Status struct {
	T           time.Time `json:"-"`
	Cpu         float64   `json:"cpu"`
	CpuCores    int       `json:"cpuCores"`
	LogicalPro  int       `json:"logicalPro"`
	CpuSpeedMhz float64   `json:"cpuSpeedMhz"`
	Mem         struct {
		Current uint64 `json:"current"`
		Total   uint64 `json:"total"`
	} `json:"mem"`
	Swap struct {
		Current uint64 `json:"current"`
		Total   uint64 `json:"total"`
	} `json:"swap"`
	Disk struct {
		Current uint64 `json:"current"`
		Total   uint64 `json:"total"`
	} `json:"disk"`
	DiskIO struct {
		Read  uint64 `json:"read"`
		Write uint64 `json:"write"`
	} `json:"diskIO"`
	DiskTraffic struct {
		Read  uint64 `json:"read"`
		Write uint64 `json:"write"`
	} `json:"diskTraffic"`
	Xray struct {
		State    ProcessState `json:"state"`
		ErrorMsg string       `json:"errorMsg"`
		Version  string       `json:"version"`
	} `json:"xray"`
	VKTurnProxy VKTurnProxyRuntimeStatus `json:"vkTurnProxy"`
	// AmneziaWG gates the overview's AmneziaWG log view: Configured stays true
	// while an inbound exists but its embedded interface isn't up yet, which
	// is exactly when that view's event lines are worth reading.
	AmneziaWG struct {
		Configured bool `json:"configured"`
		Running    bool `json:"running"`
	} `json:"amneziawg"`
	PanelVersion string    `json:"panelVersion"`
	PanelGuid    string    `json:"panelGuid"`
	Uptime       uint64    `json:"uptime"`
	Loads        []float64 `json:"loads"`
	TcpCount     int       `json:"tcpCount"`
	UdpCount     int       `json:"udpCount"`
	NetIO        struct {
		Up      uint64 `json:"up"`
		Down    uint64 `json:"down"`
		PktUp   uint64 `json:"pktUp"`
		PktDown uint64 `json:"pktDown"`
	} `json:"netIO"`
	NetTraffic struct {
		Sent    uint64 `json:"sent"`
		Recv    uint64 `json:"recv"`
		PktSent uint64 `json:"pktSent"`
		PktRecv uint64 `json:"pktRecv"`
	} `json:"netTraffic"`
	PublicIP struct {
		IPv4 string `json:"ipv4"`
		IPv6 string `json:"ipv6"`
	} `json:"publicIP"`
	AppStats struct {
		Threads uint32 `json:"threads"`
		Mem     uint64 `json:"mem"`
		Uptime  uint64 `json:"uptime"`
	} `json:"appStats"`
	CoreUptime uint64 `json:"coreUptime"`
}

Status represents comprehensive system and application status information. It includes CPU, memory, disk, network statistics, and Xray process status.

type SubBalancerService

type SubBalancerService struct{}

SubBalancerService manages client-side JSON-subscription balancers; rows are read per request by internal/sub, so mutations need no xray restart.

func (*SubBalancerService) Create

func (s *SubBalancerService) Create(balancer *model.SubBalancer) (*model.SubBalancer, error)

func (*SubBalancerService) Delete

func (s *SubBalancerService) Delete(id int) error

func (*SubBalancerService) Get

func (s *SubBalancerService) Get(id int) (*model.SubBalancer, error)

func (*SubBalancerService) List

func (s *SubBalancerService) List() ([]*model.SubBalancer, error)

List returns all balancers in subscription order.

func (*SubBalancerService) Update

func (s *SubBalancerService) Update(id int, balancer *model.SubBalancer, enabled *bool) (*model.SubBalancer, error)

type SubLinkProvider

type SubLinkProvider interface {
	SubLinksForSubId(host, subId string) ([]string, error)
	LinksForClient(host string, inbound *model.Inbound, email string) []string
	LinksForInbounds(host string, inbounds []*model.Inbound) []string
}

type TelemtConfig

type TelemtConfig struct {
	Enabled      bool   `json:"enabled"`
	Port         int    `json:"port"`
	Secret       string `json:"secret"`
	IPv4         bool   `json:"ipv4"`
	IPv6         bool   `json:"ipv6"`
	FastMode     bool   `json:"fastMode"`
	Classic      bool   `json:"classic"`
	Secure       bool   `json:"secure"`
	TLS          bool   `json:"tls"`
	SNI          string `json:"sni"`
	UpstreamType string `json:"upstreamType"`
}

type TelemtConnection

type TelemtConnection struct {
	Username           string             `json:"username"`
	CurrentConnections int                `json:"currentConnections"`
	ActiveIPs          []TelemtIPLocation `json:"activeIPs"`
	RecentIPCount      int                `json:"recentIPCount"`
	FirstSeenAt        int64              `json:"firstSeenAt"`
	LastSeenAt         int64              `json:"lastSeenAt"`
	TotalBytes         int64              `json:"totalBytes"`
	Active             bool               `json:"active"`
}

type TelemtCreateRequest

type TelemtCreateRequest struct {
	Name string `json:"name"`
	Host string `json:"host"`
}

type TelemtIPLocation added in v3.9.2

type TelemtIPLocation struct {
	IP          string  `json:"ip"`
	City        string  `json:"city"`
	Region      string  `json:"region"`
	Country     string  `json:"country"`
	CountryCode string  `json:"countryCode"`
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
}

type TelemtProxy

type TelemtProxy struct {
	Name   string `json:"name"`
	Secret string `json:"secret"`
	Host   string `json:"host"`
	Port   int    `json:"port"`
	TLS    bool   `json:"tls"`
	Link   string `json:"link"`
}

type TelemtService

type TelemtService struct{}

func (TelemtService) Apply

func (TelemtService) Apply(action string) error

func (TelemtService) CaptureWebProxyEnableSnapshot added in v3.9.5

func (TelemtService) CaptureWebProxyEnableSnapshot() (WebProxyEnableSnapshot, error)

func (TelemtService) CleanupFailedWebProxyEnable added in v3.9.5

func (TelemtService) CleanupFailedWebProxyEnable()

func (TelemtService) CleanupWebProxyBackendDependency added in v3.9.5

func (TelemtService) CleanupWebProxyBackendDependency() error

func (TelemtService) ConnectedClients

func (TelemtService) ConnectedClients() ([]TelemtConnection, error)

func (TelemtService) CreateProxy

func (TelemtService) CreateProxy(req TelemtCreateRequest) (TelemtProxy, error)

func (TelemtService) CreateProxyAtomic added in v3.9.5

func (s TelemtService) CreateProxyAtomic(req TelemtCreateRequest) (TelemtProxy, error)

func (TelemtService) DeleteProxy

func (TelemtService) DeleteProxy(username string) error

func (TelemtService) DeleteSubscriptionProxy added in v3.9.8

func (TelemtService) DeleteSubscriptionProxy(subID string) error

DeleteSubscriptionProxy revokes the deterministic Telemt user assigned to a subscription. It never starts a stopped Telemt service. When the users API is unavailable, it removes the account from telemt.toml directly and only restarts Telemt when the service was already active.

func (TelemtService) DisableWebProxy added in v3.9.3

func (TelemtService) DisableWebProxy() error

func (TelemtService) EnableWebProxy added in v3.9.3

func (TelemtService) EnableWebProxy(ctx context.Context, domain, panelCert, panelKey string) (TelemtWebProxyStatus, error)

func (TelemtService) EnsureSubscriptionProxy added in v3.9.8

func (TelemtService) EnsureSubscriptionProxy(subID, host string) (TelemtProxy, error)

EnsureSubscriptionProxy returns the stable Telemt proxy assigned to subID, creating it on first use. Repeated calls keep the same username and secret. It never starts a stopped Telemt service. When Telemt is installed but stopped, the user is persisted to telemt.toml and becomes active on the next normal start.

func (TelemtService) EnsureWebProxyBackend added in v3.9.5

func (TelemtService) EnsureWebProxyBackend() error

func (TelemtService) GetConfig

func (TelemtService) GetConfig() (TelemtConfig, error)

fix by meko

func (TelemtService) IsEnabled added in v3.9.8

func (TelemtService) IsEnabled() bool

IsEnabled reports whether the Telemt systemd service is enabled without performing the heavier status/version checks used by Status().

func (TelemtService) ListProxies

func (TelemtService) ListProxies() ([]TelemtProxy, error)

func (TelemtService) ReconcileSubscriptionProxies added in v3.9.8

func (TelemtService) ReconcileSubscriptionProxies(enabled bool, host string) error

ReconcileSubscriptionProxies makes the deterministic Telemt accounts match every subscription already stored by the panel. Enabling provisions missing accounts for all saved subscription IDs; disabling revokes every generated subscription account, including stale accounts left by subscriptions that no longer exist in the database.

The operation is intentionally idempotent so it can also be used to repair a partially-applied previous attempt without creating duplicate Telemt users.

func (TelemtService) RestoreFailedWebProxyEnable added in v3.9.5

func (TelemtService) RestoreFailedWebProxyEnable(snapshot WebProxyEnableSnapshot) error

func (TelemtService) RestoreWebProxyEnableSnapshot added in v3.9.5

func (s TelemtService) RestoreWebProxyEnableSnapshot(snapshot WebProxyEnableSnapshot) error

func (TelemtService) SaveConfig

func (TelemtService) SaveConfig(c TelemtConfig) error

func (TelemtService) SaveConfigAtomic added in v3.9.5

func (s TelemtService) SaveConfigAtomic(c TelemtConfig) error

func (TelemtService) Status

func (TelemtService) Status() TelemtStatus

func (TelemtService) WebProxyNginxInstalled added in v3.9.5

func (TelemtService) WebProxyNginxInstalled() bool

func (TelemtService) WebProxyStatus added in v3.9.3

func (TelemtService) WebProxyStatus(defaultDomain, panelCert, panelKey string) (TelemtWebProxyStatus, error)

func (TelemtService) WebProxyTelemtActive added in v3.9.5

func (TelemtService) WebProxyTelemtActive() bool

type TelemtStatus

type TelemtStatus struct {
	Installed       bool                 `json:"installed"`
	Active          bool                 `json:"active"`
	Enabled         bool                 `json:"enabled"`
	Configured      bool                 `json:"configured"`
	Version         string               `json:"version"`
	LatestVersion   string               `json:"latestVersion"`
	UpdateAvailable bool                 `json:"updateAvailable"`
	MekoEnabled     bool                 `json:"mekoEnabled"`
	WebProxy        TelemtWebProxyStatus `json:"webProxy"`
}

type TelemtWebProxyState added in v3.9.3

type TelemtWebProxyState struct {
	Enabled            bool   `json:"enabled"`
	Domain             string `json:"domain"`
	Secret             string `json:"secret"`
	DecoyDir           string `json:"decoyDir"`
	ListenPort         int    `json:"listenPort"`
	CertFile           string `json:"certFile"`
	KeyFile            string `json:"keyFile"`
	PublicAddr         string `json:"publicAddr,omitempty"`
	NginxManaged       bool   `json:"nginxManaged,omitempty"`
	NginxWasEnabled    bool   `json:"nginxWasEnabled,omitempty"`
	NginxStateCaptured bool   `json:"nginxStateCaptured,omitempty"`
}

type TelemtWebProxyStatus added in v3.9.3

type TelemtWebProxyStatus struct {
	Enabled          bool   `json:"enabled"`
	Supported        bool   `json:"supported"`
	Domain           string `json:"domain"`
	DefaultDomain    string `json:"defaultDomain"`
	Link             string `json:"link"`
	NginxInstalled   bool   `json:"nginxInstalled"`
	NginxActive      bool   `json:"nginxActive"`
	CertificateReady bool   `json:"certificateReady"`
	CertificateFile  string `json:"certificateFile"`
	ListenPort       int    `json:"listenPort"`
	Port443Available bool   `json:"port443Available"`
	Port443Owner     string `json:"port443Owner"`
	Error            string `json:"error"`
}

type VKTurnProxyClient added in v3.9.2

type VKTurnProxyClient struct {
	ID            string                 `json:"id"`
	Email         string                 `json:"email"`
	Enable        bool                   `json:"enable"`
	Comment       string                 `json:"comment,omitempty"`
	LimitIP       int                    `json:"limitIp,omitempty"`
	TotalGB       int64                  `json:"totalGB,omitempty"`
	ExpiryTime    int64                  `json:"expiryTime,omitempty"`
	TgID          int64                  `json:"tgId,omitempty"`
	SubID         string                 `json:"subId,omitempty"`
	Reset         int                    `json:"reset,omitempty"`
	CreatedAt     int64                  `json:"created_at,omitempty"`
	UpdatedAt     int64                  `json:"updated_at,omitempty"`
	Link          string                 `json:"link"`
	Links         []string               `json:"links,omitempty"`
	LinkSecondary string                 `json:"linkSecondary,omitempty"`
	PeerPublicKey string                 `json:"peerPublicKey"`
	PeerManaged   bool                   `json:"peerManaged,omitempty"`
	Peer          *VKTurnProxyClientPeer `json:"peer,omitempty"`
}

type VKTurnProxyClientCreateResult added in v3.9.2

type VKTurnProxyClientCreateResult struct {
	ClientID string `json:"clientId"`
	Email    string `json:"email"`
	Link     string `json:"link,omitempty"`
}

type VKTurnProxyClientPeer added in v3.9.2

type VKTurnProxyClientPeer struct {
	PrivateKey   string   `json:"privateKey"`
	PublicKey    string   `json:"publicKey"`
	PreSharedKey string   `json:"preSharedKey,omitempty"`
	AllowedIPs   []string `json:"allowedIPs,omitempty"`
	KeepAlive    int      `json:"keepAlive,omitempty"`
}

type VKTurnProxyExportedClient added in v3.9.2

type VKTurnProxyExportedClient struct {
	ClientID string `json:"clientId"`
	Email    string `json:"email"`
	Link     string `json:"link"`
}

type VKTurnProxyForward added in v3.9.2

type VKTurnProxyForward struct {
	Type               VKTurnProxyForwardType `json:"type"`
	WireGuardInboundID int                    `json:"wireguardInboundId,omitempty"`
	Hysteria2InboundID int                    `json:"hysteria2InboundId,omitempty"`
	Host               string                 `json:"host,omitempty"`
	Port               int                    `json:"port,omitempty"`
}

type VKTurnProxyForwardType added in v3.9.2

type VKTurnProxyForwardType string
const (
	VKTurnProxyForwardWireGuardInbound VKTurnProxyForwardType = "wireguardInbound"
	VKTurnProxyForwardHysteria2Inbound VKTurnProxyForwardType = "hysteria2Inbound"
	VKTurnProxyForwardExternal         VKTurnProxyForwardType = "external"
)

type VKTurnProxyManagedWGConfig added in v3.9.2

type VKTurnProxyManagedWGConfig struct {
	PrivateKey      string
	PublicKey       string
	Address         string
	ServerPublicKey string
	MTU             int
	Endpoint        string
}

VKTurnProxyManagedWGConfig is the provisioned WireGuard config of a managed vk-turn-proxy client, returned to the panel for DTLS self-enrollment.

type VKTurnProxyPeerBinding added in v3.9.2

type VKTurnProxyPeerBinding struct {
	InboundID     int    `json:"inboundId"`
	InboundRemark string `json:"inboundRemark"`
	ClientID      string `json:"clientId"`
	ClientEmail   string `json:"clientEmail"`
}

type VKTurnProxyPeerOption added in v3.9.2

type VKTurnProxyPeerOption struct {
	PublicKey  string                  `json:"publicKey"`
	AllowedIPs []string                `json:"allowedIPs"`
	Bound      *VKTurnProxyPeerBinding `json:"bound,omitempty"`
}

type VKTurnProxyPeerOptionsResponse added in v3.9.2

type VKTurnProxyPeerOptionsResponse struct {
	WireGuardInboundID int                     `json:"wireguardInboundId"`
	WireGuardRemark    string                  `json:"wireguardRemark"`
	Peers              []VKTurnProxyPeerOption `json:"peers"`
}

type VKTurnProxyRuntimeStatus added in v3.9.2

type VKTurnProxyRuntimeStatus struct {
	Available bool         `json:"available"`
	Total     int          `json:"total"`
	Enabled   int          `json:"enabled"`
	Running   int          `json:"running"`
	State     ProcessState `json:"state"`
	ErrorMsg  string       `json:"errorMsg"`
	Version   string       `json:"version"`
	Uptime    uint64       `json:"uptime"`
}

type VKTurnProxyService added in v3.9.2

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

func VKTurnProxyRuntime added in v3.9.2

func VKTurnProxyRuntime() *VKTurnProxyService

func (*VKTurnProxyService) EnsureRunning added in v3.9.2

func (s *VKTurnProxyService) EnsureRunning() error

func (*VKTurnProxyService) GetStatus added in v3.9.2

func (*VKTurnProxyService) HeartbeatStatesByInbound added in v3.9.2

func (s *VKTurnProxyService) HeartbeatStatesByInbound() map[int]map[string]vkturnproxy.HeartbeatState

func (*VKTurnProxyService) ReconcileClientPeers added in v3.9.2

func (s *VKTurnProxyService) ReconcileClientPeers() error

ReconcileClientPeers enforces per-client traffic/expiry limits by pulling or restoring the managed wireguard peers of every vk-turn-proxy inbound. Runs outside the process mutex so a wireguard inbound update can't stall the sidecar reconcile loop.

func (*VKTurnProxyService) RestartAll added in v3.9.2

func (s *VKTurnProxyService) RestartAll() error

func (*VKTurnProxyService) ShutdownForRestart added in v3.9.2

func (s *VKTurnProxyService) ShutdownForRestart() error

func (*VKTurnProxyService) StartAll added in v3.9.2

func (s *VKTurnProxyService) StartAll() error

func (*VKTurnProxyService) StopAll added in v3.9.2

func (s *VKTurnProxyService) StopAll() error

func (*VKTurnProxyService) UpdateBinary added in v3.9.2

func (s *VKTurnProxyService) UpdateBinary(version string) error

func (*VKTurnProxyService) UploadCustomBinary added in v3.9.2

func (s *VKTurnProxyService) UploadCustomBinary(reader io.Reader) error

type VKTurnProxySettings added in v3.9.2

type VKTurnProxySettings struct {
	Forward VKTurnProxyForward `json:"forward"`
	// VK call links shared by every client of this inbound. Per-client
	// link/links/linkSecondary may still override these on the export
	// path (legacy data and edge cases).
	Link                 string   `json:"link,omitempty"`
	Links                []string `json:"links,omitempty"`
	LinkSecondary        string   `json:"linkSecondary,omitempty"`
	SessionMode          string   `json:"sessionMode,omitempty"`
	LocalEndpoint        string   `json:"localEndpoint,omitempty"`
	WGDNS                string   `json:"wgDns,omitempty"`
	WGMTU                int      `json:"wgMtu,omitempty"`
	WGAllowedIPs         string   `json:"wgAllowedIps,omitempty"`
	Threads              int      `json:"threads,omitempty"`
	UseUDP               *bool    `json:"useUdp,omitempty"`
	NoObfuscation        *bool    `json:"noObfuscation,omitempty"`
	CredsGroupSize       int      `json:"credsGroupSize,omitempty"`
	WrapMode             string   `json:"wrapMode,omitempty"`
	WrapCipher           string   `json:"wrapCipher,omitempty"`
	WrapKeyHex           string   `json:"wrapKeyHex,omitempty"`
	WrapAcceptClientKeys *bool    `json:"wrapAcceptClientKeys,omitempty"`
	// Panel DTLS-provisioning: when PanelGRPC is set the relay dials the wingsv
	// panel so apps can self-enroll their wg config over the DTLS PROVISION path.
	// NodeID is this relay's id as registered in the panel. PanelCAPin is only
	// needed for a self-signed panel; a publicly trusted panel needs none.
	PanelGRPC     string              `json:"panelGrpc,omitempty"`
	NodeID        string              `json:"nodeId,omitempty"`
	PanelToken    string              `json:"panelToken,omitempty"`
	PanelCAPin    string              `json:"panelCaPin,omitempty"`
	PanelInsecure *bool               `json:"panelInsecure,omitempty"`
	Clients       []VKTurnProxyClient `json:"clients,omitempty"`
}

type WebProxyEnableSnapshot added in v3.9.5

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

type XrayMetricsService

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

func (*XrayMetricsService) AggregateMetric

func (s *XrayMetricsService) AggregateMetric(metric string, bucketSeconds, maxPoints int) []map[string]any

func (*XrayMetricsService) AggregateObservatory

func (s *XrayMetricsService) AggregateObservatory(tag string, bucketSeconds, maxPoints int) []map[string]any

func (*XrayMetricsService) HasObservatoryTag

func (s *XrayMetricsService) HasObservatoryTag(tag string) bool

func (*XrayMetricsService) ObservatorySnapshot

func (s *XrayMetricsService) ObservatorySnapshot() []ObsTagSnapshot

func (*XrayMetricsService) Sample

func (s *XrayMetricsService) Sample(t time.Time)

func (*XrayMetricsService) State

func (s *XrayMetricsService) State() xrayMetricsState

type XrayService

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

XrayService provides business logic for Xray process management. It handles starting, stopping, restarting Xray, and managing its configuration.

func (*XrayService) ApplyPendingRestart

func (s *XrayService) ApplyPendingRestart()

ApplyPendingRestart consumes the need-restart flag and restarts Xray. If the restart fails (for example GetXrayConfig hits a transient DB error and leaves the old process running), it re-arms the flag so the next tick retries instead of silently dropping the pending config change.

func (*XrayService) BeginXrayTrafficRead added in v3.9.7

func (s *XrayService) BeginXrayTrafficRead() ([]*xray.Traffic, []*xray.ClientTraffic, *XrayTrafficRead, error)

BeginXrayTrafficRead fetches one traffic delta while retaining the traffic mutex. The caller must Commit after the database write succeeds or Rollback after it fails, preventing a failed write from consuming the Xray delta.

func (*XrayService) DidXrayCrash

func (s *XrayService) DidXrayCrash() bool

DidXrayCrash checks if Xray crashed by verifying it's not running and wasn't manually stopped.

func (*XrayService) GetBalancersStatus

func (s *XrayService) GetBalancersStatus(tags []string) ([]BalancerStatus, error)

GetBalancersStatus queries the running core for the live state of the given balancer tags. Per-tag failures are reported as Running=false rather than failing the whole call, so the UI can render saved-but-not-applied balancers alongside live ones.

func (*XrayService) GetHeldBackConfig

func (s *XrayService) GetHeldBackConfig() string

GetHeldBackConfig returns why the running core still serves its previous config, or "" when the pending config was applied.

func (*XrayService) GetOnlineUsers

func (s *XrayService) GetOnlineUsers() ([]xray.OnlineUser, bool, error)

GetOnlineUsers returns connection-based online users (email + source IPs) from the running core's online-stats API. ok=false means the API is not available — xray isn't running or the core predates the online-stats RPCs — and callers must use the legacy traffic-delta / access-log paths. The capability is probed lazily per process: an Unimplemented answer pins this core as unsupported until the next restart, while transient errors leave the capability undecided so a flaky poll can't lock in legacy mode.

func (*XrayService) GetXrayAPIPort

func (s *XrayService) GetXrayAPIPort() int

GetXrayAPIPort returns the port the local xray process is listening on for its gRPC HandlerService, or 0 when xray isn't currently running. Exposed for the runtime package's LocalRuntime adapter without a service-package import cycle.

func (*XrayService) GetXrayConfig

func (s *XrayService) GetXrayConfig() (*xray.Config, error)

GetXrayConfig retrieves and builds the Xray configuration from settings and inbounds.

func (*XrayService) GetXrayErr

func (s *XrayService) GetXrayErr() error

GetXrayErr returns the error from the Xray process, if any.

func (*XrayService) GetXrayResult

func (s *XrayService) GetXrayResult() string

GetXrayResult returns the result string from the Xray process.

func (*XrayService) GetXrayTraffic

func (s *XrayService) GetXrayTraffic() ([]*xray.Traffic, []*xray.ClientTraffic, error)

GetXrayTraffic fetches the current traffic statistics from the running Xray process.

func (*XrayService) GetXrayVersion

func (s *XrayService) GetXrayVersion() string

GetXrayVersion returns the version of the running Xray process.

func (*XrayService) IsNeedRestartAndSetFalse

func (s *XrayService) IsNeedRestartAndSetFalse() bool

IsNeedRestartAndSetFalse checks if restart is needed and resets the flag to false.

func (*XrayService) IsXrayRunning

func (s *XrayService) IsXrayRunning() bool

IsXrayRunning checks if the Xray process is currently running.

func (*XrayService) OverrideBalancer

func (s *XrayService) OverrideBalancer(tag, target string) error

OverrideBalancer forces a balancer in the running core to use the given outbound tag; an empty target clears the override. When target names another balancer, the override resolves to the loopback outbound that routes traffic through the target balancer via the routing rules.

func (*XrayService) RestartXray

func (s *XrayService) RestartXray(isForce bool) error

RestartXray reconciles the running Xray process with the current desired config. When isForce is false it first tries to apply the changes through the Xray gRPC API without restarting the process (inbounds, outbounds and routing rules/balancers are hot-reloadable); only changes the core cannot take at runtime — or a force request — stop and restart the process.

func (*XrayService) SetToNeedRestart

func (s *XrayService) SetToNeedRestart()

SetToNeedRestart marks that Xray needs to be restarted.

func (*XrayService) StopXray

func (s *XrayService) StopXray() error

StopXray stops the running Xray process.

func (*XrayService) TestRoute

TestRoute asks the running core which outbound its router picks for the described connection.

type XraySettingService

type XraySettingService struct {
	SettingService
}

XraySettingService provides business logic for Xray configuration management. It handles validation and storage of Xray template configurations.

func (*XraySettingService) CheckXrayConfig

func (s *XraySettingService) CheckXrayConfig(XrayTemplateConfig string) error

func (*XraySettingService) PropagateInboundTagRename

func (s *XraySettingService) PropagateInboundTagRename(oldTag, newTag string) (bool, error)

PropagateInboundTagRename rewrites routing rules and loopback outbound references when a panel inbound tag changes.

func (*XraySettingService) RemoveInboundTagReferences

func (s *XraySettingService) RemoveInboundTagReferences(deletedTag string) (bool, error)

RemoveInboundTagReferences drops a deleted inbound tag from routing rules. Rules that only matched that inbound are removed; rules with additional matchers keep the rule and only lose the inboundTag entry.

func (*XraySettingService) SaveXraySetting

func (s *XraySettingService) SaveXraySetting(newXraySettings string) error

func (*XraySettingService) UpdateWarpXraySetting

func (s *XraySettingService) UpdateWarpXraySetting(warpData map[string]string, warpConfig map[string]any) error

type XrayTrafficRead added in v3.9.7

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

XrayTrafficRead keeps the Xray absolute-counter baseline locked until the matching database transaction is either committed or rolled back.

func (*XrayTrafficRead) Commit added in v3.9.7

func (r *XrayTrafficRead) Commit()

func (*XrayTrafficRead) Rollback added in v3.9.7

func (r *XrayTrafficRead) Rollback()

Source Files

Directories

Path Synopsis
Package service: WebSocketService owns the per-connection pump goroutines and bridges the HTTP-layer controller to the broadcast hub.
Package service: WebSocketService owns the per-connection pump goroutines and bridges the HTTP-layer controller to the broadcast hub.

Jump to

Keyboard shortcuts

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