service

package
v4.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 20, 2026 License: MIT Imports: 60 Imported by: 0

Documentation

Index

Constants

View Source
const (
	AgentDiagnosticActionServiceStatus  = "service_status"
	AgentDiagnosticActionServiceRestart = "service_restart"
	AgentDiagnosticActionLogTail        = "log_tail"

	// AgentDiagnosticLogTailMaxLines 限制 log_tail 一次最多返回的行数,避免超大输出。
	AgentDiagnosticLogTailMaxLines = 1000
)
View Source
const (
	ForwardNodeInventoryScopeAny     = ""
	ForwardNodeInventoryScopeNodeX   = "nodex"
	ForwardNodeInventoryScopeAnsible = "ansible"

	ForwardNodeInventoryTagAnsibleMachine = "ansible-machine"
)
View Source
const (
	PluginWebUIMenuParentServices   = "services"
	PluginWebUIMenuParentOperations = "operations"
	PluginWebUIMenuParentSystem     = "system"
	PluginWebUIMenuParentExtensions = "extensions"
)

WebUI menu parents are a stable shell contract. Unknown, but otherwise valid, package metadata is kept visible in the explicit extensions bucket instead of creating an unregistered top-level navigation section.

View Source
const (
	NodeLogLevelDebug   = "debug"
	NodeLogLevelInfo    = "info"
	NodeLogLevelWarning = "warning"
	NodeLogLevelError   = "error"
)
View Source
const (
	CacheKeyNodeList      = "nodes:list"
	CacheKeyNode          = "node:"           // + nodeID
	CacheKeyNodeProtocols = "node:protocols:" // + nodeID
)

缓存键

View Source
const (
	PluginPermissionModeLegacy        = "legacy"
	PluginPermissionModeAuthoritative = "authoritative"
	PluginPermissionModeMixed         = "mixed"
)
View Source
const (
	CacheKeyDashboardStats   = "stats:dashboard"
	CacheKeyUserStats        = "stats:users"
	CacheKeyOrderStats       = "stats:orders"
	CacheKeyNodeStats        = "stats:nodes"
	CacheKeyUserSubscription = "user:subscription:" // + userID
)

缓存键

View Source
const (
	StatsCacheTTL        = 60 * time.Second // 统计数据缓存 60 秒
	SubscriptionCacheTTL = 30 * time.Second // 用户订阅缓存 30 秒
	NodeCacheTTL         = 10 * time.Second // 节点缓存 10 秒
)

缓存过期时间

View Source
const AgentPluginInstallAPIVersion = "anixops.io/plugin-install/v1alpha1"
View Source
const KernelOperationEnvelopeVersion = "anixops.operation/v1"
View Source
const MaxPluginArtifactBytes = 64 << 20
View Source
const PluginAPIGrantResourceType = "plugin_api"
View Source
const SensitiveSystemConfigPlaceholder = "********"
View Source
const SystemConfigKeySubscribeDomains = "app.subscribe_domains"

Variables

View Source
var (
	ErrAgentPluginAssignmentDenied   = errors.New("node is not assigned the requested plugin release")
	ErrAgentPluginAssignmentConflict = errors.New("node plugin assignments have conflicting desired state")
	ErrAgentPluginReleaseIntegrity   = errors.New("agent plugin release integrity check failed")
	ErrAgentPluginReconcileNotReady  = errors.New("agent plugin assignment is not ready to reconcile")
	ErrAgentPluginAddressMismatch    = errors.New("agent plugin download address does not match verified content")
)
View Source
var (
	ErrForwardCleanAgentUnauthorized = errors.New("invalid forward clean agent credentials")
	ErrForwardCleanAgentRevoked      = errors.New("forward clean agent revoked")
)
View Source
var (
	ErrInvalidKernelOperationID    = errors.New("operation_id must be a UUID")
	ErrKernelOperationNotPending   = errors.New("operation is not cancellable")
	ErrPluginTrustRootRequired     = errors.New("official plugin trust root is required")
	ErrExtensionCatalogIntegrity   = errors.New("extension catalog integrity check failed")
	ErrPluginConfigurationConflict = errors.New("plugin configuration revision conflict")
	ErrPluginDependencyUnsatisfied = errors.New("plugin dependency is not enabled")
	ErrPluginConflict              = errors.New("plugin conflict is enabled")
	ErrPluginArtifactRequired      = errors.New("verified plugin artifact is required")
	ErrPluginArtifactImmutable     = errors.New("plugin artifact is immutable")
	ErrPluginRouteNotFound         = errors.New("plugin control route is not registered")
	ErrPluginRouteForbidden        = errors.New("plugin control route permission denied")
	ErrPackageMigrationImmutable   = errors.New("package migration run is immutable")
	ErrValidationPrecondition      = errors.New("package validation precondition failed")
	ErrCohortTransition            = errors.New("package cohort transition is invalid")
	ErrRollbackPrecondition        = errors.New("package rollback precondition failed")
)
View Source
var (
	ErrOrderNotFound     = errors.New("订单不存在")
	ErrOrderPlanNotFound = errors.New("关联套餐不存在")
)
View Source
var (
	ErrPlanNotFound     = errors.New("套餐不存在")
	ErrPlanUserNotFound = errors.New("用户不存在")
)
View Source
var (
	ErrPluginDependencyGraphEmpty             = errors.New("plugin dependency graph is empty")
	ErrPluginDependencyGraphRootRequired      = errors.New("plugin dependency graph root is required")
	ErrPluginDependencyGraphDuplicateManifest = errors.New("duplicate plugin manifest in dependency graph")
	ErrPluginDependencyGraphDuplicateRoot     = errors.New("duplicate plugin dependency graph root")
	ErrPluginDependencyGraphInvalid           = errors.New("invalid plugin dependency graph manifest")
	ErrPluginDependencyGraphSelf              = errors.New("plugin dependency graph contains a self dependency")
	ErrPluginDependencyGraphDuplicate         = errors.New("plugin dependency graph contains a duplicate relationship")
	ErrPluginDependencyGraphMissing           = errors.New("plugin dependency graph dependency is missing")
	ErrPluginDependencyGraphCycle             = errors.New("plugin dependency graph contains a cycle")
	ErrPluginDependencyGraphConflict          = errors.New("plugin dependency graph contains a conflict")
)

The graph resolver operates on releases that have already passed signature and manifest admission. These errors describe graph-level failures so the caller can distinguish a malformed graph from a package-manager failure.

View Source
var (
	ErrSubscriptionUserNotFound      = errors.New("用户不存在")
	ErrSubscriptionPlanNotFound      = errors.New("套餐不存在")
	ErrSubscriptionGroupNotFound     = errors.New("分组不存在")
	ErrSubscriptionUserGroupNotFound = errors.New("用户订阅分组不存在")
	ErrSubscriptionPlanGroupNotFound = errors.New("套餐订阅分组不存在")
)
View Source
var (
	ErrTopologyDeploymentNotPlanned = errors.New("topology deployment is not planned")
	ErrTopologyDeploymentTerminal   = errors.New("topology deployment is already terminal")
	ErrTopologyDeploymentBusy       = errors.New("topology has an active deployment")
)
View Source
var AgentDiagnosticActions = map[string]AgentDiagnosticActionSpec{
	AgentDiagnosticActionServiceStatus:  {Params: []string{"service"}, Mutating: false},
	AgentDiagnosticActionServiceRestart: {Params: []string{"service"}, Mutating: true},
	AgentDiagnosticActionLogTail:        {Params: []string{"service", "lines"}, Mutating: false},
}

AgentDiagnosticActions 是终端功能允许下发的全部动作,不接受任意 action 字符串。

View Source
var ErrInvalidNodeProtocol = errors.New("invalid node protocol")
View Source
var ErrNegativeTraffic = errors.New("traffic values must be non-negative")
View Source
var ErrNodeProtocolNotFound = errors.New("协议不存在")

ErrNodeProtocolNotFound indicates at least one requested node protocol ID does not exist.

View Source
var ErrUserNotFound = errors.New("用户不存在")

ErrUserNotFound is returned when a mutating user operation targets no row.

Functions

func AdvancePackageCohort

func AdvancePackageCohort(db *gorm.DB, generationID uint, target uint8) (*model.PackageRouteGeneration, error)

AdvancePackageCohort creates the next immutable route generation only for the canonical 1, 5, 25, 100 rollout sequence. Retrying the exact request returns the previously-created successor.

func ApplyTopologyObservedState

func ApplyTopologyObservedState(db *gorm.DB, update TopologyObservedStateUpdate) (*model.TopologyObservedState, bool, error)

ApplyTopologyObservedState writes one node observation with monotonic desired/observed revisions. A stale or out-of-order observation returns the current row with changed=false and never regresses deployment state.

func ApplyTopologyObservedStateTx

func ApplyTopologyObservedStateTx(tx *gorm.DB, update TopologyObservedStateUpdate) (*model.TopologyObservedState, bool, error)

ApplyTopologyObservedStateTx is the transaction form used by gRPC observed callbacks so operation and topology state commit atomically.

func BackfillForwardPortBindings

func BackfillForwardPortBindings(db *gorm.DB) error

func BeginPackageMigration

func BeginPackageMigration(db *gorm.DB, input model.PackageMigrationRun) (*model.PackageMigrationRun, error)

BeginPackageMigration creates one generation-fenced migration run. A retry with the same immutable input returns the original run and its latest checkpoint so an interrupted host call can resume safely.

func BootstrapIdentityPlatformPackage

func BootstrapIdentityPlatformPackage(db *gorm.DB, encodedPublicKey, directory string) error

BootstrapIdentityPlatformPackage imports exactly one signed identity package before HTTP routes are exposed. This is the only package allowed through the unauthenticated cold-start path; every later package admission stays behind the authenticated kernel API.

func BuildAgentPluginInstallConfig

func BuildAgentPluginInstallConfig(db *gorm.DB, nodeID uint, pluginID, version string) (string, error)

func BuildLoginRateLimitKey

func BuildLoginRateLimitKey(email, ip string) string

func BuildNodeProtocolConfig

func BuildNodeProtocolConfig(node *model.Node, protocol *model.NodeProtocol) map[string]any

BuildNodeProtocolConfig 从 Node + NodeProtocol 构建下发给节点的完整协议配置。

这是节点拉配置的**唯一真源**: HTTP UniProxy handler 和 gRPC 两个配置构建函数 都调它, 避免"改了一处忘了另一处"(SS2022 server_key 之前就是只在订阅端修了, gRPC 节点拿不到)。输出字段名与老 UniProxy JSON 保持一致:

node_type, type, host, server_port, server_name, tls, network,
cipher, server_key, flow, tls_settings, network_settings,
send_through, routes, base_config, 以及 custom_config 覆盖的任意字段

SS2022 (2022-blake3-*) 的 server_key 若 Settings 未显式给出, 按老 XBoard 算法 从节点 created_at 派生 (子节点用父节点 created_at), 与订阅端 DeriveSS2022ServerKey 和节点端 V2bX 三方一致。

func BuildRegisterRateLimitKey

func BuildRegisterRateLimitKey(ip string) string

func CancelKernelOperation

func CancelKernelOperation(db *gorm.DB, operationID string, at time.Time) (*model.KernelOperation, error)

func CanonicalKernelOperationConfig

func CanonicalKernelOperationConfig(raw string) (string, error)

CanonicalKernelOperationConfig normalizes a JSON config before it becomes part of an idempotency identity and Agent envelope. This makes hash and retry behavior independent of request whitespace or object key ordering.

func CanonicalPluginManifest

func CanonicalPluginManifest(manifest PluginManifest) ([]byte, error)

CanonicalPluginManifest is the exact byte representation that AnixOps signs and verifies. Schema JSON is recursively parsed and re-encoded so whitespace or object-key ordering cannot change a release signature.

func CreateKernelOperation

func CreateKernelOperation(db *gorm.DB, operation model.KernelOperation) (*model.KernelOperation, bool, error)

CreateKernelOperation persists a requested operation before it can be sent to an agent. A repeated idempotency key returns the original operation, including after a control-plane restart.

func CreateTopologyRevision

func CreateTopologyRevision(db *gorm.DB, topologyID, actorID uint, input TopologyRevisionInput) (*model.TopologyRevision, error)

func DecodeSecret

func DecodeSecret(encoded string) ([]byte, error)

DecodeSecret 解码密钥

func DeriveSS2022ServerKey

func DeriveSS2022ServerKey(createdAt time.Time, cipher string) string

DeriveSS2022ServerKey 复刻老 XBoard `Helper::getServerKey($timestamp, $len)`: base64(substr(md5(unix秒的十进制字符串), 0, len))。md5 输出取 hex 字符串的前 len 个字符(不是字节), 再 base64。节点端 V2bX 用相同的 server_key, 两边必须一致。 导出供 handler 层 (UniProxy 节点拉配置) 复用, 保证订阅端和节点端算出的值一致。

func EncodeSecret

func EncodeSecret(secret []byte) string

EncodeSecret 编码密钥

func EnsureAgentDiagnosticTaskSchema

func EnsureAgentDiagnosticTaskSchema(db *gorm.DB) error

EnsureAgentDiagnosticTaskSchema creates the diagnostic task table if it does not exist. Called unconditionally at startup (including production) because AutoMigrate only runs in dev/test. It touches only the new table, never existing ones.

func EnsureForwardBridgeSchema

func EnsureForwardBridgeSchema(db *gorm.DB) error

EnsureForwardBridgeSchema creates the durable clean_agent bridge mapping table if it does not exist. Called unconditionally at startup (including production) because AutoMigrate only runs in dev/test. Idempotent and only touches this new table.

func EnsureForwardNodeMetricsPortColumn

func EnsureForwardNodeMetricsPortColumn(db *gorm.DB) error

EnsureForwardNodeMetricsPortColumn adds the metrics_port column to the existing v2_forward_node table if it does not exist yet. Called unconditionally at startup (including production) because AutoMigrate only runs in dev/test. It touches only this one column on an existing table, never anything else.

func EnsureForwardPortBindingSchema

func EnsureForwardPortBindingSchema(db *gorm.DB) error

func EnsureForwardRuntimeJobSchema

func EnsureForwardRuntimeJobSchema(db *gorm.DB) error

EnsureForwardRuntimeJobSchema keeps runtime job invariants that GORM cannot express with model tags, especially the partial unique index for active jobs.

func EnsureKernelSchema

func EnsureKernelSchema(db *gorm.DB) error

EnsureKernelSchema only migrates new kernel-owned tables and is safe to run in production without touching legacy plugin-owned tables.

func EnsureNodeRuntimeHealthSchema

func EnsureNodeRuntimeHealthSchema(db *gorm.DB) error

EnsureNodeRuntimeHealthSchema adds the runtime-health columns used by WireGuard relay supervision. It is kept separate because production startup intentionally does not run the complete application AutoMigrate set.

func EnsureObservabilitySchema

func EnsureObservabilitySchema(db *gorm.DB) error

EnsureObservabilitySchema creates the latency bucket table if it does not exist. Called unconditionally at startup (including production) because AutoMigrate only runs in dev/test. It touches only the new observability table, never existing tables.

func EnsurePluginTrustRoot

func EnsurePluginTrustRoot(db *gorm.DB, publicKey ed25519.PublicKey) (*model.PluginTrustRoot, error)

func EnsureStatsSchema

func EnsureStatsSchema(db *gorm.DB) error

EnsureStatsSchema creates or completes the traffic/stats tables: v2_server_log, v2_online_log, v2_stat_user, v2_stat_server. Called unconditionally at startup (including production) because AutoMigrate only runs in dev/test. Idempotent and limited to these tables; existing legacy tables are only expanded with missing columns/indexes.

func EnsureWireGuardPeerSchema

func EnsureWireGuardPeerSchema(db *gorm.DB) error

EnsureWireGuardPeerSchema creates or upgrades only the WireGuard peer table. The server intentionally skips the full AutoMigrate set in production, so newly introduced runtime tables need an explicit idempotent schema hook.

func ExpireKernelOperations

func ExpireKernelOperations(db *gorm.DB, now time.Time) (int64, error)

ExpireKernelOperations marks undispatched or in-flight work that has passed its deadline. A future supervisor worker may call this on a timer; read APIs also invoke it so stale intent is never presented as pending.

func ExtractPluginWebUIBundle

func ExtractPluginWebUIBundle(manifest PluginManifest, artifact []byte) ([]byte, error)

func GenerateWireGuardKeypair

func GenerateWireGuardKeypair() (privateKey string, publicKey string, err error)

GenerateWireGuardKeypair exposes server-key generation to the authenticated admin workflow without persisting private material until the protocol is explicitly saved.

func GetPluginArtifact

func GetPluginArtifact(db *gorm.DB, releaseID uint) (*model.PluginArtifact, error)

func GetPluginConfiguration

func GetPluginConfiguration(db *gorm.DB, installationID uint) (*model.PluginConfiguration, error)

GetPluginConfiguration returns the persisted document for one installation. An installation with no document has the deterministic empty-object default.

func GetPluginWebUIAsset

func GetPluginWebUIAsset(db *gorm.DB, pluginID, version, bundleSHA256 string) (*model.PluginWebUIAsset, error)

func HashKernelOperationConfig

func HashKernelOperationConfig(raw string) (string, error)

HashKernelOperationConfig returns the deterministic SHA-256 digest used in operation envelopes and persisted plugin configuration rows. The input is canonicalized first so semantically identical JSON documents share a hash.

func InitAdmin

func InitAdmin(cfg *config.Config)

InitAdmin 初始化管理员账号

func InitDefaultAuthKeyFromEnv

func InitDefaultAuthKeyFromEnv()

InitDefaultAuthKeyFromEnv 从环境变量初始化默认授权密钥 环境变量: NODE_DEFAULT_AUTH_KEY 如果设置了该环境变量:

  • 如果密钥内容已存在 → 跳过
  • 如果存在同名默认密钥但内容不同 → 删除旧的,创建新的
  • 如果不存在 → 创建

Note: 改变 AuthKey 只会影响新节点注册,已注册节点使用 APIKey 通信,不受影响

func InitDefaultPlan

func InitDefaultPlan()

InitDefaultPlan 确保数据库中有一个默认套餐(如“基础套餐”)

func InitForwardRuntimeSystemConfig

func InitForwardRuntimeSystemConfig(db *gorm.DB) error

func InitSubscriptionDefaults

func InitSubscriptionDefaults()

InitSubscriptionDefaults 初始化默认订阅分组和模板

func IsAgentPluginOperation

func IsAgentPluginOperation(kind string) bool

IsAgentPluginOperation reports whether an operation has a concrete Agent Supervisor implementation. Topology operations remain mediated through their concrete plugin lifecycle operations.

func IsSensitiveSystemConfigKey

func IsSensitiveSystemConfigKey(key string) bool

func IsWireGuardExitProtocol

func IsWireGuardExitProtocol(protocol *model.NodeProtocol) bool

IsWireGuardExitProtocol identifies the overseas relay role. Exit nodes do not terminate user WireGuard peers, so they must neither allocate peer keys, appear in user subscriptions, nor receive user runtime metadata.

func LegacyTopologyObservedHealthJSON

func LegacyTopologyObservedHealthJSON() string

LegacyTopologyObservedHealthJSON is used only by the pre-executor compatibility bridge. Its Agent StateJson is intentionally discarded: that schema was never a public contract and may contain plugin-defined details.

func LoadVerifiedPluginCompatibilityRoutes

func LoadVerifiedPluginCompatibilityRoutes(db *gorm.DB, release model.PluginRelease, publicKey ed25519.PublicKey) ([]byte, error)

LoadVerifiedPluginCompatibilityRoutes returns the immutable compatibility declaration embedded in a signed v2 Control artifact. Callers still decide whether the corresponding installation is active; this helper establishes that the declaration itself is bound to the stored release and artifact.

func LoadVerifiedPluginWebUIAsset

func LoadVerifiedPluginWebUIAsset(db *gorm.DB, release model.PluginRelease, manifest PluginManifest) (*model.PluginWebUIAsset, error)

func LockNodePluginLifecycleTx

func LockNodePluginLifecycleTx(tx *gorm.DB, nodeID uint, pluginID string) (*model.NodePluginLifecycle, error)

LockNodePluginLifecycleTx acquires the aggregate lock without recalculating desired state. Mutating handlers use it before locking assignment rows so all callers follow the same lifecycle -> assignment lock order.

func LockPluginInstallationTarget

func LockPluginInstallationTarget(tx *gorm.DB, target string) error

LockPluginInstallationTarget must be called inside the transaction that validates and mutates plugin installation intent. The shared target row closes races where two packages could otherwise pass dependency/conflict checks against the same stale enabled set.

func MaskSystemConfigValue

func MaskSystemConfigValue(key, value string) (displayValue string, sensitive bool, hasValue bool)

func MaterializePluginControlArtifact

func MaterializePluginControlArtifact(db *gorm.DB, publicKey ed25519.PublicKey, pluginID, version, destinationRoot string) (pluginhost.ArtifactRef, error)

MaterializePluginControlArtifact writes the verified immutable release bytes and the signed v2 Control entrypoint into a private caller-owned directory. Task 3's lifecycle dispatcher consumes the resulting ArtifactRef; it still owns process supervision and never interprets package metadata itself.

func MigratePackageHost

func MigratePackageHost(ctx context.Context, db *gorm.DB, manager *pluginhost.Supervisor, migrationRunID uint, input pluginhost.MigrationInput) (pluginhost.MigrationOutput, error)

MigratePackageHost is the only public orchestration path that can attach a health fence to a migration run. Requiring the concrete Supervisor prevents callers from substituting an arbitrary MigrationManager that fabricates a successful health result.

func NewNodeXForwardRuntimeProviderWithHTTPClient

func NewNodeXForwardRuntimeProviderWithHTTPClient(db *gorm.DB, httpClient nodeXForwardHTTPDoer) *nodeXForwardRuntimeProvider

func NormalizeNodeLogLevel

func NormalizeNodeLogLevel(level string) string

func NormalizeNodeType

func NormalizeNodeType(nodeType string) string

NormalizeNodeType 归一化节点类型别名 (v2ray/vmess-aead → vmess)。

func ParseIPRange

func ParseIPRange(r string) ([]net.IP, error)

ParseIPRange parses CIDR or single-IP input.

func ParseOfficialPluginPublicKey

func ParseOfficialPluginPublicKey(encoded string) (ed25519.PublicKey, error)

func ParseOnlineData

func ParseOnlineData(data map[string]any) (map[uint][]string, error)

ParseOnlineData 解析在线数据

func ParseTrafficData

func ParseTrafficData(data map[string]any) (map[uint][2]int64, error)

ParseTrafficData 解析流量数据

func PlanTopologyDeployment

PlanTopologyDeployment compiles the immutable graph into deployment steps. It does not mutate an active revision or create any Agent work.

func PluginAPIPermission

func PluginAPIPermission(pluginID string) string

func PluginArtifactStorageKey

func PluginArtifactStorageKey(pluginID, version, artifactSHA256 string) string

func PluginControlBridgeRouteID

func PluginControlBridgeRouteID(pluginID, route string) string

PluginControlBridgeRouteID turns a signed Control route into a stable, package-bridge-safe operation name. The original route remains kernel-owned request metadata; package processes never choose it themselves.

func PluginTrustRootFingerprint

func PluginTrustRootFingerprint(publicKey ed25519.PublicKey) string

func PluginTrustRootKeyID

func PluginTrustRootKeyID(publicKey ed25519.PublicKey) string

func PluginWebUIAssetStorageKey

func PluginWebUIAssetStorageKey(pluginID, version, bundleSHA256, bundlePath string) string

func PluginWebUIAssetURL

func PluginWebUIAssetURL(pluginID, version, bundleSHA256, bundlePath string) string

func PurgeCompletedAssignmentDeletes

func PurgeCompletedAssignmentDeletes(tx *gorm.DB, lifecycle *model.NodePluginLifecycle, chain *AgentAssignmentOperationChain) error

PurgeCompletedAssignmentDeletes is used by handlers when dispatch is explicitly disabled and the observed plugin is already inactive.

func ReconcileAgentAssignments

func ReconcileAgentAssignments(db *gorm.DB, now time.Time) (int, error)

ReconcileAgentAssignments resumes enrolled lifecycle rows. It does not scan legacy generation-zero assignments, preserving the opt-in migration gate.

func RecordMigrationCheckpoint

func RecordMigrationCheckpoint(db *gorm.DB, migrationRunID uint, checkpoint PackageMigrationCheckpoint) (*model.PackageMigrationRun, error)

RecordMigrationCheckpoint durably records an opaque host reply. It cannot activate a route; the health proof is recorded only by the service-owned migration coordinator after the supervisor returns successfully.

func RecordNodePluginOperationSuccessTx

func RecordNodePluginOperationSuccessTx(tx *gorm.DB, operation model.KernelOperation) error

RecordNodePluginOperationSuccessTx captures the version actually selected by the Agent, so later disable/delete never guesses from a stale role row.

func RecordPackageValidation

func RecordPackageValidation(db *gorm.DB, input model.PackageValidationResult) (*model.PackageValidationResult, error)

RecordPackageValidation accepts only a completed migration checkpoint and creates the initial one-percent route generation. The digest remains opaque to the kernel.

func RefreshAgentPluginInstallationObservedState

func RefreshAgentPluginInstallationObservedState(db *gorm.DB, pluginID string) error

RefreshAgentPluginInstallationObservedState projects node lifecycle state in a separate, consistently ordered transaction. Lifecycle mutations must not call this while holding an aggregate lock because global installation writes use target -> installation -> lifecycle ordering.

func RegisterPluginRelease

func RegisterPluginRelease(db *gorm.DB, manifestJSON, signature string, publicKey ed25519.PublicKey) (*model.PluginRelease, error)

RegisterPluginRelease admits only a manifest signed by the configured AnixOps trust root and records the current exclusive trust-root fingerprint. Artifact bytes are uploaded separately and must match the signed manifest hash before an install can be enabled.

func RequestTopologyDeploymentApply

func RequestTopologyDeploymentApply(db *gorm.DB, deploymentID uint, _ time.Time) (*model.TopologyDeployment, error)

RequestTopologyDeploymentApply makes a plan eligible for the feature-gated worker. It is idempotent while already applying but never reopens terminal deployments, preserving an audit trail for every revision attempt.

func RequestTopologyDeploymentRollback

func RequestTopologyDeploymentRollback(db *gorm.DB, deploymentID uint, at time.Time) (*model.TopologyDeployment, error)

RequestTopologyDeploymentRollback stops expansion and schedules durable reverse-order compensation. It is safe to call before any operation has dispatched; such a deployment simply reaches rolled_back with skipped steps.

func ResetLoginRateLimiterForTest

func ResetLoginRateLimiterForTest()

func ResolveActivePluginWebUIAsset

func ResolveActivePluginWebUIAsset(db *gorm.DB, publicKey ed25519.PublicKey, actorID uint, legacyAdmin bool, pluginID, version, bundleSHA256 string) (*model.PluginWebUIAsset, error)

ResolveActivePluginWebUIAsset binds an immutable asset URL to the currently active control installation and to the requesting actor. Stored signatures, release metadata, artifact bytes and the extracted bundle are revalidated on every request so disabling or replacing an installation invalidates old URLs.

func RollbackPackageGeneration

func RollbackPackageGeneration(db *gorm.DB, generationID uint, reason string) (*model.PackageRouteGeneration, error)

RollbackPackageGeneration retires the current generation and returns its verified predecessor. Both the reason and any retained backup reference are recorded transactionally; no package-owned table is touched.

func StorePluginArtifact

func StorePluginArtifact(db *gorm.DB, releaseID uint, artifact []byte) (*model.PluginArtifact, error)

func StringifyConfigMap

func StringifyConfigMap(m map[string]any) map[string]string

StringifyConfigMap 把 map[string]any 转成 map[string]string (proto 的 map<string,string> 字段需要)。数字按整数/浮点合理格式化, 避免 443 变 "443.000000"。

func SyncAgentInstallationAssignments

func SyncAgentInstallationAssignments(tx *gorm.DB, installation model.PluginInstallation, configRevision int64, queue bool, now time.Time) ([]*model.KernelOperation, error)

SyncAgentInstallationAssignments updates every matching assignment revision and aggregate generation in the caller's configuration transaction.

func SyncNodePluginLifecycle

func SyncNodePluginLifecycle(tx *gorm.DB, nodeID uint, pluginID string, explicitRetry bool, _ time.Time) (*model.NodePluginLifecycle, bool, error)

SyncNodePluginLifecycle serializes every role of one node/plugin pair, computes aggregate desired state and optionally rearms an exhausted chain after an explicit administrator mutation.

func UpdatePluginConfiguration

func UpdatePluginConfiguration(db *gorm.DB, publicKey ed25519.PublicKey, installationID uint, rawConfig string, expectedRevision *int64, actorID uint) (*model.PluginConfiguration, error)

UpdatePluginConfiguration validates a package configuration against the signed release schema and atomically advances its installation revision. expectedRevision enables optimistic concurrency for independent WebUI pages.

func UpdatePluginConfigurationWithValidator

func UpdatePluginConfigurationWithValidator(db *gorm.DB, publicKey ed25519.PublicKey, installationID uint, rawConfig string, expectedRevision *int64, actorID uint, validator PluginConfigurationSemanticValidator) (*model.PluginConfiguration, error)

UpdatePluginConfigurationWithValidator runs semantic validation in the same transaction and against the same signed release version that is persisted.

func UpdatePluginConfigurationWithValidatorAndHook

func UpdatePluginConfigurationWithValidatorAndHook(db *gorm.DB, publicKey ed25519.PublicKey, installationID uint, rawConfig string, expectedRevision *int64, actorID uint, validator PluginConfigurationSemanticValidator, hook PluginConfigurationTxHook) (*model.PluginConfiguration, error)

func ValidateAgentDiagnosticTask

func ValidateAgentDiagnosticTask(action string, params map[string]any) (map[string]any, error)

ValidateAgentDiagnosticTask 校验 action 是否在白名单内,并将 params 归一化为 仅包含该 action 允许的字段(多余字段被丢弃,数值做范围裁剪)。 返回的 error 可直接展示给管理员。

func ValidateCodeFormat

func ValidateCodeFormat(code string) bool

ValidateCodeFormat 验证代码格式

func ValidateNodeProtocol

func ValidateNodeProtocol(protocol *model.NodeProtocol) error

ValidateNodeProtocol validates the fields that affect node runtime. The existing protocol API accepts JSON strings for protocol-specific settings, so validation belongs in the service layer and covers both HTTP and gRPC callers that persist protocols.

func ValidatePluginAPIGrantPermissions

func ValidatePluginAPIGrantPermissions(pluginID, raw string) error

ValidatePluginAPIGrantPermissions rejects malformed or cross-plugin grants before they enter the kernel authorization table.

func ValidatePluginDependencyExecutionPlan

func ValidatePluginDependencyExecutionPlan(db *gorm.DB, plan *PluginDependencyExecutionPlan) error

ValidatePluginDependencyExecutionPlan verifies every selected artifact and validates conflicts against already-enabled packages outside the closure. Callers hold PluginTargetLock while invoking it and applying the plan.

func ValidatePluginInstallationPlan

func ValidatePluginInstallationPlan(db *gorm.DB, release model.PluginRelease, target string, enabled bool, installationID uint) error

func ValidatePluginReleaseTarget

func ValidatePluginReleaseTarget(release model.PluginRelease, target string) error

func ValidateRegistrationEmail

func ValidateRegistrationEmail(email string, policy RegistrationPolicy) error

func ValidateTrafficDelta

func ValidateTrafficDelta(upload, download int64) error

func ValidateWireGuardRuntimeConfig

func ValidateWireGuardRuntimeConfig(config map[string]any) error

ValidateWireGuardRuntimeConfig validates a raw node config after it has been decoded from JSON. Raw configs bypass NodeProtocol persistence, so they need the same runtime guard before UniProxy returns them to a node.

func VerifyPluginArtifact

func VerifyPluginArtifact(manifest PluginManifest, artifact []byte) error

VerifyPluginArtifact checks the bytes fetched from the signed repository before they are handed to an installer. The manifest signature alone is not sufficient when an artifact is replaced at the transport layer.

func WithAgentLifecycleTransaction

func WithAgentLifecycleTransaction(db *gorm.DB, mutation func(*gorm.DB) error) error

WithAgentLifecycleTransaction retries only SQLite writer-contention errors. PostgreSQL locking and all semantic errors are returned immediately.

func WithRetryableTransaction

func WithRetryableTransaction(db *gorm.DB, mutation func(*gorm.DB) error) error

WithRetryableTransaction reruns the whole transaction only when SQLite reports transient writer contention. The mutation may run more than once, so it must keep side effects inside the transaction. Other databases and semantic failures return immediately.

Types

type ActorPluginAccess

type ActorPluginAccess struct {
	Unrestricted      bool
	LegacyAdmin       bool
	Permissions       []string
	RestrictedPlugins []string
	// contains filtered or unexported fields
}

ActorPluginAccess is the kernel-authoritative plugin permission view for one actor. A legacy administrator remains unrestricted for plugins that have no explicit grant, while each plugin with at least one grant is evaluated fail-closed. This permits incremental migration of plugin permissions.

func NewFailClosedActorPluginAccess

func NewFailClosedActorPluginAccess() *ActorPluginAccess

NewFailClosedActorPluginAccess is used when authentication has already committed but the optional plugin authorization lookup is temporarily unavailable. It never grants access and lets the caller return a valid session that can retry its profile fetch.

func ResolveActorPluginAccess

func ResolveActorPluginAccess(db *gorm.DB, actorID uint, legacyAdmin bool) (*ActorPluginAccess, error)

ResolveActorPluginAccess reads enabled access-group memberships and their plugin_api grants. Permission arrays and truthy object entries are unioned, de-duplicated and sorted for deterministic API responses.

func (ActorPluginAccess) Allows

func (access ActorPluginAccess) Allows(pluginID, permission string) bool

func (ActorPluginAccess) HasAnyPermission

func (access ActorPluginAccess) HasAnyPermission(pluginID string) bool

func (ActorPluginAccess) PermissionMode

func (access ActorPluginAccess) PermissionMode() string

func (ActorPluginAccess) ProfilePermissions

func (access ActorPluginAccess) ProfilePermissions() []string

func (ActorPluginAccess) ProfileRestrictedPluginList

func (access ActorPluginAccess) ProfileRestrictedPluginList() []string

ProfileRestrictedPluginList intentionally hides the catalog of configured plugin grants from non-administrators. They still receive their effective permissions, but not a cross-tenant enumeration of restricted plugins.

func (ActorPluginAccess) RestrictedPluginList

func (access ActorPluginAccess) RestrictedPluginList() []string

type AgentAssignmentOperationChain

type AgentAssignmentOperationChain struct {
	Install *model.KernelOperation
	Update  *model.KernelOperation
	Enable  *model.KernelOperation
	Disable *model.KernelOperation
}

func QueueAgentAssignmentLifecycle

func QueueAgentAssignmentLifecycle(tx *gorm.DB, assignment model.NodeServiceAssignment, now time.Time) (*AgentAssignmentOperationChain, error)

QueueAgentAssignmentLifecycle remains the focused entry point used by tests and callers that have already mutated one assignment transactionally.

func QueueNodePluginLifecycle

func QueueNodePluginLifecycle(tx *gorm.DB, lifecycle *model.NodePluginLifecycle, now time.Time) (*AgentAssignmentOperationChain, error)

QueueNodePluginLifecycle reuses a valid current chain before touching signed package metadata. Failed chains receive bounded retry epochs with backoff; successful or in-flight chains are never duplicated on periodic reconcile.

type AgentDiagnosticActionSpec

type AgentDiagnosticActionSpec struct {
	Params   []string
	Mutating bool
}

AgentDiagnosticActionSpec 描述一个白名单诊断动作允许携带哪些参数,以及是否为变更类操作。

type AgentDiagnosticTaskService

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

AgentDiagnosticTaskService 负责白名单诊断任务的持久化,取代 AgentHandler 里原来的 taskResults sync.Map,使任务结果在面板进程重启后仍可查询。

func NewAgentDiagnosticTaskService

func NewAgentDiagnosticTaskService(db *gorm.DB) *AgentDiagnosticTaskService

func (*AgentDiagnosticTaskService) CompleteTask

func (s *AgentDiagnosticTaskService) CompleteTask(taskID string, nodeID uint, action string, success bool, output string, errMsg string, durationMS int64) error

CompleteTask 写入任务的最终执行结果。如果 task_id 尚无记录(例如 agent 直接 上报、面板端没有先建任务),则补建一条 completed/failed 记录,保持幂等可查询。

func (*AgentDiagnosticTaskService) CreateTask

func (s *AgentDiagnosticTaskService) CreateTask(taskID string, nodeID uint, action string, params map[string]any) (*model.AgentDiagnosticTask, error)

CreateTask 插入一条 pending 状态的诊断任务记录,params 必须已经过 ValidateAgentDiagnosticTask 归一化。

func (*AgentDiagnosticTaskService) GetTask

GetTask 按 task_id 查询单条任务。

func (*AgentDiagnosticTaskService) ListTasks

func (s *AgentDiagnosticTaskService) ListTasks(nodeID uint, limit int) ([]model.AgentDiagnosticTask, error)

ListTasks 按可选的 node_id 过滤,返回最近的诊断任务历史(供管理页任务历史表使用)。

func (*AgentDiagnosticTaskService) MarkStatus

func (s *AgentDiagnosticTaskService) MarkStatus(taskID string, status string) error

MarkStatus 更新任务的投递状态(如 dispatched),不改变最终结果字段。

func (*AgentDiagnosticTaskService) PullPendingTasks

func (s *AgentDiagnosticTaskService) PullPendingTasks(nodeID uint) ([]model.AgentDiagnosticTask, error)

PullPendingTasks 取出该节点尚未下发 (status=pending) 的诊断任务,并原子标记为 dispatched,供 HTTP 轮询降级路径 (AgentGetTasks) 使用,避免并发重复下发。

type AgentPluginArtifactAddress

type AgentPluginArtifactAddress struct {
	URL    string `json:"url"`
	SHA256 string `json:"sha256"`
	Size   int64  `json:"size"`
}

type AgentPluginInstallConfig

type AgentPluginInstallConfig struct {
	APIVersion string                     `json:"api_version"`
	PluginID   string                     `json:"plugin_id"`
	Version    string                     `json:"version"`
	Artifact   AgentPluginArtifactAddress `json:"artifact"`
	Manifest   AgentPluginManifestAddress `json:"manifest"`
}

type AgentPluginManifestAddress

type AgentPluginManifestAddress struct {
	URL        string `json:"url"`
	SHA256     string `json:"sha256"`
	Size       int64  `json:"size"`
	Signature  string `json:"signature"`
	Publisher  string `json:"publisher"`
	KeyID      string `json:"key_id"`
	APIVersion string `json:"api_version"`
}

type AgentPluginReleaseDownload

type AgentPluginReleaseDownload struct {
	Release      model.PluginRelease
	Manifest     PluginManifest
	ManifestData []byte
	Artifact     model.PluginArtifact
	ManifestHash string
}

AgentPluginReleaseDownload is verified immediately before a node-facing response is written. Data is intentionally kept out of JSON response types.

func LoadAgentPluginArtifactBlob

func LoadAgentPluginArtifactBlob(db *gorm.DB, metadata *AgentPluginReleaseDownload) (*AgentPluginReleaseDownload, error)

LoadAgentPluginArtifactBlob is intentionally separate from authorization and content-address validation. Node-facing handlers call it only after the bounded metadata path has accepted the exact immutable address.

func LoadAuthorizedAgentPluginMetadata

func LoadAuthorizedAgentPluginMetadata(db *gorm.DB, nodeID uint, pluginID, version string) (*AgentPluginReleaseDownload, error)

LoadAuthorizedAgentPluginMetadata verifies assignment, global installation, official signature and persisted size metadata without loading the package blob. Reconcile and manifest requests must stay on this bounded path.

func LoadAuthorizedAgentPluginRelease

func LoadAuthorizedAgentPluginRelease(db *gorm.DB, nodeID uint, pluginID, version string) (*AgentPluginReleaseDownload, error)

LoadAuthorizedAgentPluginRelease is the unbounded blob path used only for the authenticated artifact response. It re-hashes bytes immediately before streaming so database corruption cannot cross the trust boundary.

func (*AgentPluginReleaseDownload) ValidateArtifactAddress

func (download *AgentPluginReleaseDownload) ValidateArtifactAddress(sha256Hex string, size int64) error

func (*AgentPluginReleaseDownload) ValidateManifestAddress

func (download *AgentPluginReleaseDownload) ValidateManifestAddress(sha256Hex string, size int64) error

type AgentTask

type AgentTask struct {
	ID        uint64    `json:"id"`
	NodeID    uint32    `json:"node_id"`
	Type      string    `json:"type"`
	Status    string    `json:"status"` // pending, running, completed, failed
	Command   string    `json:"command"`
	Action    string    `json:"action"`
	Params    string    `json:"params"`
	Result    string    `json:"result"`
	Error     string    `json:"error"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

AgentTask represents a task dispatched to an agent node.

type AuthService

type AuthService struct{}

func NewAuthService

func NewAuthService() *AuthService

func (*AuthService) Authenticate

func (s *AuthService) Authenticate(email, password string) (*model.User, error)

Authenticate validates login credentials and account state without issuing a token.

func (*AuthService) IssueToken

func (s *AuthService) IssueToken(user *model.User, cfg *config.Config) (string, error)

IssueToken signs a JWT for an already authenticated user.

func (*AuthService) Login

func (s *AuthService) Login(email, password string, cfg *config.Config) (string, *model.User, error)

Login validates credentials and returns a JWT token.

func (*AuthService) Register

func (s *AuthService) Register(email, password string, cfg *config.Config) (string, *model.User, error)

Register keeps the existing public API and registers without an invite code.

func (*AuthService) RegisterWithInvite

func (s *AuthService) RegisterWithInvite(email, password, inviteCode string, cfg *config.Config) (string, *model.User, error)

RegisterWithInvite creates a user and optionally consumes an invite code.

type BackupService

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

func NewBackupService

func NewBackupService(db *gorm.DB) *BackupService

func (*BackupService) CleanupOldBackups

func (s *BackupService) CleanupOldBackups() error

func (*BackupService) CreateBackup

func (s *BackupService) CreateBackup(backupType string, createdBy *uint) (*model.BackupRecord, error)

func (*BackupService) DeleteBackup

func (s *BackupService) DeleteBackup(id uint) error

func (*BackupService) GetBackupStats

func (s *BackupService) GetBackupStats() (map[string]any, error)

func (*BackupService) GetConfig

func (s *BackupService) GetConfig() (*model.BackupConfig, error)

func (*BackupService) ListBackups

func (s *BackupService) ListBackups(page, pageSize int) ([]model.BackupRecord, int64, error)

func (*BackupService) RestoreBackup

func (s *BackupService) RestoreBackup(id uint) error

func (*BackupService) UpdateConfig

func (s *BackupService) UpdateConfig(cfg *model.BackupConfig) error

type CreateOrderParams

type CreateOrderParams struct {
	UserID   uint
	PlanID   uint
	Period   string // month, quarter, half_year, year, two_year, three_year, onetime
	CouponID *uint
}

CreateOrderParams 创建订单参数

type CreateRuleRequest

type CreateRuleRequest struct {
	Name         string     `json:"name"`
	RelayNodeID  uint       `json:"relay_node_id"`
	ExitNodeID   uint       `json:"exit_node_id"`
	Protocol     string     `json:"protocol"`
	TargetHost   string     `json:"target_host"`
	TargetPort   int        `json:"target_port"`
	SpeedLimit   *int64     `json:"speed_limit"`
	TrafficLimit *int64     `json:"traffic_limit"`
	ExpireTime   *time.Time `json:"expire_time"`
}

CreateRuleRequest is the request payload for user-owned rule creation.

type DashboardStats

type DashboardStats struct {
	// 用户统计
	TotalUsers    int64 `json:"total_users"`
	ActiveUsers   int64 `json:"active_users"`
	ExpiredUsers  int64 `json:"expired_users"`
	BannedUsers   int64 `json:"banned_users"`
	TodayNewUsers int64 `json:"today_new_users"`

	// 订单统计
	TotalOrders   int64 `json:"total_orders"`
	PendingOrders int64 `json:"pending_orders"`
	PaidOrders    int64 `json:"paid_orders"`
	TotalRevenue  int64 `json:"total_revenue"`  // 总收入 (分)
	MonthlyIncome int64 `json:"monthly_income"` // 月收入 (分)
	TodayIncome   int64 `json:"today_income"`   // 今日收入 (分)

	// 节点统计
	TotalNodes  int64 `json:"total_nodes"`
	ActiveNodes int64 `json:"active_nodes"`
	OnlineUsers int64 `json:"online_users"`

	// 流量统计
	TotalTrafficUsed int64 `json:"total_traffic_used"` // 总已用流量 (字节)
	TodayTraffic     int64 `json:"today_traffic"`      // 今日流量 (字节)

	// 缓存信息
	CachedAt time.Time `json:"cached_at"`
}

DashboardStats 仪表盘统计数据

type DiagnosisOutcome

type DiagnosisOutcome struct {
	Success     bool    `json:"success"`
	Description string  `json:"description"`
	NodeName    string  `json:"nodeName"`
	NodeID      string  `json:"nodeId"`
	TargetIP    string  `json:"targetIp"`
	TargetPort  int     `json:"targetPort,omitempty"`
	Message     string  `json:"message,omitempty"`
	AverageTime float64 `json:"averageTime,omitempty"`
	PacketLoss  float64 `json:"packetLoss,omitempty"`
}

type DiagnosisReport

type DiagnosisReport struct {
	ForwardName string             `json:"forwardName"`
	Timestamp   int64              `json:"timestamp"`
	Results     []DiagnosisOutcome `json:"results"`
}

type EffectiveAccess

type EffectiveAccess struct {
	ScopeID string                `json:"scope_id"`
	Groups  []model.AccessGroup   `json:"groups"`
	Grants  []model.ResourceGrant `json:"grants"`
	Quotas  []model.QuotaPolicy   `json:"quotas"`
}

func ResolveEffectiveAccess

func ResolveEffectiveAccess(db *gorm.DB, userID uint, planID *uint, scopeID string) (*EffectiveAccess, error)

ResolveEffectiveAccess returns the allow-union of direct and plan-derived memberships for one scope. Calling it once per scope keeps counters and quota policy ownership isolated.

type ForwardAgentBridgeService

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

ForwardAgentBridgeService handles result write-back for clean_agent bridge jobs. It is shared by the agent result handler so that job/forward state transitions stay identical to the local ansible executor semantics.

func NewForwardAgentBridgeService

func NewForwardAgentBridgeService(db *gorm.DB) *ForwardAgentBridgeService

func (*ForwardAgentBridgeService) CompleteJob

func (s *ForwardAgentBridgeService) CompleteJob(taskID string, success bool, output, errMsg string) (alreadyDone bool, err error)

CompleteJob writes an agent-reported result back to the runtime job, the forward runtime state, and marks the bridge mapping completed. It is idempotent: a mapping already in a terminal state is a no-op (duplicate/late reports return alreadyDone=true).

func (*ForwardAgentBridgeService) LookupBridgeTask

func (s *ForwardAgentBridgeService) LookupBridgeTask(taskID string) (*model.ForwardAgentBridgeTask, error)

LookupBridgeTask returns the durable bridge mapping for a given agent task_id. Returns (nil, nil) when the task_id is not a bridge task (e.g. an admin memory task).

type ForwardAgentBridgeWorker

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

ForwardAgentBridgeWorker claims pending clean_agent runtime jobs, asks NodeX to translate the panel payload into a legacy AgentTask, and persists the durable task_id ↔ runtime_job mapping so the agent-task dispatch path can hand it to the target agent. It only touches backend='clean_agent'; nftables_ansible stays on the local ansible executor and gost stays on the synchronous NodeX execute path.

func NewForwardAgentBridgeWorker

func NewForwardAgentBridgeWorker(db *gorm.DB) *ForwardAgentBridgeWorker

func (*ForwardAgentBridgeWorker) RunPendingJobs

func (w *ForwardAgentBridgeWorker) RunPendingJobs(ctx context.Context) error

func (*ForwardAgentBridgeWorker) Start

type ForwardAnsibleStatsWorker

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

ForwardAnsibleStatsWorker 定期为 nftables_ansible/iptables_ansible 后端的转发采集流量统计。 nftables/iptables 的 NAT 计数器只统计经过 DNAT 链的字节数(近似上行方向),没有独立的回程方向 计数,因此这里只记录 uploadTotal,downloadTotal 恒为 0,这是计数器本身的限制而非实现遗漏。

func NewForwardAnsibleStatsWorker

func NewForwardAnsibleStatsWorker(db *gorm.DB) *ForwardAnsibleStatsWorker

func (*ForwardAnsibleStatsWorker) RunOnce

func (*ForwardAnsibleStatsWorker) Start

type ForwardCleanAgentAction

type ForwardCleanAgentAction struct {
	JobID        uint            `json:"jobId"`
	Action       string          `json:"action"`
	ResourceType string          `json:"resourceType"`
	ResourceID   *uint           `json:"resourceId"`
	ForwardID    *uint           `json:"forwardId"`
	TunnelID     *uint           `json:"tunnelId"`
	NodeID       *uint           `json:"nodeId"`
	Payload      json.RawMessage `json:"payload"`
}

type ForwardCleanAgentCreateInput

type ForwardCleanAgentCreateInput struct {
	Name   string `json:"name"`
	NodeID *uint  `json:"nodeId"`
}

type ForwardCleanAgentHeartbeatInput

type ForwardCleanAgentHeartbeatInput struct {
	AgentID      uint     `json:"agentId"`
	Token        string   `json:"token"`
	Version      string   `json:"version"`
	Hostname     string   `json:"hostname"`
	OS           string   `json:"os"`
	Arch         string   `json:"arch"`
	Kernel       string   `json:"kernel"`
	PublicIP     string   `json:"publicIp"`
	PrivateIP    string   `json:"privateIp"`
	Capabilities []string `json:"capabilities"`
	Limit        int      `json:"limit"`
}

type ForwardCleanAgentRegisterInput

type ForwardCleanAgentRegisterInput struct {
	Name         string   `json:"name"`
	Token        string   `json:"token"`
	NodeID       *uint    `json:"nodeId"`
	Version      string   `json:"version"`
	Hostname     string   `json:"hostname"`
	OS           string   `json:"os"`
	Arch         string   `json:"arch"`
	Kernel       string   `json:"kernel"`
	PublicIP     string   `json:"publicIp"`
	PrivateIP    string   `json:"privateIp"`
	Capabilities []string `json:"capabilities"`
}

type ForwardCleanAgentReportInput

type ForwardCleanAgentReportInput struct {
	AgentID  uint   `json:"agentId"`
	Token    string `json:"token"`
	JobID    uint   `json:"jobId"`
	Status   int    `json:"status"`
	Success  *bool  `json:"success"`
	Result   string `json:"result"`
	Error    string `json:"error"`
	Upload   int64  `json:"upload"`
	Download int64  `json:"download"`
}

type ForwardCleanAgentService

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

func NewForwardCleanAgentService

func NewForwardCleanAgentService(db *gorm.DB) *ForwardCleanAgentService

func (*ForwardCleanAgentService) CreateToken

func (*ForwardCleanAgentService) Heartbeat

func (*ForwardCleanAgentService) ListAgents

func (*ForwardCleanAgentService) Register

func (*ForwardCleanAgentService) Report

func (*ForwardCleanAgentService) RevokeAgent

func (s *ForwardCleanAgentService) RevokeAgent(id uint) error

type ForwardCleanAgentTokenResult

type ForwardCleanAgentTokenResult struct {
	Agent *model.ForwardCleanAgent `json:"agent"`
	Token string                   `json:"token"`
}

type ForwardFlowResetWorker

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

func NewForwardFlowResetWorker

func NewForwardFlowResetWorker(db *gorm.DB) *ForwardFlowResetWorker

func (*ForwardFlowResetWorker) RunOnce

func (w *ForwardFlowResetWorker) RunOnce(now time.Time) error

func (*ForwardFlowResetWorker) Start

func (w *ForwardFlowResetWorker) Start(ctx context.Context)

type ForwardGostStatsWorker

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

func NewForwardGostStatsWorker

func NewForwardGostStatsWorker(db *gorm.DB) *ForwardGostStatsWorker

func (*ForwardGostStatsWorker) RunOnce

func (w *ForwardGostStatsWorker) RunOnce(ctx context.Context) error

func (*ForwardGostStatsWorker) Start

func (w *ForwardGostStatsWorker) Start(ctx context.Context)

type ForwardLatencyProber

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

ForwardLatencyProber periodically TCPing-probes forward/tunnel/node targets and stores one pre-aggregated bucket row per (target, time-bucket).

func NewForwardLatencyProber

func NewForwardLatencyProber(db *gorm.DB) *ForwardLatencyProber

func (*ForwardLatencyProber) RunOnce

func (w *ForwardLatencyProber) RunOnce(ctx context.Context) error

RunOnce executes a single probe cycle. Exposed for tests.

func (*ForwardLatencyProber) Start

func (w *ForwardLatencyProber) Start(ctx context.Context)

type ForwardNodeService

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

ForwardNodeService 转发节点服务

func NewForwardNodeService

func NewForwardNodeService(db *gorm.DB) *ForwardNodeService

NewForwardNodeService 创建服务

func (*ForwardNodeService) Create

func (s *ForwardNodeService) Create(node *model.ForwardNode) error

Create 创建节点

func (*ForwardNodeService) Delete

func (s *ForwardNodeService) Delete(id uint) error

Delete 删除节点

func (*ForwardNodeService) GenerateAPIToken

func (s *ForwardNodeService) GenerateAPIToken() (string, error)

GenerateAPIToken 生成API Token

func (*ForwardNodeService) GetByID

func (s *ForwardNodeService) GetByID(id uint) (*model.ForwardNode, error)

GetByID 根据ID获取节点

func (*ForwardNodeService) GetByIDForInventoryScope

func (s *ForwardNodeService) GetByIDForInventoryScope(id uint, scope string) (*model.ForwardNode, error)

func (*ForwardNodeService) GetByType

func (s *ForwardNodeService) GetByType(nodeType string) ([]*model.ForwardNode, error)

GetByType 根据类型获取节点列表

func (*ForwardNodeService) GetNodesByGroup

func (s *ForwardNodeService) GetNodesByGroup(nodeType, group string) ([]*model.ForwardNode, error)

GetNodesByGroup 根据标签获取节点组

func (*ForwardNodeService) GetOnlineNodes

func (s *ForwardNodeService) GetOnlineNodes(nodeType string) ([]*model.ForwardNode, error)

GetOnlineNodes 获取在线节点

func (*ForwardNodeService) HealthCheck

func (s *ForwardNodeService) HealthCheck(ctx context.Context, nodeID uint) (*HealthCheckResult, error)

HealthCheck 健康检查

func (*ForwardNodeService) HealthCheckAll

func (s *ForwardNodeService) HealthCheckAll(ctx context.Context) ([]*HealthCheckResult, error)

HealthCheckAll 检查所有节点

func (*ForwardNodeService) List

func (s *ForwardNodeService) List(nodeType string, status *int, page, pageSize int) ([]*model.ForwardNode, int64, error)

List 获取节点列表

func (*ForwardNodeService) ListByInventoryScope

func (s *ForwardNodeService) ListByInventoryScope(scope, nodeType string, status *int, page, pageSize int) ([]*model.ForwardNode, int64, error)

func (*ForwardNodeService) ParseTags

func (s *ForwardNodeService) ParseTags(tags string) []string

ParseTags 解析标签

func (*ForwardNodeService) ParseTagsWithError

func (s *ForwardNodeService) ParseTagsWithError(tags string) ([]string, error)

ParseTagsWithError 解析标签并返回无效 JSON 错误

func (*ForwardNodeService) SelectBestNode

func (s *ForwardNodeService) SelectBestNode(nodeType string, mode string) (*model.ForwardNode, error)

SelectBestNode 选择最佳节点 (负载均衡)

func (*ForwardNodeService) SetTags

func (s *ForwardNodeService) SetTags(nodeID uint, tags []string) error

SetTags 设置标签

func (*ForwardNodeService) Update

func (s *ForwardNodeService) Update(node *model.ForwardNode) error

Update 更新节点

func (*ForwardNodeService) UpdateStats

func (s *ForwardNodeService) UpdateStats(nodeID uint, upload, download int64, connDelta int) error

UpdateStats 更新节点统计

type ForwardObservabilityService

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

ForwardObservabilityService serves read-only latency/topology queries backed by the v2_forward_latency_bucket time-series written by ForwardLatencyProber.

func NewForwardObservabilityService

func NewForwardObservabilityService(db *gorm.DB) *ForwardObservabilityService

func (*ForwardObservabilityService) GetLatencyTrend

func (s *ForwardObservabilityService) GetLatencyTrend(targetKey string, from, to time.Time) (*LatencyTrend, error)

func (*ForwardObservabilityService) GetMultiIngressLatency

func (s *ForwardObservabilityService) GetMultiIngressLatency(forwardID uint) ([]MultiIngressRow, error)

GetMultiIngressLatency lists, for the forward's exit target, the latest ingress latency of every tunnel reaching the same exit node (incl. the forward's own tunnel).

func (*ForwardObservabilityService) GetTopology

func (s *ForwardObservabilityService) GetTopology() (*Topology, error)

func (*ForwardObservabilityService) ListTargets

func (s *ForwardObservabilityService) ListTargets() ([]TargetCatalogItem, error)

type ForwardRuleConfig

type ForwardRuleConfig struct {
	RuleID     uint   `json:"rule_id"`
	ListenPort int    `json:"listen_port"`
	Protocol   string `json:"protocol"`
	TargetHost string `json:"target_host"`
	TargetPort int    `json:"target_port"`
	SpeedLimit int64  `json:"speed_limit,omitempty"`
}

ForwardRuleConfig is the node-side rule payload.

type ForwardRuleService

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

ForwardRuleService handles forward rule persistence and runtime sync.

func NewForwardRuleService

func NewForwardRuleService(db *gorm.DB, nodeService *ForwardNodeService) *ForwardRuleService

NewForwardRuleService preserves the existing constructor shape and uses the default runtime provider.

func NewForwardRuleServiceWithProvider

func NewForwardRuleServiceWithProvider(db *gorm.DB, nodeService *ForwardNodeService, provider ForwardRuntimeProvider) *ForwardRuleService

NewForwardRuleServiceWithProvider allows future runtime backends to be injected explicitly.

func (*ForwardRuleService) CheckIPAllowed

func (s *ForwardRuleService) CheckIPAllowed(rule *model.ForwardRule, ip string) bool

CheckIPAllowed checks whether an IP is allowed to access the rule. If the rule has an AllowedIPs list (comma-separated CIDRs or single IPs), the client IP must fall within at least one entry. If no allowlist is configured, all IPs are permitted.

func (*ForwardRuleService) CheckPortAvailable

func (s *ForwardRuleService) CheckPortAvailable(relayNodeID uint, port int) (bool, error)

CheckPortAvailable checks whether a relay port is free.

func (*ForwardRuleService) Create

func (s *ForwardRuleService) Create(rule *model.ForwardRule) error

Create creates a rule and syncs it to the runtime when enabled.

func (*ForwardRuleService) CreateRuleForUser

func (s *ForwardRuleService) CreateRuleForUser(userID uint, req *CreateRuleRequest) (*model.ForwardRule, error)

CreateRuleForUser creates a user-owned rule.

func (*ForwardRuleService) Delete

func (s *ForwardRuleService) Delete(id uint) error

Delete deletes a rule from the runtime backend and then removes it from the database.

func (*ForwardRuleService) GetByID

func (s *ForwardRuleService) GetByID(id uint) (*model.ForwardRule, error)

GetByID fetches a rule by ID.

func (*ForwardRuleService) GetConfigForNode

func (s *ForwardRuleService) GetConfigForNode(nodeID uint) (*NodeForwardConfig, error)

GetConfigForNode returns runtime config that should be pushed to a node.

func (*ForwardRuleService) GetEnabledRules

func (s *ForwardRuleService) GetEnabledRules() ([]*model.ForwardRule, error)

GetEnabledRules fetches enabled rules.

func (*ForwardRuleService) GetFreePort

func (s *ForwardRuleService) GetFreePort(relayNodeID uint, startPort, endPort int) (int, error)

GetFreePort finds a free relay port in the given range.

func (*ForwardRuleService) GetPortMapping

func (s *ForwardRuleService) GetPortMapping(relayNodeID uint) (map[int]*model.ForwardRule, error)

GetPortMapping returns the port-to-rule mapping for a relay node.

func (*ForwardRuleService) GetTrafficStats

func (s *ForwardRuleService) GetTrafficStats(ruleID uint, start, end time.Time) ([]*model.ForwardStats, error)

GetTrafficStats returns traffic stats for a rule over a time range.

func (*ForwardRuleService) GetUserRules

func (s *ForwardRuleService) GetUserRules(userID uint) ([]*model.ForwardRule, error)

GetUserRules fetches rules owned by a user.

func (*ForwardRuleService) List

func (s *ForwardRuleService) List(page, pageSize int, userID *uint) ([]*model.ForwardRule, int64, error)

List fetches a paginated rule list.

func (*ForwardRuleService) MatchRule

func (s *ForwardRuleService) MatchRule(sourceIP string, targetHost string, targetPort int) (*model.ForwardRule, error)

MatchRule matches an incoming request against enabled rules.

func (*ForwardRuleService) Toggle

func (s *ForwardRuleService) Toggle(id uint, enabled bool) error

Toggle updates the enabled state and syncs it to the runtime backend.

func (*ForwardRuleService) Update

func (s *ForwardRuleService) Update(rule *model.ForwardRule) error

Update updates a rule and syncs it to the runtime backend.

func (*ForwardRuleService) UpdateConnections

func (s *ForwardRuleService) UpdateConnections(ruleID uint, delta int) error

UpdateConnections updates connection counters.

func (*ForwardRuleService) UpdateTraffic

func (s *ForwardRuleService) UpdateTraffic(ruleID uint, upload, download int64) error

UpdateTraffic updates traffic counters.

func (*ForwardRuleService) ValidateUserForRule

func (s *ForwardRuleService) ValidateUserForRule(rule *model.ForwardRule) error

ValidateUserForRule validates that a user-bound rule's associated user is valid. Checks: user exists, is not banned, and has remaining traffic allowance.

func (*ForwardRuleService) ValidateUserRule

func (s *ForwardRuleService) ValidateUserRule(userID, ruleID uint) (bool, error)

ValidateUserRule validates whether a user can access a rule.

type ForwardRuntimeProvider

type ForwardRuntimeProvider interface {
	CreateForwardRule(ctx context.Context, rule *model.ForwardRule) error
	UpdateForwardRule(ctx context.Context, rule *model.ForwardRule) error
	DeleteForwardRule(ctx context.Context, rule *model.ForwardRule) error
	SyncForwardRule(ctx context.Context, rule *model.ForwardRule) error
}

ForwardRuntimeProvider abstracts the runtime backend used by forward rule sync.

func NewForwardRuntimeProvider

func NewForwardRuntimeProvider(db *gorm.DB) ForwardRuntimeProvider

NewForwardRuntimeProvider returns the default runtime provider.

type GroupStats

type GroupStats struct {
	GroupID       uint   `json:"group_id"`
	GroupName     string `json:"group_name"`
	UserCount     int64  `json:"user_count"`
	TemplateCount int64  `json:"template_count"`
	ProtocolCount int64  `json:"protocol_count"`
	OnlineNodes   int64  `json:"online_nodes"`
	TotalTraffic  int64  `json:"total_traffic"` // 用户已用流量总和 (bytes)
	EnabledUsers  int64  `json:"enabled_users"` // 未过期用户数
	PlanCount     int64  `json:"plan_count"`    // 关联套餐数
}

GroupStats 订阅分组统计数据

type HealthCheckResult

type HealthCheckResult struct {
	NodeID    uint      `json:"node_id"`
	Status    int       `json:"status"`
	Latency   int64     `json:"latency"`
	CheckTime time.Time `json:"check_time"`
	Error     string    `json:"error,omitempty"`
}

HealthCheckResult 健康检查结果

type HourlyTraffic

type HourlyTraffic struct {
	HourTs  int64 `json:"hour_ts"` // 该小时起始的 Unix 秒 (整点)
	Traffic int64 `json:"traffic"` // 该小时流量 (字节, 已按倍率计)
}

HourlyTraffic 单个小时的流量统计

type InviteService

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

InviteService 邀请服务

func NewInviteService

func NewInviteService(db *gorm.DB) *InviteService

NewInviteService 创建服务

func (*InviteService) AddCommission

func (s *InviteService) AddCommission(userID, orderID, fromUserID uint, amount float64, recordType int, remark string) error

AddCommission 添加佣金

func (*InviteService) CalculateCommission

func (s *InviteService) CalculateCommission(orderAmount float64) float64

CalculateCommission 计算佣金

func (*InviteService) GenerateCodesForUser

func (s *InviteService) GenerateCodesForUser(userID uint, count int) error

GenerateCodesForUser 为用户生成邀请码

func (*InviteService) GenerateInviteCode

func (s *InviteService) GenerateInviteCode(userID *uint) (*model.InviteCode, error)

GenerateInviteCode 生成邀请码

func (*InviteService) GetCommissionRecords

func (s *InviteService) GetCommissionRecords(userID uint, page, pageSize int) ([]model.CommissionRecord, int64, error)

GetCommissionRecords 获取佣金记录

func (*InviteService) GetConfig

func (s *InviteService) GetConfig() (*model.InviteConfig, error)

GetConfig 获取配置

func (*InviteService) GetInviteStats

func (s *InviteService) GetInviteStats(userID uint) (map[string]any, error)

GetInviteStats 获取邀请统计

func (*InviteService) GetUserInviteCodes

func (s *InviteService) GetUserInviteCodes(userID uint) ([]model.InviteCode, error)

GetUserInviteCodes 获取用户邀请码

func (*InviteService) GetWithdrawRecords

func (s *InviteService) GetWithdrawRecords(userID uint, page, pageSize int) ([]model.CommissionWithdraw, int64, error)

GetWithdrawRecords 获取提现记录

func (*InviteService) RequestWithdraw

func (s *InviteService) RequestWithdraw(userID uint, amount float64, method, account, name string) (*model.CommissionWithdraw, error)

RequestWithdraw 申请提现

func (*InviteService) SetConfig

func (s *InviteService) SetConfig(cfg *model.InviteConfig)

SetConfig 设置配置

func (*InviteService) UseInviteCode

func (s *InviteService) UseInviteCode(code string, userID uint) error

UseInviteCode 使用邀请码

func (*InviteService) ValidateInviteCode

func (s *InviteService) ValidateInviteCode(code string) (*model.InviteCode, error)

ValidateInviteCode 验证邀请码

type KernelOperationStatus

type KernelOperationStatus struct {
	ID                   string     `json:"id"`
	NodeID               *uint      `json:"node_id,omitempty"`
	PluginID             string     `json:"plugin_id"`
	TargetVersion        string     `json:"target_version"`
	TopologyDeploymentID *uint      `json:"topology_deployment_id,omitempty"`
	TopologyStepID       *uint      `json:"topology_step_id,omitempty"`
	TopologyRevision     int64      `json:"topology_revision,omitempty"`
	Kind                 string     `json:"kind"`
	Revision             int64      `json:"revision"`
	ConfigHash           string     `json:"config_hash,omitempty"`
	State                string     `json:"state"`
	DeadlineAt           *time.Time `json:"deadline_at,omitempty"`
	DispatchedAt         *time.Time `json:"dispatched_at,omitempty"`
	AcknowledgedAt       *time.Time `json:"acknowledged_at,omitempty"`
	ObservedAt           *time.Time `json:"observed_at,omitempty"`
	CancelAt             *time.Time `json:"cancel_at,omitempty"`
	Attempt              int        `json:"attempt"`
	HasResult            bool       `json:"has_result"`
	HasError             bool       `json:"has_error"`
	CreatedAt            time.Time  `json:"created_at"`
	UpdatedAt            time.Time  `json:"updated_at"`
}

KernelOperationStatus is the public, non-secret projection of a durable operation. ConfigJSON, ResultJSON, session identity, and raw Agent errors intentionally stay inside the kernel persistence boundary.

func PublicKernelOperation

func PublicKernelOperation(operation model.KernelOperation) KernelOperationStatus

type LatencyPoint

type LatencyPoint struct {
	BucketAt int64   `json:"bucketAt"` // unix milli
	Min      float64 `json:"min"`
	Avg      float64 `json:"avg"`
	Max      float64 `json:"max"`
	P95      float64 `json:"p95"`
	Loss     float64 `json:"loss"`
	Samples  int     `json:"samples"`
	Success  int     `json:"success"`
}

type LatencyTrend

type LatencyTrend struct {
	TargetKey       string         `json:"targetKey"`
	Label           string         `json:"label"`
	Host            string         `json:"host"`
	Port            int            `json:"port"`
	IntervalSeconds int            `json:"intervalSeconds"`
	Points          []LatencyPoint `json:"points"`
}

type LoadBalancerService

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

LoadBalancerService 负载均衡服务

func NewLoadBalancerService

func NewLoadBalancerService(db *gorm.DB) *LoadBalancerService

NewLoadBalancerService 创建服务

func (*LoadBalancerService) Create

Create 创建负载均衡器

func (*LoadBalancerService) Delete

func (s *LoadBalancerService) Delete(id uint) error

Delete 删除负载均衡器

func (*LoadBalancerService) GetByID

func (s *LoadBalancerService) GetByID(id uint) (*model.LoadBalancer, error)

GetByID 获取负载均衡器

func (*LoadBalancerService) GetStats

func (s *LoadBalancerService) GetStats(lbID uint) (map[string]any, error)

GetStats 获取负载均衡统计

func (*LoadBalancerService) List

func (s *LoadBalancerService) List(groupID uint) ([]model.LoadBalancer, error)

List 获取负载均衡器列表

func (*LoadBalancerService) RunHealthCheck

func (s *LoadBalancerService) RunHealthCheck(lbID uint) error

RunHealthCheck 执行健康检查

func (*LoadBalancerService) SelectNode

func (s *LoadBalancerService) SelectNode(lbID uint) (*model.ForwardNode, error)

SelectNode 选择最佳节点

func (*LoadBalancerService) Update

Update 更新负载均衡器

type LoginRateLimitOptions

type LoginRateLimitOptions struct {
	Enabled      bool
	MaxAttempts  int
	Window       time.Duration
	Lockout      time.Duration
	CleanupAfter time.Duration
}

func ResolveLoginRateLimitOptions

func ResolveLoginRateLimitOptions(cfg *config.Config) LoginRateLimitOptions

func ResolveRegisterRateLimitOptions

func ResolveRegisterRateLimitOptions(cfg *config.Config) LoginRateLimitOptions

type LoginRateLimiter

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

func GetLoginRateLimiter

func GetLoginRateLimiter() *LoginRateLimiter

func NewLoginRateLimiter

func NewLoginRateLimiter() *LoginRateLimiter

func (*LoginRateLimiter) Check

func (l *LoginRateLimiter) Check(key string, options LoginRateLimitOptions) (bool, time.Duration)

func (*LoginRateLimiter) RecordFailure

func (l *LoginRateLimiter) RecordFailure(key string, options LoginRateLimitOptions)

func (*LoginRateLimiter) RecordSuccess

func (l *LoginRateLimiter) RecordSuccess(key string)

type MFAService

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

MFAService MFA服务

func NewMFAService

func NewMFAService(db *gorm.DB, config *model.MFAConfig) *MFAService

NewMFAService 创建服务

func (*MFAService) CheckBruteForce

func (s *MFAService) CheckBruteForce(userID uint, maxAttempts int, window time.Duration) (bool, error)

CheckBruteForce 检查暴力破解

func (*MFAService) DisableMFA

func (s *MFAService) DisableMFA(userID uint, password string) error

DisableMFA 禁用MFA

func (*MFAService) EnableTOTP

func (s *MFAService) EnableTOTP(userID uint, code string) error

EnableTOTP 启用TOTP

func (*MFAService) GenerateQRCodeURL

func (s *MFAService) GenerateQRCodeURL(secret, email string) string

GenerateQRCodeURL 生成二维码URL (用于Google Chart API等)

func (*MFAService) GetRemainingBackupCodes

func (s *MFAService) GetRemainingBackupCodes(userID uint) (int, error)

GetRemainingBackupCodes 获取剩余备用码数量

func (*MFAService) GetUserMFA

func (s *MFAService) GetUserMFA(userID uint) (*model.UserMFA, error)

GetUserMFA 获取用户MFA配置

func (*MFAService) IsEnabled

func (s *MFAService) IsEnabled() bool

IsEnabled 检查MFA是否启用

func (*MFAService) IsEnforcedForUser

func (s *MFAService) IsEnforcedForUser(user *model.User) bool

IsEnforcedForUser 检查是否对用户强制MFA

func (*MFAService) IsUserMFAEnabled

func (s *MFAService) IsUserMFAEnabled(userID uint) (bool, error)

IsUserMFAEnabled 检查用户是否启用了MFA

func (*MFAService) RecordLoginAttempt

func (s *MFAService) RecordLoginAttempt(userID uint, ip, userAgent string, success bool, method string) error

RecordLoginAttempt 记录登录尝试

func (*MFAService) RegenerateBackupCodes

func (s *MFAService) RegenerateBackupCodes(userID uint) ([]string, error)

RegenerateBackupCodes 重新生成备用码

func (*MFAService) SetConfig

func (s *MFAService) SetConfig(config *model.MFAConfig)

SetConfig updates MFA runtime config without recreating service.

func (*MFAService) SetupTOTP

func (s *MFAService) SetupTOTP(userID uint, email string) (*TOTPSetup, error)

SetupTOTP 设置TOTP

func (*MFAService) Verify

func (s *MFAService) Verify(userID uint, code, method string) (bool, error)

Verify 验证MFA代码

func (*MFAService) VerifyBackupCode

func (s *MFAService) VerifyBackupCode(mfa *model.UserMFA, code string) (bool, error)

VerifyBackupCode 验证备用码

func (*MFAService) VerifyTOTP

func (s *MFAService) VerifyTOTP(secret, code string) bool

VerifyTOTP 验证TOTP代码

type MonitorEntry

type MonitorEntry struct {
	NodeID    uint32         `json:"node_id"`
	System    map[string]any `json:"system"`
	Timestamp time.Time      `json:"timestamp"`
}

MonitorEntry holds a single monitor data push from an agent.

type MonitorStore

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

MonitorStore keeps a ring buffer of the last N monitor entries per node.

func NewMonitorStore

func NewMonitorStore(capacity int) *MonitorStore

NewMonitorStore creates a MonitorStore with the given per-node capacity.

func (*MonitorStore) Get

func (ms *MonitorStore) Get(nodeID uint32, n int) []MonitorEntry

Get returns the last N monitor entries for the node (up to all available).

func (*MonitorStore) Latest

func (ms *MonitorStore) Latest(nodeID uint32) *MonitorEntry

Latest returns the most recent monitor entry for the node.

func (*MonitorStore) Push

func (ms *MonitorStore) Push(nodeID uint32, system map[string]any, ts time.Time)

Push adds a monitor entry for the node.

type MultiIngressRow

type MultiIngressRow struct {
	TunnelID      uint    `json:"tunnelId"`
	TunnelName    string  `json:"tunnelName"`
	IngressNodeID uint    `json:"ingressNodeId"`
	IngressLabel  string  `json:"ingressLabel"`
	IngressIP     string  `json:"ingressIp"`
	AvgRTT        float64 `json:"avgRtt"`
	Loss          float64 `json:"loss"`
	BucketAt      int64   `json:"bucketAt"`
	Online        bool    `json:"online"`
}

type NodeForwardConfig

type NodeForwardConfig struct {
	NodeID uint                `json:"node_id"`
	Rules  []ForwardRuleConfig `json:"rules"`
}

NodeForwardConfig is the rule bundle for a node.

type NodeListParams

type NodeListParams struct {
	Page     int
	PageSize int
	Status   *model.NodeStatus
	GroupID  *uint
	Search   string
}

type NodeListResult

type NodeListResult struct {
	Total int64        `json:"total"`
	List  []model.Node `json:"list"`
}

type NodeLogInput

type NodeLogInput struct {
	Level      string
	Source     string
	Message    string
	TraceID    string
	FieldsJSON string
	LoggedAt   *time.Time
}

type NodeLogListParams

type NodeLogListParams struct {
	NodeID   uint
	Page     int
	PageSize int
	Level    string
	Source   string
	Search   string
}

type NodeLogListResult

type NodeLogListResult struct {
	Total int64           `json:"total"`
	List  []model.NodeLog `json:"list"`
}

type NodeLogService

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

func NewNodeLogService

func NewNodeLogService() *NodeLogService

func (*NodeLogService) GetLogs

func (*NodeLogService) RecordLogs

func (s *NodeLogService) RecordLogs(nodeID uint, inputs []NodeLogInput) error

type NodeMonthlyResetWorker

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

NodeMonthlyResetWorker 按每个节点自己的 MonthlyResetDay 把 monthly_upload/ monthly_download 清零, 用于月流量限额的周期性重置(仅统计层面, 不做任何 自动禁用/限速动作)。

func NewNodeMonthlyResetWorker

func NewNodeMonthlyResetWorker(db *gorm.DB) *NodeMonthlyResetWorker

func (*NodeMonthlyResetWorker) RunOnce

func (w *NodeMonthlyResetWorker) RunOnce(now time.Time) error

func (*NodeMonthlyResetWorker) Start

func (w *NodeMonthlyResetWorker) Start(ctx context.Context)

type NodePluginObservedSnapshot

type NodePluginObservedSnapshot struct {
	PluginID         string
	Version          string
	DesiredRevision  uint64
	ObservedRevision uint64
	ConfigHash       string
	Health           string
	RulesetSHA256    string
	ObservedAt       time.Time
	RuleCounters     []NodePluginRuleCounter
}

NodePluginObservedSnapshot is the bounded data the authenticated Agent may contribute on its heartbeat. It deliberately has no raw plugin payload or error text: those would make the generic observed-state API a secret leak.

type NodePluginRuleCounter

type NodePluginRuleCounter struct {
	RuleID  string `json:"rule_id"`
	Packets uint64 `json:"packets"`
	Bytes   uint64 `json:"bytes"`
}

NodePluginRuleCounter is the kernel-neutral form of one plugin-owned counter. The kernel stores it as opaque, bounded data and does not infer quota or billing semantics from it.

type NodeService

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

NodeService 节点服务

func NewNodeService

func NewNodeService() *NodeService

NewNodeService 创建节点服务

func (*NodeService) AccumulateTrafficOnly

func (s *NodeService) AccumulateTrafficOnly(nodeID uint, upload, download int64) error

AccumulateTrafficOnly 仅把流量累加到节点自身及其父节点链,不更新心跳指标。

func (*NodeService) AssignProtocolsToGroup

func (s *NodeService) AssignProtocolsToGroup(groupID uint, protocolIDs []uint) error

AssignProtocolsToGroup 为分组分配节点协议

func (*NodeService) CreateNode

func (s *NodeService) CreateNode(node *model.Node) error

CreateNode 创建节点 (手动添加)

func (*NodeService) CreateProtocol

func (s *NodeService) CreateProtocol(protocol *model.NodeProtocol) error

CreateProtocol 创建协议

func (*NodeService) DeleteAuthKey

func (s *NodeService) DeleteAuthKey(id uint) error

DeleteAuthKey 删除授权密钥

func (*NodeService) DeleteNode

func (s *NodeService) DeleteNode(id uint) error

DeleteNode 删除节点

func (*NodeService) DeleteProtocol

func (s *NodeService) DeleteProtocol(id uint) error

DeleteProtocol 删除协议

func (*NodeService) GenerateAuthKey

func (s *NodeService) GenerateAuthKey(name string, expireDays int) (*model.AuthorizedKey, string, error)

GenerateAuthKey 生成授权密钥

func (*NodeService) GetAllAvailableProtocols

func (s *NodeService) GetAllAvailableProtocols() ([]model.NodeProtocol, error)

GetAllAvailableProtocols 获取所有可显示在订阅中的协议

func (*NodeService) GetAuthKeys

func (s *NodeService) GetAuthKeys() ([]model.AuthorizedKey, error)

GetAuthKeys 获取授权密钥列表

func (*NodeService) GetNode

func (s *NodeService) GetNode(id uint) (*model.Node, error)

GetNode 获取节点详情

func (*NodeService) GetNodeByAPIKey

func (s *NodeService) GetNodeByAPIKey(apiKey string) (*model.Node, error)

GetNodeByAPIKey 通过API Key获取节点

func (*NodeService) GetNodeStats

func (s *NodeService) GetNodeStats() (map[string]any, error)

GetNodeStats 获取节点统计

func (*NodeService) GetNodes

func (s *NodeService) GetNodes(params NodeListParams) (*NodeListResult, error)

GetNodes 获取节点列表

func (*NodeService) GetProtocol

func (s *NodeService) GetProtocol(id uint) (*model.NodeProtocol, error)

GetProtocol 获取协议详情

func (*NodeService) GetProtocols

func (s *NodeService) GetProtocols(nodeID uint) ([]model.NodeProtocol, error)

GetProtocols 获取节点的协议列表

func (*NodeService) GetProtocolsByGroup

func (s *NodeService) GetProtocolsByGroup(groupID uint) ([]model.NodeProtocol, error)

GetProtocolsByGroup 获取关联到特定分组的协议列表

func (*NodeService) Heartbeat

func (s *NodeService) Heartbeat(nodeID uint, req *model.NodeHeartbeatRequest) error

Heartbeat 节点心跳

func (*NodeService) RecordNodePluginObservedStates

func (s *NodeService) RecordNodePluginObservedStates(nodeID uint, snapshots []NodePluginObservedSnapshot, receivedAt time.Time) (int, error)

RecordNodePluginObservedStates stores trusted, monotonic plugin runtime snapshots from an authenticated node. A malformed or unauthorized snapshot is rejected independently so one bad plugin cannot suppress another plugin's heartbeat evidence.

func (*NodeService) RecordPluginTelemetry

func (s *NodeService) RecordPluginTelemetry(nodeID uint, heartbeatMetrics map[string]float64, receivedAt time.Time) (int, error)

RecordPluginTelemetry persists only metrics from enabled assignments on the authenticated node. Non-plugin heartbeat metrics remain kernel-owned and are intentionally excluded from this plugin state table.

func (*NodeService) RegisterNode

func (s *NodeService) RegisterNode(req *model.NodeRegisterRequest, clientIP string) (*model.NodeRegisterResponse, error)

RegisterNode 节点自动注册

func (*NodeService) SyncProtocolToNode

func (s *NodeService) SyncProtocolToNode(nodeID uint) error

SyncProtocolToNode 同步协议配置到节点

func (*NodeService) UpdateLastCheckAt

func (s *NodeService) UpdateLastCheckAt(nodeID uint) error

UpdateLastCheckAt 更新节点最后检查时间 (用于 UniProxy 接口)

func (*NodeService) UpdateNode

func (s *NodeService) UpdateNode(id uint, updates map[string]any) error

UpdateNode 更新节点

func (*NodeService) UpdateProtocol

func (s *NodeService) UpdateProtocol(id uint, updates map[string]any) error

UpdateProtocol 更新协议

func (*NodeService) UpdateRuntimeHealth

func (s *NodeService) UpdateRuntimeHealth(nodeID uint, healthy bool, message string) error

UpdateRuntimeHealth records the health of a node's managed runtime process. It deliberately does not change last_check_at: the node can be reachable while its relay process is unhealthy, and those are different signals.

type NotificationService

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

NotificationService 通知服务

func NewNotificationService

func NewNotificationService(db *gorm.DB, cfg *config.Config) *NotificationService

NewNotificationService 创建服务

func (*NotificationService) Broadcast

func (s *NotificationService) Broadcast(title, content string) error

Broadcast 广播通知 (所有用户)

func (*NotificationService) CreateTemplate

func (s *NotificationService) CreateTemplate(tmpl *model.NotificationTemplate) error

CreateTemplate 创建模板

func (*NotificationService) DeleteTemplate

func (s *NotificationService) DeleteTemplate(id uint) error

DeleteTemplate 删除模板

func (*NotificationService) Drain

func (s *NotificationService) Drain()

Drain waits for all asynchronous notifications registered before the call. The registration mutex prevents WaitGroup.Add from racing with Wait.

func (*NotificationService) GetLogs

func (s *NotificationService) GetLogs(page, pageSize int, notifyType string) ([]*model.NotificationLog, int64, error)

GetLogs 获取通知日志

func (*NotificationService) GetTemplate

func (s *NotificationService) GetTemplate(notifyType, event string) (*model.NotificationTemplate, error)

GetTemplate 获取通知模板

func (*NotificationService) ListTemplates

func (s *NotificationService) ListTemplates() ([]*model.NotificationTemplate, error)

ListTemplates 获取模板列表

func (*NotificationService) NotifyNodeOffline

func (s *NotificationService) NotifyNodeOffline(node *model.Node) error

NotifyNodeOffline 节点离线通知 (管理员)

func (*NotificationService) NotifyOrderPaid

func (s *NotificationService) NotifyOrderPaid(order *model.Order, user *model.User) error

NotifyOrderPaid 订单支付成功通知

func (*NotificationService) NotifyTicketReply

func (s *NotificationService) NotifyTicketReply(ticket *model.Ticket, replyBy string) error

NotifyTicketReply 工单回复通知

func (*NotificationService) NotifyTrafficLow

func (s *NotificationService) NotifyTrafficLow(user *model.User, percentLeft float64) error

NotifyTrafficLow 流量不足通知

func (*NotificationService) NotifyUserExpire

func (s *NotificationService) NotifyUserExpire(user *model.User, daysLeft int) error

NotifyUserExpire 用户到期通知

func (*NotificationService) Send

func (s *NotificationService) Send(userID *uint, notifyType, event, title, content string, data map[string]any) error

Send 发送通知

func (*NotificationService) SendEmail

func (s *NotificationService) SendEmail(to, subject, body string) error

SendEmail 发送邮件

func (*NotificationService) SetEmailConfig

func (s *NotificationService) SetEmailConfig(cfg *model.EmailConfig)

SetEmailConfig 设置邮件配置

func (*NotificationService) UpdateTemplate

func (s *NotificationService) UpdateTemplate(tmpl *model.NotificationTemplate) error

UpdateTemplate 更新模板

type OperationLogInput

type OperationLogInput struct {
	UserID     *uint
	Username   string
	Action     string
	Module     string
	TargetType string
	TargetID   *uint
	Content    string
	IP         string
	UserAgent  string
	Status     int
}

type OperationLogService

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

func NewOperationLogService

func NewOperationLogService(db *gorm.DB) *OperationLogService

func (*OperationLogService) Record

func (s *OperationLogService) Record(input *OperationLogInput) error

type OrderListParams

type OrderListParams struct {
	Page     int
	PageSize int
	UserID   *uint
	Status   *int
	Type     *int
	TradeNo  string
	Email    string
	OrderBy  string
}

OrderListParams 订单列表查询参数

type OrderListResult

type OrderListResult struct {
	Total int64         `json:"total"`
	List  []model.Order `json:"list"`
}

OrderListResult 订单列表结果

type OrderService

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

OrderService 订单服务

func NewOrderService

func NewOrderService() *OrderService

NewOrderService 创建订单服务

func (*OrderService) Cancel

func (s *OrderService) Cancel(orderID uint) error

Cancel 取消订单

func (*OrderService) Complete

func (s *OrderService) Complete(orderID uint) error

Complete 完成订单 (支付成功后处理)

func (*OrderService) Create

func (s *OrderService) Create(params CreateOrderParams) (*model.Order, error)

Create 创建订单

func (*OrderService) GetByID

func (s *OrderService) GetByID(id uint) (*model.Order, error)

GetByID 根据ID获取订单

func (*OrderService) GetByTradeNo

func (s *OrderService) GetByTradeNo(tradeNo string) (*model.Order, error)

GetByTradeNo 根据交易号获取订单

func (*OrderService) GetList

func (s *OrderService) GetList(params OrderListParams) (*OrderListResult, error)

GetList 获取订单列表

func (*OrderService) GetStats

func (s *OrderService) GetStats() (map[string]any, error)

GetStats 获取订单统计

func (*OrderService) GetUserOrders

func (s *OrderService) GetUserOrders(userID uint, page, pageSize int) (*OrderListResult, error)

GetUserOrders 获取用户订单

func (*OrderService) UpdateStatus

func (s *OrderService) UpdateStatus(orderID uint, status int) error

UpdateStatus 更新订单状态

type PackageMigrationCheckpoint

type PackageMigrationCheckpoint struct {
	OpaqueCheckpoint string
	ValidationDigest string
	Complete         bool
	FailureCode      string
}

PackageMigrationCheckpoint is an opaque host response persisted before the caller can act on that response. Its fields are intentionally not parsed by the kernel.

type PanelAdminTunnelItem

type PanelAdminTunnelItem struct {
	ID            uint    `json:"id"`
	Name          string  `json:"name"`
	InNodeID      uint    `json:"inNodeId"`
	OutNodeID     *uint   `json:"outNodeId"`
	Type          int     `json:"type"`
	Flow          int     `json:"flow"`
	TrafficRatio  float64 `json:"trafficRatio"`
	InterfaceName string  `json:"interfaceName"`
	Protocol      string  `json:"protocol"`
	TCPListenAddr string  `json:"tcpListenAddr"`
	UDPListenAddr string  `json:"udpListenAddr"`
	InIP          string  `json:"inIp"`
	OutIP         string  `json:"outIp"`
	Status        int     `json:"status"`
	CreatedTime   int64   `json:"createdTime"`
	UpdatedTime   int64   `json:"updatedTime"`
}

type PanelForwardFlowData

type PanelForwardFlowData struct {
	N string `json:"n"`
	U int64  `json:"u"`
	D int64  `json:"d"`
}

type PanelForwardInput

type PanelForwardInput struct {
	Name          string `json:"name"`
	TunnelID      uint   `json:"tunnelId"`
	InPort        *int   `json:"inPort"`
	RemoteAddr    string `json:"remoteAddr"`
	InterfaceName string `json:"interfaceName"`
	Strategy      string `json:"strategy"`
}

type PanelForwardListItem

type PanelForwardListItem struct {
	ID                  uint   `json:"id"`
	Name                string `json:"name"`
	TunnelID            uint   `json:"tunnelId"`
	TunnelName          string `json:"tunnelName"`
	InIP                string `json:"inIp"`
	InPort              int    `json:"inPort"`
	RemoteAddr          string `json:"remoteAddr"`
	InterfaceName       string `json:"interfaceName"`
	Strategy            string `json:"strategy"`
	Status              int    `json:"status"`
	InFlow              int64  `json:"inFlow"`
	OutFlow             int64  `json:"outFlow"`
	RuntimeBackend      string `json:"runtimeBackend"`
	RuntimeStatus       int    `json:"runtimeStatus"`
	RuntimeMessage      string `json:"runtimeMessage"`
	LastRuntimeSyncTime int64  `json:"lastRuntimeSyncTime"`
	CreatedTime         int64  `json:"createdTime"`
	UpdatedTime         int64  `json:"updatedTime"`
	UserName            string `json:"userName"`
	UserID              uint   `json:"userId"`
	Inx                 int    `json:"inx"`
}

type PanelForwardOrderUpdate

type PanelForwardOrderUpdate struct {
	ID  uint `json:"id"`
	Inx int  `json:"inx"`
}

type PanelForwardRuntimeAttachmentState

type PanelForwardRuntimeAttachmentState struct {
	Model       string `json:"model"`
	Description string `json:"description"`
}

type PanelForwardRuntimeCommandHints

type PanelForwardRuntimeCommandHints struct {
	PowerShell []string `json:"powerShell"`
	Bash       []string `json:"bash"`
	Upgrade    []string `json:"upgrade"`
	References []string `json:"references"`
}

type PanelForwardRuntimeConfigState

type PanelForwardRuntimeConfigState struct {
	Backend           string `json:"backend"`
	NodeXMode         bool   `json:"nodeXMode"`
	BaseURL           string `json:"baseUrl,omitempty"`
	BaseURLConfigured bool   `json:"baseUrlConfigured"`
	TokenConfigured   bool   `json:"tokenConfigured"`
	TimeoutSeconds    int64  `json:"timeoutSeconds"`
}

type PanelForwardRuntimeDoctorSummary

type PanelForwardRuntimeDoctorSummary struct {
	PanelForwardRuntimeStatusSummary
	Commands PanelForwardRuntimeCommandHints `json:"commands"`
}

type PanelForwardRuntimeJobExecutor

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

func NewPanelForwardRuntimeJobExecutor

func NewPanelForwardRuntimeJobExecutor(db *gorm.DB) *PanelForwardRuntimeJobExecutor

func (*PanelForwardRuntimeJobExecutor) RunPendingJobs

func (e *PanelForwardRuntimeJobExecutor) RunPendingJobs(ctx context.Context) error

func (*PanelForwardRuntimeJobExecutor) Start

type PanelForwardRuntimeProbe

type PanelForwardRuntimeProbe struct {
	OK         bool   `json:"ok"`
	StatusCode int    `json:"statusCode,omitempty"`
	Body       string `json:"body,omitempty"`
	Error      string `json:"error,omitempty"`
}

type PanelForwardRuntimeReadiness

type PanelForwardRuntimeReadiness struct {
	Ready  bool   `json:"ready"`
	Reason string `json:"reason,omitempty"`
}

type PanelForwardRuntimeService

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

func NewPanelForwardRuntimeService

func NewPanelForwardRuntimeService(db *gorm.DB) *PanelForwardRuntimeService

func (*PanelForwardRuntimeService) Apply

func (s *PanelForwardRuntimeService) Apply(ctx context.Context, action string, forward *model.Forward, tunnel *model.ForwardTunnel) (*panelForwardRuntimeResult, error)

func (*PanelForwardRuntimeService) DiagnoseLocalOperator

func (*PanelForwardRuntimeService) DiagnoseNodeXOperator

func (*PanelForwardRuntimeService) DiagnosePanelRuntime

func (*PanelForwardRuntimeService) GetLocalOperatorStatus

func (*PanelForwardRuntimeService) GetNodeXOperatorStatus

func (*PanelForwardRuntimeService) GetNodeXRuntimeStatus

func (s *PanelForwardRuntimeService) GetNodeXRuntimeStatus(ctx context.Context) (*nodeXForwardRuntimeStatus, error)

func (*PanelForwardRuntimeService) GetPanelRuntimeStatus

func (*PanelForwardRuntimeService) MarkForwardsPendingForBackend

func (s *PanelForwardRuntimeService) MarkForwardsPendingForBackend(targetBackend string) (int64, error)

MarkForwardsPendingForBackend resets forwards that need re-sync to pending status. Call this after a backend switch to mark mismatched forwards for later re-sync.

func (*PanelForwardRuntimeService) SyncForwardsToBackend

func (s *PanelForwardRuntimeService) SyncForwardsToBackend(targetBackend string) (synced int, failed int, err error)

SyncForwardsToBackend re-synces all active forwards to the current backend. Used when the admin switches between NodeX and Local mode.

type PanelForwardRuntimeStatusSnapshot

type PanelForwardRuntimeStatusSnapshot struct {
	OK           bool                             `json:"ok"`
	StatusCode   int                              `json:"statusCode,omitempty"`
	Error        string                           `json:"error,omitempty"`
	Version      string                           `json:"version,omitempty"`
	AuthRequired *bool                            `json:"authRequired,omitempty"`
	ExecutePath  string                           `json:"executePath,omitempty"`
	StatusPath   string                           `json:"statusPath,omitempty"`
	Supports     *nodeXForwardRuntimeSupportState `json:"supports,omitempty"`
	Modes        *nodeXForwardRuntimeModes        `json:"modes,omitempty"`
	Issues       []string                         `json:"issues,omitempty"`
}

type PanelForwardRuntimeStatusSummary

type PanelForwardRuntimeStatusSummary struct {
	BaseURL       string                             `json:"baseUrl,omitempty"`
	CheckedAt     string                             `json:"checkedAt"`
	Config        PanelForwardRuntimeConfigState     `json:"config"`
	Attachment    PanelForwardRuntimeAttachmentState `json:"attachment"`
	Reachability  PanelForwardRuntimeReadiness       `json:"reachability"`
	RuntimeReady  PanelForwardRuntimeReadiness       `json:"runtimeReady"`
	Summary       string                             `json:"summary,omitempty"`
	Warnings      []string                           `json:"warnings,omitempty"`
	Health        PanelForwardRuntimeProbe           `json:"health"`
	RuntimeStatus PanelForwardRuntimeStatusSnapshot  `json:"runtimeStatus"`
	LocalAnsible  *nodeXForwardRuntimeAnsibleStatus  `json:"localAnsible,omitempty"`
}

type PanelForwardService

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

func NewPanelForwardService

func NewPanelForwardService(db *gorm.DB) *PanelForwardService

func (*PanelForwardService) ApplyForwardTrafficSnapshots

func (s *PanelForwardService) ApplyForwardTrafficSnapshots(records []PanelForwardTrafficSnapshot) error

func (*PanelForwardService) AssignUserTunnel

func (s *PanelForwardService) AssignUserTunnel(input PanelUserTunnelInput) error

func (*PanelForwardService) CreateForward

func (s *PanelForwardService) CreateForward(userID uint, isAdmin bool, input PanelForwardInput) (*PanelForwardListItem, error)

func (*PanelForwardService) CreateTunnel

func (*PanelForwardService) DeleteForward

func (s *PanelForwardService) DeleteForward(userID uint, isAdmin bool, forwardID uint, force bool) error

func (*PanelForwardService) DeleteTunnel

func (s *PanelForwardService) DeleteTunnel(id uint) error

func (*PanelForwardService) DiagnoseForward

func (s *PanelForwardService) DiagnoseForward(userID uint, isAdmin bool, forwardID uint) (*DiagnosisReport, error)

func (*PanelForwardService) DiagnoseLocalOperator

func (s *PanelForwardService) DiagnoseLocalOperator(ctx context.Context) (*PanelForwardRuntimeDoctorSummary, error)

func (*PanelForwardService) DiagnoseNodeXOperator

func (s *PanelForwardService) DiagnoseNodeXOperator(ctx context.Context) (*PanelForwardRuntimeDoctorSummary, error)

func (*PanelForwardService) DiagnoseRuntime

func (*PanelForwardService) DiagnoseTunnel

func (s *PanelForwardService) DiagnoseTunnel(id uint) (*TunnelDiagnosisReport, error)

func (*PanelForwardService) GetLocalOperatorStatus

func (s *PanelForwardService) GetLocalOperatorStatus(ctx context.Context) (*PanelForwardRuntimeStatusSummary, error)

func (*PanelForwardService) GetNodeXOperatorStatus

func (s *PanelForwardService) GetNodeXOperatorStatus(ctx context.Context) (*PanelForwardRuntimeStatusSummary, error)

func (*PanelForwardService) GetRuntimeStatus

func (*PanelForwardService) ListAdminTunnels

func (s *PanelForwardService) ListAdminTunnels() ([]PanelAdminTunnelItem, error)

func (*PanelForwardService) ListForwards

func (s *PanelForwardService) ListForwards(userID uint, isAdmin bool) ([]PanelForwardListItem, error)

func (*PanelForwardService) ListRuntimeJobs

func (s *PanelForwardService) ListRuntimeJobs(filter PanelRuntimeJobFilter) ([]model.ForwardRuntimeJob, error)

func (*PanelForwardService) ListTunnels

func (s *PanelForwardService) ListTunnels(userID uint, isAdmin bool) ([]PanelTunnelListItem, error)

func (*PanelForwardService) ListUserTunnels

func (*PanelForwardService) RecordForwardTraffic

func (s *PanelForwardService) RecordForwardTraffic(records []PanelForwardTrafficRecord) error

func (*PanelForwardService) RemoveUserTunnel

func (s *PanelForwardService) RemoveUserTunnel(id uint) error

func (*PanelForwardService) ResetUserTunnelTraffic

func (s *PanelForwardService) ResetUserTunnelTraffic(id uint) error

func (*PanelForwardService) RuntimeService

func (s *PanelForwardService) RuntimeService() *PanelForwardRuntimeService

func (*PanelForwardService) SetForwardStatus

func (s *PanelForwardService) SetForwardStatus(userID uint, isAdmin bool, forwardID uint, status int) error

func (*PanelForwardService) UpdateForward

func (s *PanelForwardService) UpdateForward(userID uint, isAdmin bool, input PanelForwardUpdateInput) (*PanelForwardListItem, error)

func (*PanelForwardService) UpdateOrder

func (s *PanelForwardService) UpdateOrder(userID uint, isAdmin bool, updates []PanelForwardOrderUpdate) error

func (*PanelForwardService) UpdateTunnel

func (*PanelForwardService) UpdateUserTunnel

func (s *PanelForwardService) UpdateUserTunnel(input PanelUserTunnelUpdateInput) error

func (*PanelForwardService) UploadFluxForwardFlow

func (s *PanelForwardService) UploadFluxForwardFlow(data PanelForwardFlowData) error

type PanelForwardTrafficRecord

type PanelForwardTrafficRecord struct {
	ForwardID uint  `json:"forwardId"`
	Upload    int64 `json:"upload"`
	Download  int64 `json:"download"`
}

type PanelForwardTrafficSnapshot

type PanelForwardTrafficSnapshot struct {
	ForwardID     uint   `json:"forwardId"`
	Backend       string `json:"backend"`
	UploadTotal   int64  `json:"uploadTotal"`
	DownloadTotal int64  `json:"downloadTotal"`
}

type PanelForwardUpdateInput

type PanelForwardUpdateInput struct {
	ID            uint   `json:"id"`
	UserID        uint   `json:"userId"`
	Name          string `json:"name"`
	TunnelID      uint   `json:"tunnelId"`
	InPort        *int   `json:"inPort"`
	RemoteAddr    string `json:"remoteAddr"`
	InterfaceName string `json:"interfaceName"`
	Strategy      string `json:"strategy"`
}

type PanelRuntimeJobFilter

type PanelRuntimeJobFilter struct {
	Backend   string
	Status    *int
	ForwardID *uint
	Limit     int
}

type PanelTunnelInput

type PanelTunnelInput struct {
	Name          string   `json:"name"`
	InNodeID      uint     `json:"inNodeId"`
	OutNodeID     *uint    `json:"outNodeId"`
	Type          int      `json:"type"`
	Flow          int      `json:"flow"`
	TrafficRatio  *float64 `json:"trafficRatio"`
	InterfaceName string   `json:"interfaceName"`
	Protocol      string   `json:"protocol"`
	TCPListenAddr string   `json:"tcpListenAddr"`
	UDPListenAddr string   `json:"udpListenAddr"`
}

type PanelTunnelListItem

type PanelTunnelListItem struct {
	ID            uint   `json:"id"`
	Name          string `json:"name"`
	IP            string `json:"ip"`
	InIP          string `json:"inIp"`
	InNodePortSta *int   `json:"inNodePortSta"`
	InNodePortEnd *int   `json:"inNodePortEnd"`
	Type          int    `json:"type"`
	Protocol      string `json:"protocol"`
	Status        int    `json:"status"`
}

type PanelTunnelUpdateInput

type PanelTunnelUpdateInput struct {
	ID            uint     `json:"id"`
	Name          string   `json:"name"`
	Flow          int      `json:"flow"`
	TrafficRatio  *float64 `json:"trafficRatio"`
	InterfaceName string   `json:"interfaceName"`
	Protocol      string   `json:"protocol"`
	TCPListenAddr string   `json:"tcpListenAddr"`
	UDPListenAddr string   `json:"udpListenAddr"`
}

type PanelUserTunnelDetailItem

type PanelUserTunnelDetailItem struct {
	ID             uint   `json:"id"`
	UserID         uint   `json:"userId"`
	TunnelID       uint   `json:"tunnelId"`
	Flow           int64  `json:"flow"`
	Num            int    `json:"num"`
	FlowResetTime  int64  `json:"flowResetTime"`
	ExpTime        int64  `json:"expTime"`
	SpeedID        *uint  `json:"speedId"`
	SpeedLimitName string `json:"speedLimitName"`
	Speed          int64  `json:"speed"`
	TunnelName     string `json:"tunnelName"`
	TunnelFlow     int    `json:"tunnelFlow"`
	InFlow         int64  `json:"inFlow"`
	OutFlow        int64  `json:"outFlow"`
	Status         int    `json:"status"`
}

type PanelUserTunnelInput

type PanelUserTunnelInput struct {
	UserID        uint  `json:"userId"`
	TunnelID      uint  `json:"tunnelId"`
	Flow          int64 `json:"flow"`
	Num           int   `json:"num"`
	FlowResetTime int64 `json:"flowResetTime"`
	ExpTime       int64 `json:"expTime"`
	SpeedID       *uint `json:"speedId"`
}

type PanelUserTunnelQueryInput

type PanelUserTunnelQueryInput struct {
	UserID uint `json:"userId"`
}

type PanelUserTunnelUpdateInput

type PanelUserTunnelUpdateInput struct {
	ID            uint  `json:"id"`
	Flow          int64 `json:"flow"`
	Num           int   `json:"num"`
	FlowResetTime int64 `json:"flowResetTime"`
	ExpTime       int64 `json:"expTime"`
	Status        int   `json:"status"`
	SpeedID       *uint `json:"speedId"`
}

type PaymentGatewayService

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

PaymentGatewayService 支付网关服务

func NewPaymentGatewayService

func NewPaymentGatewayService(db *gorm.DB) *PaymentGatewayService

NewPaymentGatewayService 创建服务

func (*PaymentGatewayService) CalculateFee

func (s *PaymentGatewayService) CalculateFee(gateway *model.PaymentGateway, amount float64) float64

CalculateFee 计算手续费

func (*PaymentGatewayService) Create

func (s *PaymentGatewayService) Create(gateway *model.PaymentGateway) error

Create 创建网关

func (*PaymentGatewayService) CreateRecord

func (s *PaymentGatewayService) CreateRecord(record *model.PaymentRecord) error

CreateRecord 创建支付记录

func (*PaymentGatewayService) Delete

func (s *PaymentGatewayService) Delete(id uint) error

Delete 删除网关

func (*PaymentGatewayService) GenerateTradeNo

func (s *PaymentGatewayService) GenerateTradeNo() string

GenerateTradeNo 生成商户订单号

func (*PaymentGatewayService) GetByID

GetByID 根据ID获取网关

func (*PaymentGatewayService) GetByType

func (s *PaymentGatewayService) GetByType(gatewayType string) (*model.PaymentGateway, error)

GetByType 根据类型获取网关

func (*PaymentGatewayService) GetChannels

func (s *PaymentGatewayService) GetChannels() ([]*model.PaymentChannel, error)

GetChannels 获取支付渠道 (前端显示用)

func (*PaymentGatewayService) GetEnabled

func (s *PaymentGatewayService) GetEnabled() ([]*model.PaymentGateway, error)

GetEnabled 获取启用的网关

func (*PaymentGatewayService) GetRecordByGatewayTradeNo

func (s *PaymentGatewayService) GetRecordByGatewayTradeNo(gatewayTradeNo string) (*model.PaymentRecord, error)

GetRecordByGatewayTradeNo 根据第三方订单号获取记录

func (*PaymentGatewayService) GetRecordByTradeNo

func (s *PaymentGatewayService) GetRecordByTradeNo(tradeNo string) (*model.PaymentRecord, error)

GetRecordByTradeNo 根据订单号获取记录

func (*PaymentGatewayService) GetStats

func (s *PaymentGatewayService) GetStats(start, end time.Time) (*PaymentStats, error)

GetStats 获取支付统计

func (*PaymentGatewayService) GetUserRecords

func (s *PaymentGatewayService) GetUserRecords(userID uint, page, pageSize int) ([]*model.PaymentRecord, int64, error)

GetUserRecords 获取用户支付记录

func (*PaymentGatewayService) List

List 获取网关列表

func (*PaymentGatewayService) ListRecords

func (s *PaymentGatewayService) ListRecords(page, pageSize int, status *int, gatewayType string) ([]*model.PaymentRecord, int64, error)

ListRecords 获取支付记录列表

func (*PaymentGatewayService) MarkAsPaid

func (s *PaymentGatewayService) MarkAsPaid(tradeNo string, gatewayTradeNo string, notifyData string) error

MarkAsPaid 标记为已支付

func (*PaymentGatewayService) MarkOrderPaid

func (s *PaymentGatewayService) MarkOrderPaid(tradeNo string, gatewayTradeNo string, notifyData string) error

MarkOrderPaid 标记支付并更新订单状态 (事务)

func (*PaymentGatewayService) MarkOrderPaidWithAmount

func (s *PaymentGatewayService) MarkOrderPaidWithAmount(tradeNo string, gatewayTradeNo string, notifyData string, paidAmount *float64) error

MarkOrderPaidWithAmount 标记支付并核对回调金额。

func (*PaymentGatewayService) ParseConfig

func (s *PaymentGatewayService) ParseConfig(gateway *model.PaymentGateway) (any, error)

ParseConfig 解析网关配置

func (*PaymentGatewayService) Toggle

func (s *PaymentGatewayService) Toggle(id uint, enabled bool) error

Toggle 切换网关状态

func (*PaymentGatewayService) Update

func (s *PaymentGatewayService) Update(gateway *model.PaymentGateway) error

Update 更新网关

func (*PaymentGatewayService) UpdateRecordStatus

func (s *PaymentGatewayService) UpdateRecordStatus(tradeNo string, status int, gatewayTradeNo string) error

UpdateRecordStatus 更新支付状态

func (*PaymentGatewayService) UpdateStats

func (s *PaymentGatewayService) UpdateStats(gatewayID uint, amount float64) error

UpdateStats 更新统计

func (*PaymentGatewayService) ValidateAmount

func (s *PaymentGatewayService) ValidateAmount(gateway *model.PaymentGateway, amount float64) error

ValidateAmount 验证金额范围

func (*PaymentGatewayService) ValidateGatewayUsable

func (s *PaymentGatewayService) ValidateGatewayUsable(gateway *model.PaymentGateway) error

ValidateGatewayUsable verifies that a gateway can be shown to users and used to create new payment records.

type PaymentStats

type PaymentStats struct {
	TotalAmount   float64 `json:"total_amount"`
	TotalCount    int64   `json:"total_count"`
	PendingAmount float64 `json:"pending_amount"`
	PendingCount  int64   `json:"pending_count"`
}

PaymentStats 支付统计

type PlanService

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

PlanService 管理套餐

func NewPlanService

func NewPlanService() *PlanService

NewPlanService 创建 PlanService

func (*PlanService) AssignToUser

func (s *PlanService) AssignToUser(planID, userID uint, expireAt *int64) error

AssignToUser 将套餐分配给用户(同步修改用户记录),并写事件

func (*PlanService) Create

func (s *PlanService) Create(p *model.Plan) error

Create 创建套餐并写入事件

func (*PlanService) Delete

func (s *PlanService) Delete(id uint) error

Delete 删除套餐并写事件

func (*PlanService) Get

func (s *PlanService) Get(id uint) (*model.Plan, error)

Get 获取单个套餐

func (*PlanService) List

func (s *PlanService) List() ([]*model.Plan, error)

List 获取所有套餐

func (*PlanService) Update

func (s *PlanService) Update(p *model.Plan) error

Update 更新套餐并写事件

type PluginCompatibilityRoutes

type PluginCompatibilityRoutes struct {
	Path   string `json:"path"`
	SHA256 string `json:"sha256"`
}

type PluginConfigurationSemanticValidator

type PluginConfigurationSemanticValidator func(pluginID, version string, canonicalConfig json.RawMessage) error

PluginConfigurationSemanticValidator supplies optional, version-bound package semantics that cannot be expressed safely in JSON Schema.

type PluginConfigurationTxHook

type PluginConfigurationTxHook func(tx *gorm.DB, installation model.PluginInstallation, configuration model.PluginConfiguration) error

type PluginControlRouteResolution

type PluginControlRouteResolution struct {
	PluginID       string `json:"plugin_id"`
	Version        string `json:"version"`
	InstallationID uint   `json:"installation_id"`
	Generation     uint64 `json:"generation"`
	Route          string `json:"route"`
	MatchedRoute   string `json:"matched_route"`
	Permission     string `json:"permission"`
}

func ResolvePluginControlRoute

func ResolvePluginControlRoute(db *gorm.DB, publicKey ed25519.PublicKey, pluginID, requestPath string, actorID uint, legacyAdmin bool) (*PluginControlRouteResolution, error)

type PluginDependencyExecutionPlan

type PluginDependencyExecutionPlan struct {
	Target string                          `json:"target"`
	RootID string                          `json:"root_id"`
	Order  []string                        `json:"order"`
	Steps  []PluginDependencyExecutionStep `json:"steps"`
}

PluginDependencyExecutionPlan is the dependency-first closure used by the Control lifecycle worker. Unlike the compatibility resolver, it can select the latest registered target-compatible release for a missing dependency so an administrator can enable one root package without first hand-installing every package in its closure.

An existing installation always pins its desired version. That makes an upgrade deterministic and prevents a newly published dependency release from silently changing an already configured package.

func ResolvePluginDependencyExecutionPlan

func ResolvePluginDependencyExecutionPlan(db *gorm.DB, rootRelease model.PluginRelease, target string) (*PluginDependencyExecutionPlan, error)

ResolvePluginDependencyExecutionPlan resolves a root release and all of its transitive dependencies into a stable dependency-first execution plan. It validates the selected stored manifests, including registered trust roots, but does not mutate installation intent.

type PluginDependencyExecutionStep

type PluginDependencyExecutionStep struct {
	PluginID     string                    `json:"plugin_id"`
	Release      model.PluginRelease       `json:"release"`
	Manifest     PluginManifest            `json:"manifest"`
	Installation *model.PluginInstallation `json:"installation,omitempty"`
}

PluginDependencyExecutionStep binds one graph vertex to the immutable release that an execution plan will use. Installation is nil when the package has not been installed on the target yet.

type PluginDependencyResolution

type PluginDependencyResolution struct {
	Roots   []string         `json:"roots"`
	Order   []string         `json:"order"`
	Ordered []PluginManifest `json:"ordered"`
}

PluginDependencyResolution is the deterministic execution plan for a set of selected plugin releases. Ordered and Order contain the same nodes; the former is convenient for lifecycle execution and the latter is convenient for persistence, logging, and test assertions.

Dependencies always precede their dependents. Nodes that are otherwise unrelated are ordered by plugin ID, making the result independent of map, input-slice, and manifest-declaration order.

func ResolvePluginDependencies

func ResolvePluginDependencies(rootIDs []string, manifests []PluginManifest) (*PluginDependencyResolution, error)

ResolvePluginDependencies is an argument-order convenience wrapper for callers that naturally have roots before loading the selected manifests.

func ResolvePluginDependencyGraph

func ResolvePluginDependencyGraph(manifests []PluginManifest, rootIDs []string) (*PluginDependencyResolution, error)

ResolvePluginDependencyGraph resolves root plugin IDs from a selected manifest set. The returned order is a stable dependency-first topological order. A shared dependency is emitted once, even when multiple roots depend on it.

func ResolvePluginDependencyGraphFromDB

func ResolvePluginDependencyGraphFromDB(db *gorm.DB, rootRelease model.PluginRelease, target string) (*PluginDependencyResolution, error)

ResolvePluginDependencyGraphFromDB is a descriptive alias for callers that do not have an installation object yet and are resolving from a release.

func ResolvePluginInstallationDependencyGraph

func ResolvePluginInstallationDependencyGraph(db *gorm.DB, rootRelease model.PluginRelease, target string) (*PluginDependencyResolution, error)

ResolvePluginInstallationDependencyGraph loads the selected desired release for every dependency from the Control database, verifies the stored release metadata, and then delegates graph validation/order to ResolvePluginDependencyGraph. A dependency installation may be disabled: this function is a preflight graph check, not an enable operation. The existing ValidatePluginInstallationPlan check still enforces that every dependency is enabled before production intent is accepted.

Registered releases carry a trust-root fingerprint and are re-verified from the stored trust-root row. Rows created by the pre-signature 3.x migration without a fingerprint retain the existing compatibility behavior and are checked through DecodePluginReleaseManifest.

type PluginDependencyResolver

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

PluginDependencyResolver indexes one selected release per plugin ID. A release catalog may contain many versions, but callers must select the version to execute before constructing this resolver.

func NewPluginDependencyResolver

func NewPluginDependencyResolver(manifests []PluginManifest) (*PluginDependencyResolver, error)

NewPluginDependencyResolver validates and indexes the selected manifests. It intentionally performs graph relationship validation here, while full signature/artifact validation remains the responsibility of RegisterPluginRelease/VerifyStoredPluginRelease.

func (*PluginDependencyResolver) Resolve

Resolve computes the dependency closure and a deterministic topological order for rootIDs.

type PluginEntrypoint

type PluginEntrypoint struct {
	Path   string `json:"path"`
	SHA256 string `json:"sha256"`
}

type PluginInstallationStatus

type PluginInstallationStatus struct {
	ID                  uint       `json:"id"`
	PluginID            string     `json:"plugin_id"`
	Target              string     `json:"target"`
	DesiredVersion      string     `json:"desired_version"`
	ObservedVersion     string     `json:"observed_version"`
	PreviousVersion     string     `json:"previous_version"`
	State               string     `json:"state"`
	Enabled             bool       `json:"enabled"`
	LifecycleGeneration int64      `json:"lifecycle_generation"`
	ConfigRevision      int64      `json:"config_revision"`
	HasError            bool       `json:"has_error"`
	LastError           string     `json:"last_error,omitempty"`
	CreatedAt           time.Time  `json:"created_at"`
	UpdatedAt           time.Time  `json:"updated_at"`
	DisabledAt          *time.Time `json:"disabled_at,omitempty"`
}

PluginInstallationStatus is the public projection of a plugin installation. Installation LastError can originate in a Control executor or from an Agent lifecycle result, so it is reduced to a stable state summary before it crosses an HTTP boundary.

func PublicPluginInstallation

func PublicPluginInstallation(installation model.PluginInstallation) PluginInstallationStatus

PublicPluginInstallation keeps package lifecycle metadata useful to the WebUI while withholding raw executor and Agent diagnostics.

type PluginManifest

type PluginManifest struct {
	ID                  string                     `json:"id"`
	Name                string                     `json:"name"`
	Version             string                     `json:"version"`
	APIVersion          string                     `json:"api_version"`
	Publisher           string                     `json:"publisher"`
	Targets             []string                   `json:"targets"`
	Architectures       []string                   `json:"architectures"`
	ArtifactSHA256      string                     `json:"artifact_sha256"`
	Capabilities        []string                   `json:"capabilities"`
	Dependencies        []string                   `json:"dependencies"`
	Conflicts           []string                   `json:"conflicts"`
	Permissions         []string                   `json:"permissions"`
	ConfigSchema        json.RawMessage            `json:"config_schema"`
	SecretFields        []string                   `json:"secret_fields"`
	Entrypoints         map[string]string          `json:"entrypoints"`
	Migration           int64                      `json:"migration_version"`
	ControlRoutes       []string                   `json:"control_routes"`
	FrontendSHA256      string                     `json:"frontend_sha256"`
	WebUI               *PluginWebUI               `json:"webui,omitempty"`
	ControlEntrypoint   *PluginEntrypoint          `json:"control_entrypoint,omitempty"`
	AgentEntrypoint     *PluginEntrypoint          `json:"agent_entrypoint,omitempty"`
	Migrations          *PluginMigrations          `json:"migrations,omitempty"`
	CompatibilityRoutes *PluginCompatibilityRoutes `json:"compatibility_routes,omitempty"`
	RouteContractDigest string                     `json:"route_contract_digest,omitempty"`
	RuntimeAPIVersion   string                     `json:"runtime_api_version,omitempty"`
}

func DecodePluginReleaseManifest

func DecodePluginReleaseManifest(release model.PluginRelease) (*PluginManifest, error)

ValidatePluginReleaseTarget verifies the internally persisted release metadata before it is used to create desired state. Releases are immutable, so a mismatch here indicates corrupt or manually modified kernel data.

func VerifyPluginRelease

func VerifyPluginRelease(manifestJSON, signature string, publicKey ed25519.PublicKey) (*PluginManifest, error)

func VerifyStoredPluginRelease

func VerifyStoredPluginRelease(db *gorm.DB, release model.PluginRelease, fallback ed25519.PublicKey) (*PluginManifest, error)

func (PluginManifest) Validate

func (m PluginManifest) Validate() error

type PluginMigrations

type PluginMigrations struct {
	Index  string `json:"index"`
	SHA256 string `json:"sha256"`
}

type PluginWebUI

type PluginWebUI struct {
	Bundle      PluginWebUIBundle  `json:"bundle"`
	Permissions []string           `json:"permissions"`
	Menus       []PluginWebUIMenu  `json:"menus"`
	Routes      []PluginWebUIRoute `json:"routes"`
}

PluginWebUI describes a signed, declarative administrator extension. Bundle paths are relative to the verified plugin artifact; they are not URLs and are never fetched directly by the control kernel.

type PluginWebUIBundle

type PluginWebUIBundle struct {
	Path   string `json:"path"`
	SHA256 string `json:"sha256"`
}

type PluginWebUIMenu

type PluginWebUIMenu struct {
	ID         string `json:"id"`
	Parent     string `json:"parent"`
	Label      string `json:"label"`
	Icon       string `json:"icon"`
	Route      string `json:"route"`
	Permission string `json:"permission"`
	Order      int    `json:"order"`
}

type PluginWebUIRoute

type PluginWebUIRoute struct {
	ID         string `json:"id"`
	Path       string `json:"path"`
	Export     string `json:"export"`
	Permission string `json:"permission"`
}

type RegistrationPolicy

type RegistrationPolicy struct {
	Enabled             bool
	RequireInvite       bool
	AllowedEmailDomains []string
	BlockedEmailDomains []string
}

func ResolveRegistrationPolicy

func ResolveRegistrationPolicy(cfg *config.Config) RegistrationPolicy

type ServerService

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

ServerService 服务器服务

func NewServerService

func NewServerService() *ServerService

NewServerService 创建服务器服务

func (*ServerService) BatchRecordTrafficLog

func (s *ServerService) BatchRecordTrafficLog(serverType model.ServerType, serverID uint, traffics map[uint][2]int64, rate float64) error

BatchRecordTrafficLog 批量记录流量日志

func (*ServerService) BuildNodeConfig

func (s *ServerService) BuildNodeConfig(serverType model.ServerType, serverID uint) (map[string]any, error)

BuildNodeConfig 构建节点配置

func (*ServerService) GetAllUsersOnlineCount

func (s *ServerService) GetAllUsersOnlineCount() (map[string]int, error)

GetAllUsersOnlineCount 获取所有用户的在线IP数量

func (*ServerService) GetRoutesByIDs

func (s *ServerService) GetRoutesByIDs(ids []uint) ([]model.ServerRoute, error)

GetRoutesByIDs 根据ID列表获取路由

func (*ServerService) GetServerByTypeAndID

func (s *ServerService) GetServerByTypeAndID(serverType model.ServerType, serverID uint) (any, error)

GetServerByTypeAndID 根据类型和ID获取服务器

func (*ServerService) GetServerRate

func (s *ServerService) GetServerRate(serverType model.ServerType, serverID uint) float64

GetServerRate 获取服务器流量倍率

func (*ServerService) GetServerUsers

func (s *ServerService) GetServerUsers(serverType model.ServerType, serverID uint) ([]model.User, error)

GetServerUsers 获取服务器可用用户列表

func (*ServerService) GetUserOnlineCount

func (s *ServerService) GetUserOnlineCount(userID uint) (int64, error)

GetUserOnlineCount 获取用户在线IP数量(跨所有节点)

func (*ServerService) RecordNodeTrafficReport

func (s *ServerService) RecordNodeTrafficReport(serverType model.ServerType, nodeID uint, traffics map[uint][2]int64, rate float64) error

RecordNodeTrafficReport atomically applies a node traffic report. Node clients retry a report when the panel returns an error, so logs, node counters, daily/monthly stats, and user counters must commit together.

func (*ServerService) RecordServerStat

func (s *ServerService) RecordServerStat(serverType model.ServerType, serverID uint, upload, download int64, recordType string, recordAt int64) error

RecordServerStat 记录节点汇总统计,用于面板中的服务器统计表。 recordType: d=日统计, m=月统计。

func (*ServerService) RecordTrafficLog

func (s *ServerService) RecordTrafficLog(serverType model.ServerType, serverID uint, userID uint, upload, download int64, rate float64) error

RecordTrafficLog 记录流量日志

func (*ServerService) UpdateOnlineStatus

func (s *ServerService) UpdateOnlineStatus(serverType model.ServerType, serverID uint, userIPs map[uint][]string) error

UpdateOnlineStatus 更新用户在线状态

type SpeedLimitInput

type SpeedLimitInput struct {
	Name       string `json:"name"`
	Speed      int64  `json:"speed"`
	TunnelID   uint   `json:"tunnelId"`
	TunnelName string `json:"tunnelName"`
}

type SpeedLimitService

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

func NewSpeedLimitService

func NewSpeedLimitService(db *gorm.DB) *SpeedLimitService

func (*SpeedLimitService) Create

func (*SpeedLimitService) Delete

func (s *SpeedLimitService) Delete(id uint) error

func (*SpeedLimitService) GetByIDs

func (s *SpeedLimitService) GetByIDs(ids []uint) (map[uint]model.SpeedLimit, error)

func (*SpeedLimitService) List

func (s *SpeedLimitService) List() ([]model.SpeedLimit, error)

func (*SpeedLimitService) Update

type SpeedLimitUpdateInput

type SpeedLimitUpdateInput struct {
	ID uint `json:"id"`
	SpeedLimitInput
}

type StatsService

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

StatsService 统计服务 (带缓存)

func NewStatsService

func NewStatsService() *StatsService

NewStatsService 创建统计服务

func (*StatsService) GetDashboardStats

func (s *StatsService) GetDashboardStats(forceRefresh bool) (*DashboardStats, error)

GetDashboardStats 获取仪表盘统计 (优先从缓存读取)

func (*StatsService) GetHourlyTraffic

func (s *StatsService) GetHourlyTraffic(hours int, userID uint) ([]HourlyTraffic, error)

GetHourlyTraffic 返回最近 hours 个整点小时的流量序列 (含当前未结束的小时), 数据源为 v2_server_log, 按 (log_at/3600) 分桶聚合 SUM((u+d)*rate)。 userID > 0 时只统计该用户的流量; userID == 0 时统计全局。 缺失的小时补零, 结果按时间升序返回, 便于前端直接绘制折线图。

func (*StatsService) GetTrafficLogMeta

func (s *StatsService) GetTrafficLogMeta(userID uint) (TrafficLogMeta, error)

GetTrafficLogMeta returns lightweight diagnostics for traffic chart pages.

func (*StatsService) GetUserSubscription

func (s *StatsService) GetUserSubscription(userID uint, forceRefresh bool) (*UserSubscription, error)

GetUserSubscription 获取用户订阅详情 (优先从缓存读取)

func (*StatsService) GetUserTrafficRanking

func (s *StatsService) GetUserTrafficRanking(hours int, limit int, includeZeroUsers bool) ([]UserTrafficRank, error)

GetUserTrafficRanking 返回最近 hours 小时内按用户聚合的流量排行 (倒序), 数据源为 v2_server_log, 关联 v2_user 取 email。limit 限制返回条数。 includeZeroUsers 为 true 时从用户表出发返回无区间流量的用户, 便于后台按任意用户筛选小时图。

func (*StatsService) InvalidateUserCache

func (s *StatsService) InvalidateUserCache(userID uint)

InvalidateUserCache 使用户缓存失效 (用户数据变更时调用)

func (*StatsService) InvalidateUserCacheWithError

func (s *StatsService) InvalidateUserCacheWithError(userID uint) error

InvalidateUserCacheWithError 使用户缓存失效并返回底层缓存错误

func (*StatsService) RefreshDashboardCache

func (s *StatsService) RefreshDashboardCache() error

RefreshDashboardCache 强制刷新仪表盘缓存 (后台任务用)

type SubscriptionService

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

SubscriptionService 订阅服务

func NewSubscriptionService

func NewSubscriptionService() *SubscriptionService

NewSubscriptionService 创建订阅服务

func (*SubscriptionService) AssignGroupToPlan

func (s *SubscriptionService) AssignGroupToPlan(planID, groupID uint) error

AssignGroupToPlan 为套餐分配订阅分组

func (*SubscriptionService) AssignGroupToUser

func (s *SubscriptionService) AssignGroupToUser(userID, groupID uint, expireAt *int64, transferEnable *int64, nextRenewPrice *int64) error

AssignGroupToUser 为用户分配订阅分组

func (*SubscriptionService) BuildWireGuardRuntimeUserExtras

func (s *SubscriptionService) BuildWireGuardRuntimeUserExtras(protocol *model.NodeProtocol, users []*model.User) (map[uint]map[string]string, error)

func (*SubscriptionService) CreateGroup

func (s *SubscriptionService) CreateGroup(group *model.SubscriptionGroup) error

CreateGroup 创建订阅分组

func (*SubscriptionService) CreateTemplate

func (s *SubscriptionService) CreateTemplate(template *model.SubscriptionTemplate) error

CreateTemplate 创建订阅模板

func (*SubscriptionService) DeleteGroup

func (s *SubscriptionService) DeleteGroup(id uint) error

DeleteGroup 删除订阅分组

func (*SubscriptionService) DeleteTemplate

func (s *SubscriptionService) DeleteTemplate(id uint) error

DeleteTemplate 删除订阅模板

func (*SubscriptionService) GetGroup

GetGroup 获取订阅分组

func (*SubscriptionService) GetGroupStats

func (s *SubscriptionService) GetGroupStats() ([]GroupStats, error)

GetGroupStats 获取所有订阅分组的统计数据

func (*SubscriptionService) GetGroups

func (s *SubscriptionService) GetGroups() ([]*model.SubscriptionGroup, error)

GetGroups 获取所有订阅分组

func (*SubscriptionService) GetOrCreateWireGuardPeer

func (s *SubscriptionService) GetOrCreateWireGuardPeer(protocolID, userID uint, cidr string) (*model.WireGuardPeer, error)

func (*SubscriptionService) GetPlanGroups

func (s *SubscriptionService) GetPlanGroups(planID uint) ([]*model.SubscriptionGroup, error)

GetPlanGroups 获取套餐的订阅分组

func (*SubscriptionService) GetTemplate

func (s *SubscriptionService) GetTemplate(id uint) (*model.SubscriptionTemplate, error)

GetTemplate 获取订阅模板

func (*SubscriptionService) GetTemplatesByGroup

func (s *SubscriptionService) GetTemplatesByGroup(groupID uint) ([]*model.SubscriptionTemplate, error)

GetTemplatesByGroup 获取分组的模板

func (*SubscriptionService) GetUserGroups

func (s *SubscriptionService) GetUserGroups(userID uint) ([]*model.SubscriptionGroup, error)

GetUserGroups 获取用户的订阅分组

func (*SubscriptionService) GetUserSubscription

GetUserSubscription 获取用户订阅 token: 用户 token format: 订阅格式 groups: 指定分组 (可选) include: 包含关键词 exclude: 排除关键词

func (*SubscriptionService) RemoveGroupFromPlan

func (s *SubscriptionService) RemoveGroupFromPlan(planID, groupID uint) error

RemoveGroupFromPlan 移除套餐的订阅分组

func (*SubscriptionService) RemoveGroupFromUser

func (s *SubscriptionService) RemoveGroupFromUser(userID, groupID uint) error

RemoveGroupFromUser 移除用户的订阅分组

func (*SubscriptionService) UpdateGroup

func (s *SubscriptionService) UpdateGroup(group *model.SubscriptionGroup) error

UpdateGroup 更新订阅分组

func (*SubscriptionService) UpdateTemplate

func (s *SubscriptionService) UpdateTemplate(template *model.SubscriptionTemplate) error

UpdateTemplate 更新订阅模板

func (*SubscriptionService) UpdateTemplateFields

func (s *SubscriptionService) UpdateTemplateFields(id uint, fields map[string]any) error

UpdateTemplateFields 按字段局部更新订阅模板

type SubscriptionSettings

type SubscriptionSettings struct {
	SubscribePath    string   `json:"subscribe_path"`
	SubscribeDomains []string `json:"subscribe_domains"`
}

func GetSubscriptionSettings

func GetSubscriptionSettings(configService *SystemConfigService, cfg *config.Config) SubscriptionSettings

type SystemConfigService

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

func NewSystemConfigService

func NewSystemConfigService(db *gorm.DB) *SystemConfigService

func (*SystemConfigService) Delete

func (s *SystemConfigService) Delete(key string) error

func (*SystemConfigService) Get

func (s *SystemConfigService) Get(key string) (string, error)

func (*SystemConfigService) GetAll

func (s *SystemConfigService) GetAll() ([]model.SystemConfig, error)

func (*SystemConfigService) GetAsMap

func (s *SystemConfigService) GetAsMap() (map[string]string, error)

func (*SystemConfigService) GetByGroup

func (s *SystemConfigService) GetByGroup(group string) ([]model.SystemConfig, error)

func (*SystemConfigService) GetEntry

func (s *SystemConfigService) GetEntry(key string) (*model.SystemConfig, error)

func (*SystemConfigService) GetJSON

func (s *SystemConfigService) GetJSON(key string, v any) error

func (*SystemConfigService) Set

func (s *SystemConfigService) Set(key, value, cfgType, group, remark string) error

func (*SystemConfigService) SetJSON

func (s *SystemConfigService) SetJSON(key string, v any, group, remark string) error

type TOTPSetup

type TOTPSetup struct {
	Secret      string   `json:"secret"`       // 密钥 (base32)
	URL         string   `json:"url"`          // otpauth:// URL
	QRCode      string   `json:"qr_code"`      // 用于生成二维码
	BackupCodes []string `json:"backup_codes"` // 备用码
}

TOTPSetup TOTP设置结果

type TargetCatalogItem

type TargetCatalogItem struct {
	TargetKey      string  `json:"targetKey"`
	TargetType     string  `json:"targetType"`
	TargetID       uint    `json:"targetId"`
	Label          string  `json:"label"`
	Host           string  `json:"host"`
	Port           int     `json:"port"`
	LatestBucketAt int64   `json:"latestBucketAt"`
	LatestAvgRTT   float64 `json:"latestAvgRtt"`
	LatestLoss     float64 `json:"latestLoss"`
	Online         bool    `json:"online"`
}

type TaskStore

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

TaskStore provides a thread-safe in-memory task queue per node.

func NewTaskStore

func NewTaskStore() *TaskStore

NewTaskStore creates an empty TaskStore.

func (*TaskStore) CompleteTask

func (ts *TaskStore) CompleteTask(nodeID uint32, taskID uint64, result, errMsg string) bool

CompleteTask marks a running task as completed with the given result.

func (*TaskStore) DequeuePending

func (ts *TaskStore) DequeuePending(nodeID uint32, n int) []*AgentTask

DequeuePending returns up to n pending tasks for the given node and marks them as "running".

func (*TaskStore) Enqueue

func (ts *TaskStore) Enqueue(nodeID uint32, taskType, command, action string) *AgentTask

Enqueue adds a new pending task for the given node.

func (*TaskStore) FailTask

func (ts *TaskStore) FailTask(nodeID uint32, taskID uint64, errMsg string) bool

FailTask marks a task as failed.

func (*TaskStore) ListTasksByNode

func (ts *TaskStore) ListTasksByNode(nodeID uint32) []*AgentTask

ListTasksByNode returns all tasks (pending + completed) for a node.

type TelegramBotService

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

TelegramBotService Telegram Bot服务

func NewTelegramBotService

func NewTelegramBotService(db *gorm.DB) *TelegramBotService

NewTelegramBotService 创建服务

func (*TelegramBotService) BindUser

func (s *TelegramBotService) BindUser(telegramID int64, email string, from *TelegramUser) error

BindUser 绑定用户

func (*TelegramBotService) Broadcast

func (s *TelegramBotService) Broadcast(text string) error

Broadcast 广播消息

func (*TelegramBotService) DeleteWebhook

func (s *TelegramBotService) DeleteWebhook() error

DeleteWebhook 删除Webhook

func (*TelegramBotService) GetBot

func (s *TelegramBotService) GetBot() (*model.TelegramBot, error)

GetBot 获取Bot配置

func (*TelegramBotService) GetTelegramUserService

func (s *TelegramBotService) GetTelegramUserService() *TelegramUserService

GetTelegramUserService 获取Telegram用户服务

func (*TelegramBotService) HandleUpdate

func (s *TelegramBotService) HandleUpdate(update *TelegramUpdate) error

HandleUpdate 处理Webhook更新

func (*TelegramBotService) SendMessage

func (s *TelegramBotService) SendMessage(chatID int64, text string) error

SendMessage 发送消息

func (*TelegramBotService) SendNotification

func (s *TelegramBotService) SendNotification(telegramID int64, title, content string) error

SendNotification 发送通知

func (*TelegramBotService) SetWebhook

func (s *TelegramBotService) SetWebhook(webhookURL string) error

SetWebhook 设置Webhook

func (*TelegramBotService) UpdateBot

func (s *TelegramBotService) UpdateBot(bot *model.TelegramBot) error

UpdateBot 更新Bot配置

type TelegramCallback

type TelegramCallback struct {
	ID      string          `json:"id"`
	From    TelegramUser    `json:"from"`
	Message TelegramMessage `json:"message"`
	Data    string          `json:"data"`
}

TelegramCallback Telegram回调

type TelegramChat

type TelegramChat struct {
	ID       int64  `json:"id"`
	Type     string `json:"type"`
	Title    string `json:"title,omitempty"`
	Username string `json:"username,omitempty"`
}

TelegramChat Telegram聊天

type TelegramMessage

type TelegramMessage struct {
	MessageID int64         `json:"message_id"`
	From      *TelegramUser `json:"from,omitempty"`
	Chat      TelegramChat  `json:"chat"`
	Date      int64         `json:"date"`
	Text      string        `json:"text,omitempty"`
}

TelegramMessage Telegram消息

type TelegramUpdate

type TelegramUpdate struct {
	UpdateID      int64             `json:"update_id"`
	Message       *TelegramMessage  `json:"message,omitempty"`
	CallbackQuery *TelegramCallback `json:"callback_query,omitempty"`
}

TelegramUpdate Telegram更新

type TelegramUser

type TelegramUser struct {
	ID           int64  `json:"id"`
	IsBot        bool   `json:"is_bot"`
	FirstName    string `json:"first_name"`
	LastName     string `json:"last_name,omitempty"`
	Username     string `json:"username,omitempty"`
	LanguageCode string `json:"language_code,omitempty"`
}

TelegramUser Telegram用户

type TelegramUserService

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

TelegramUserService Telegram用户服务

func NewTelegramUserService

func NewTelegramUserService(db *gorm.DB) *TelegramUserService

NewTelegramUserService 创建服务

func (*TelegramUserService) Ban

func (s *TelegramUserService) Ban(telegramID int64) error

Ban 封禁用户

func (*TelegramUserService) GetByTelegramID

func (s *TelegramUserService) GetByTelegramID(telegramID int64) (*model.TelegramUser, error)

GetByTelegramID 根据TelegramID获取用户

func (*TelegramUserService) GetByUserID

func (s *TelegramUserService) GetByUserID(userID uint) (*model.TelegramUser, error)

GetByUserID 根据系统用户ID获取绑定

func (*TelegramUserService) Unban

func (s *TelegramUserService) Unban(telegramID int64) error

Unban 解封用户

func (*TelegramUserService) UpdateLastActive

func (s *TelegramUserService) UpdateLastActive(telegramID int64) error

UpdateLastActive 更新最后活跃时间

type Topology

type Topology struct {
	Nodes []TopologyNode `json:"nodes"`
	Edges []TopologyEdge `json:"edges"`
}

type TopologyDeploymentDiagnosticCheck

type TopologyDeploymentDiagnosticCheck struct {
	Name    string `json:"name"`
	Status  string `json:"status"`
	Message string `json:"message,omitempty"`
}

type TopologyDeploymentDiagnosticIssue

type TopologyDeploymentDiagnosticIssue struct {
	Code     string `json:"code"`
	Path     string `json:"path"`
	Message  string `json:"message"`
	Severity string `json:"severity"`
}

TopologyDeploymentDiagnosticIssue is stable, machine-readable preflight output. Code and path intentionally mirror ValidateTopology where possible so the editor can point at the same field that the planner would reject.

type TopologyDeploymentEvent

type TopologyDeploymentEvent struct {
	Type        string    `json:"type"`
	At          time.Time `json:"at"`
	OperationID string    `json:"operation_id,omitempty"`
	StepID      *uint     `json:"step_id,omitempty"`
	NodeID      *uint     `json:"node_id,omitempty"`
	PluginID    string    `json:"plugin_id,omitempty"`
	State       string    `json:"state"`
	HasError    bool      `json:"has_error"`
	// Message is a stable kernel-generated lifecycle summary. It is never a
	// persisted deployment, observed-state, or Agent error.
	Message string `json:"message,omitempty"`
}

func PublicTopologyDeploymentEvent

func PublicTopologyDeploymentEvent(event TopologyDeploymentEvent) TopologyDeploymentEvent

PublicTopologyDeploymentEvent never trusts a pre-existing Message field: callers only receive a summary regenerated from the event type and state.

type TopologyDeploymentExecutor

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

TopologyDeploymentExecutor turns a previously planned revision into durable Agent operations. It never opens a network/data-plane connection itself; the existing KernelOperationBridge remains the sole Agent transport.

A failed operation fences further expansion immediately, cancels in-flight sibling work, then compensates successful/started steps in reverse DAG order. The compensation restores the previous active revision where possible, or disables a newly introduced plugin vertex when no predecessor exists.

func NewTopologyDeploymentExecutor

func NewTopologyDeploymentExecutor(db *gorm.DB) (*TopologyDeploymentExecutor, error)

func (*TopologyDeploymentExecutor) RunOnce

func (e *TopologyDeploymentExecutor) RunOnce(ctx context.Context) (int, error)

RunOnce reconciles every active topology deployment. Operations are created in database transactions and carry stable idempotency keys, so repeating a pass after a Control restart is safe.

func (*TopologyDeploymentExecutor) Start

func (e *TopologyDeploymentExecutor) Start(ctx context.Context, interval time.Duration, reportError func(error))

Start runs reconciliation until ctx is cancelled. It is deliberately separate from plan/apply HTTP paths so topology execution remains opt-in.

type TopologyDeploymentOperation

type TopologyDeploymentOperation struct {
	OperationID   string     `json:"operation_id"`
	StepID        *uint      `json:"step_id,omitempty"`
	NodeID        *uint      `json:"node_id,omitempty"`
	PluginID      string     `json:"plugin_id"`
	TargetVersion string     `json:"target_version"`
	Kind          string     `json:"kind"`
	Revision      int64      `json:"revision"`
	State         string     `json:"state"`
	Attempt       int        `json:"attempt"`
	DeadlineAt    *time.Time `json:"deadline_at,omitempty"`
	DispatchedAt  *time.Time `json:"dispatched_at,omitempty"`
	ObservedAt    *time.Time `json:"observed_at,omitempty"`
	HasError      bool       `json:"has_error"`
	LastError     string     `json:"last_error,omitempty"`
	UpdatedAt     time.Time  `json:"updated_at"`
}

TopologyDeploymentOperation is the safe, linkage-focused projection used by deployment status. ConfigJSON, ResultJSON, and raw Agent errors are intentionally omitted. LastError is a stable kernel-generated summary, not the persisted operation error.

func PublicTopologyDeploymentOperation

func PublicTopologyDeploymentOperation(operation TopologyDeploymentOperation) TopologyDeploymentOperation

PublicTopologyDeploymentOperation defensively regenerates the error summary even though the timeline loader already does so. This keeps the HTTP view safe if an in-process caller constructs a TopologyDeploymentStatus manually.

type TopologyDeploymentPlanInput

type TopologyDeploymentPlanInput struct {
	TopologyID    uint
	RevisionID    uint
	ActorID       uint
	RolloutGroup  string
	FailurePolicy string
}

TopologyDeploymentPlanInput identifies an immutable topology revision to compile into a durable node-operation plan. The plan is intentionally inert: callers must request apply and the separately feature-gated executor must be running before any Agent operation is created.

type TopologyDeploymentPreview

type TopologyDeploymentPreview struct {
	TopologyID         uint                                `json:"topology_id"`
	RevisionID         uint                                `json:"revision_id"`
	PreviousRevisionID *uint                               `json:"previous_revision_id,omitempty"`
	RolloutGroup       string                              `json:"rollout_group,omitempty"`
	FailurePolicy      string                              `json:"failure_policy"`
	Valid              bool                                `json:"valid"`
	Issues             []TopologyDeploymentDiagnosticIssue `json:"issues"`
	Checks             []TopologyDeploymentDiagnosticCheck `json:"checks"`
	Steps              []TopologyDeploymentPreviewStep     `json:"steps"`
}

func PreviewTopologyDeployment

func PreviewTopologyDeployment(db *gorm.DB, input TopologyDeploymentPreviewInput) (*TopologyDeploymentPreview, error)

PreviewTopologyDeployment performs all checks that are safe to perform before PlanTopologyDeployment. It intentionally does not call the planner, since the planner persists a deployment row by design.

type TopologyDeploymentPreviewInput

type TopologyDeploymentPreviewInput struct {
	TopologyID    uint   `json:"topology_id"`
	RevisionID    uint   `json:"revision_id"`
	RolloutGroup  string `json:"rollout_group"`
	FailurePolicy string `json:"failure_policy"`
}

TopologyDeploymentPreviewInput identifies a revision and the optional rollout slice to inspect. Preview is deliberately read-only: it never creates a deployment, operation, or observed-state row.

type TopologyDeploymentPreviewStep

type TopologyDeploymentPreviewStep struct {
	Order         int      `json:"order"`
	VertexID      uint     `json:"vertex_id"`
	VertexKey     string   `json:"vertex_key"`
	NodeID        uint     `json:"node_id"`
	PluginID      string   `json:"plugin_id"`
	Role          string   `json:"role"`
	TargetVersion string   `json:"target_version"`
	Removal       bool     `json:"removal"`
	ApplyAction   string   `json:"apply_action"`
	RollbackMode  string   `json:"rollback_mode"`
	Dependencies  []string `json:"dependencies,omitempty"`
	Operations    []string `json:"operations"`
	ConfigHash    string   `json:"config_hash,omitempty"`
}

TopologyDeploymentPreviewStep describes the durable operations that a real plan would create. Operation IDs are intentionally absent because preview must not reserve identities or mutate the operation stream.

type TopologyDeploymentStatus

type TopologyDeploymentStatus struct {
	Deployment model.TopologyDeployment       `json:"deployment"`
	Steps      []model.TopologyDeploymentStep `json:"steps"`
	Observed   []model.TopologyObservedState  `json:"observed_states"`
	Operations []TopologyDeploymentOperation  `json:"operations"`
	Events     []TopologyDeploymentEvent      `json:"events"`
}

TopologyDeploymentStatus is the transport-neutral status read used by the HTTP handler and tests. All fields are persisted kernel records.

func GetTopologyDeploymentStatus

func GetTopologyDeploymentStatus(db *gorm.DB, deploymentID uint) (*TopologyDeploymentStatus, error)

type TopologyDeploymentStatusView

type TopologyDeploymentStatusView struct {
	Deployment TopologyDeploymentView        `json:"deployment"`
	Steps      []TopologyDeploymentStepView  `json:"steps"`
	Observed   []TopologyObservedStateView   `json:"observed_states"`
	Operations []TopologyDeploymentOperation `json:"operations"`
	Events     []TopologyDeploymentEvent     `json:"events"`
}

TopologyDeploymentStatusView is the only topology deployment status shape intended for HTTP clients. It deliberately leaves configuration documents, rollback documents, Agent result documents, Agent health documents, and raw persisted errors on the kernel side of the boundary.

func PublicTopologyDeploymentStatus

func PublicTopologyDeploymentStatus(status TopologyDeploymentStatus) TopologyDeploymentStatusView

PublicTopologyDeploymentStatus projects an internal status read to its explicit public representation. Do not return TopologyDeploymentStatus from an HTTP handler: it contains persistence models with secret-bearing fields.

type TopologyDeploymentStepView

type TopologyDeploymentStepView struct {
	ID                           uint      `json:"id"`
	DeploymentID                 uint      `json:"deployment_id"`
	VertexID                     uint      `json:"vertex_id"`
	VertexKey                    string    `json:"vertex_key"`
	NodeID                       uint      `json:"node_id"`
	PluginID                     string    `json:"plugin_id"`
	Role                         string    `json:"role"`
	TargetVersion                string    `json:"target_version"`
	ApplyOrder                   int       `json:"apply_order"`
	Removal                      bool      `json:"removal"`
	ApplyAction                  string    `json:"apply_action"`
	RollbackMode                 string    `json:"rollback_mode"`
	State                        string    `json:"state"`
	HasError                     bool      `json:"has_error"`
	LastError                    string    `json:"last_error,omitempty"`
	ConfigureOperationID         string    `json:"configure_operation_id,omitempty"`
	EnableOperationID            string    `json:"enable_operation_id,omitempty"`
	DisableOperationID           string    `json:"disable_operation_id,omitempty"`
	RollbackConfigureOperationID string    `json:"rollback_configure_operation_id,omitempty"`
	RollbackEnableOperationID    string    `json:"rollback_enable_operation_id,omitempty"`
	RollbackDisableOperationID   string    `json:"rollback_disable_operation_id,omitempty"`
	CreatedAt                    time.Time `json:"created_at"`
	UpdatedAt                    time.Time `json:"updated_at"`
}

TopologyDeploymentStepView is a state-only projection. The active and rollback configuration documents remain write-only kernel records.

type TopologyDeploymentView

type TopologyDeploymentView struct {
	ID                   uint       `json:"id"`
	TopologyID           uint       `json:"topology_id"`
	RevisionID           uint       `json:"revision_id"`
	PreviousRevisionID   *uint      `json:"previous_revision_id,omitempty"`
	RolloutGroup         string     `json:"rollout_group"`
	State                string     `json:"state"`
	FailurePolicy        string     `json:"failure_policy"`
	HasError             bool       `json:"has_error"`
	LastError            string     `json:"last_error,omitempty"`
	HealthGateDeadlineAt *time.Time `json:"health_gate_deadline_at,omitempty"`
	RollbackStartedAt    *time.Time `json:"rollback_started_at,omitempty"`
	RollbackCompletedAt  *time.Time `json:"rollback_completed_at,omitempty"`
	CreatedBy            uint       `json:"created_by"`
	CreatedAt            time.Time  `json:"created_at"`
	UpdatedAt            time.Time  `json:"updated_at"`
	CompletedAt          *time.Time `json:"completed_at,omitempty"`
}

TopologyDeploymentView is a status-only projection of a durable deployment. LastError is a stable summary derived from state, never the persisted value.

func PublicTopologyDeployment

func PublicTopologyDeployment(deployment model.TopologyDeployment) TopologyDeploymentView

type TopologyEdge

type TopologyEdge struct {
	ID           string  `json:"id"` // "tunnel-<id>"
	Source       string  `json:"source"`
	Target       string  `json:"target"`
	Label        string  `json:"label"`
	TunnelID     uint    `json:"tunnelId"`
	ForwardCount int     `json:"forwardCount"`
	Status       int     `json:"status"`
	AvgRTT       float64 `json:"avgRtt"`
}

type TopologyNode

type TopologyNode struct {
	ID          string  `json:"id"` // "node-<id>"
	Label       string  `json:"label"`
	Kind        string  `json:"kind"` // relay | exit
	Region      string  `json:"region"`
	Online      bool    `json:"online"`
	Status      int     `json:"status"`
	LatencyMs   int     `json:"latencyMs"`
	Load        float64 `json:"load"`
	CurrentConn int     `json:"currentConn"`
	Weight      int     `json:"weight"`
}

type TopologyObservedStateUpdate

type TopologyObservedStateUpdate struct {
	DeploymentID     uint
	NodeID           uint
	DesiredRevision  int64
	ObservedRevision int64
	State            string
	HealthJSON       string
	LastError        string
	ObservedAt       time.Time
}

TopologyObservedStateUpdate is the transport-neutral observed-state write contract used by the Agent bridge and the future topology executor.

type TopologyObservedStateView

type TopologyObservedStateView struct {
	ID               uint      `json:"id"`
	DeploymentID     uint      `json:"deployment_id"`
	NodeID           uint      `json:"node_id"`
	DesiredRevision  int64     `json:"desired_revision"`
	ObservedRevision int64     `json:"observed_revision"`
	State            string    `json:"state"`
	HealthReported   bool      `json:"health_reported"`
	HasError         bool      `json:"has_error"`
	LastError        string    `json:"last_error,omitempty"`
	UpdatedAt        time.Time `json:"updated_at"`
}

TopologyObservedStateView contains only monotonic state metadata. Health is represented by a presence bit because plugin-provided health JSON is not a stable or safe API contract.

func PublicTopologyObservedState

func PublicTopologyObservedState(observed model.TopologyObservedState) TopologyObservedStateView

func PublicTopologyObservedStates

func PublicTopologyObservedStates(rows []model.TopologyObservedState) []TopologyObservedStateView

type TopologyRevisionInput

type TopologyRevisionInput struct {
	Message  string                 `json:"message"`
	Vertices []model.TopologyVertex `json:"vertices"`
	Edges    []model.TopologyEdge   `json:"edges"`
}

type TopologyValidationIssue

type TopologyValidationIssue struct {
	Code    string `json:"code"`
	Path    string `json:"path"`
	Message string `json:"message"`
}

func ValidateTopologyAssignments

func ValidateTopologyAssignments(db *gorm.DB, scopeID string, input TopologyRevisionInput) []TopologyValidationIssue

ValidateTopologyAssignments verifies that a plugin instance placed on a physical node has a matching enabled service assignment in the topology's scope. It deliberately does not inspect runtime Agent capabilities yet: those are observed by the Supervisor phase, while assignments remain the control-plane source of truth.

type TrafficLogMeta

type TrafficLogMeta struct {
	LatestLogAt int64 `json:"latest_log_at"` // 最近一次流量上报 Unix 秒, 0 表示从未上报
}

type TunnelDiagnosisReport

type TunnelDiagnosisReport struct {
	TunnelID   uint               `json:"tunnelId"`
	TunnelName string             `json:"tunnelName"`
	TunnelType string             `json:"tunnelType"`
	Timestamp  int64              `json:"timestamp"`
	Results    []DiagnosisOutcome `json:"results"`
}

type UserListParams

type UserListParams struct {
	Page     int
	PageSize int
	Email    string
	PlanID   *uint
	Status   string // all, active, expired, banned
	OrderBy  string
}

UserListParams 用户列表查询参数

type UserListResult

type UserListResult struct {
	Total int64        `json:"total"`
	List  []model.User `json:"list"`
}

UserListResult 用户列表结果

type UserService

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

UserService 用户服务

func NewUserService

func NewUserService() *UserService

NewUserService 创建用户服务

func (*UserService) Ban

func (s *UserService) Ban(id uint) error

Ban 封禁用户

func (*UserService) BatchUpdateTraffic

func (s *UserService) BatchUpdateTraffic(traffics map[uint][2]int64) error

BatchUpdateTraffic 批量更新用户流量

func (*UserService) Create

func (s *UserService) Create(user *model.User) error

Create 创建用户

func (*UserService) Delete

func (s *UserService) Delete(id uint) error

Delete 删除用户

func (*UserService) GetActiveUsers

func (s *UserService) GetActiveUsers() ([]model.User, error)

GetActiveUsers 获取所有有效用户

func (*UserService) GetActiveUsersByGroupID

func (s *UserService) GetActiveUsersByGroupID(groupID uint) ([]model.User, error)

GetActiveUsersByGroupID 获取指定分组的有效用户

func (*UserService) GetActiveUsersForNode

func (s *UserService) GetActiveUsersForNode(groupID *uint) ([]*model.User, error)

GetActiveUsersForNode 获取节点可用的有效用户 groupID 为 nil 时返回所有有效用户,否则返回指定分组的用户

func (*UserService) GetByEmail

func (s *UserService) GetByEmail(email string) (*model.User, error)

GetByEmail 根据Email获取用户

func (*UserService) GetByID

func (s *UserService) GetByID(id uint) (*model.User, error)

GetByID 根据ID获取用户

func (*UserService) GetByToken

func (s *UserService) GetByToken(token string) (*model.User, error)

GetByToken 根据Token获取用户

func (*UserService) GetByUUID

func (s *UserService) GetByUUID(uuid string) (*model.User, error)

GetByUUID 根据UUID获取用户

func (*UserService) GetList

func (s *UserService) GetList(params UserListParams) (*UserListResult, error)

GetList 获取用户列表

func (*UserService) GetStats

func (s *UserService) GetStats() (map[string]any, error)

GetStats 获取用户统计

func (*UserService) ResetToken

func (s *UserService) ResetToken(id uint) (string, error)

ResetToken 为用户重新生成订阅 token, 让旧的 /s/<token> 链接立即失效。 不改动 UUID, 所以节点端密码/连接不受影响, 用户只需重新导入订阅。 返回新生成的 token。

func (*UserService) ResetTraffic

func (s *UserService) ResetTraffic(id uint) error

ResetTraffic 重置用户流量

func (*UserService) Unban

func (s *UserService) Unban(id uint) error

Unban 解封用户

func (*UserService) Update

func (s *UserService) Update(id uint, updates map[string]any) error

Update 更新用户

func (*UserService) UpdateTraffic

func (s *UserService) UpdateTraffic(userID uint, upload, download int64) error

UpdateTraffic 更新用户流量

type UserSubscription

type UserSubscription struct {
	UserID           uint      `json:"user_id"`
	Email            string    `json:"email"`
	PlanID           *uint     `json:"plan_id"`
	PlanName         string    `json:"plan_name"`
	TransferEnable   int64     `json:"transfer_enable"`  // 总流量 (字节)
	UsedTraffic      int64     `json:"used_traffic"`     // 已用流量 (字节)
	UploadTraffic    int64     `json:"upload_traffic"`   // 上传流量 (字节)
	DownloadTraffic  int64     `json:"download_traffic"` // 下载流量 (字节)
	ExpiredAt        int64     `json:"expired_at"`       // 到期时间
	IsExpired        bool      `json:"is_expired"`
	DaysRemaining    int       `json:"days_remaining"`
	UsagePercent     float64   `json:"usage_percent"`
	SubscribePath    string    `json:"subscribe_path,omitempty"`
	SubscribeDomains []string  `json:"subscribe_domains,omitempty"`
	CachedAt         time.Time `json:"cached_at"`
}

UserSubscription 用户订阅详情

type UserTrafficRank

type UserTrafficRank struct {
	UserID  uint   `json:"user_id"`
	Email   string `json:"email"`
	Traffic int64  `json:"traffic"` // 区间内总流量 (字节, 已按倍率计)
}

UserTrafficRank 单个用户的流量排行项

type WebUIExtension

type WebUIExtension struct {
	PluginID       string               `json:"plugin_id"`
	PluginName     string               `json:"plugin_name"`
	Publisher      string               `json:"publisher"`
	Version        string               `json:"version"`
	APIVersion     string               `json:"api_version"`
	InstallationID uint                 `json:"installation_id"`
	State          string               `json:"state"`
	Bundle         WebUIExtensionBundle `json:"bundle"`
	Permissions    []string             `json:"permissions"`
	Menus          []PluginWebUIMenu    `json:"menus"`
	Routes         []PluginWebUIRoute   `json:"routes"`
	ConfigSchema   json.RawMessage      `json:"config_schema"`
}

func ListEnabledWebUIExtensions

func ListEnabledWebUIExtensions(db *gorm.DB, publicKey ed25519.PublicKey) ([]WebUIExtension, error)

ListEnabledWebUIExtensions returns only locally installed administrator extensions whose observed control version exactly matches desired state. Stored release signatures are rechecked so catalog reads fail closed if metadata is corrupted after admission.

func ListEnabledWebUIExtensionsForActor

func ListEnabledWebUIExtensionsForActor(db *gorm.DB, publicKey ed25519.PublicKey, actorID uint, legacyAdmin bool) ([]WebUIExtension, error)

ListEnabledWebUIExtensionsForActor applies the same authoritative plugin permissions as the control route gateway. Restricted actors only receive routes, menus and declared permissions that they can actually use.

type WebUIExtensionBundle

type WebUIExtensionBundle struct {
	Path   string `json:"path"`
	SHA256 string `json:"sha256"`
	URL    string `json:"url"`
}

type WireGuardPeerRotationResult

type WireGuardPeerRotationResult struct {
	RotatedPeerCount int
	ProtocolIDs      []uint
}

WireGuardPeerRotationResult reports only non-secret rotation metadata.

func RotateWireGuardPeerKeys

func RotateWireGuardPeerKeys(db *gorm.DB, protocolID uint) (*WireGuardPeerRotationResult, error)

RotateWireGuardPeerKeys replaces every selected peer's client private key, public key, and preshared key in one database transaction. Peer IDs, user assignments, and tunnel IPs remain stable so the node can apply the update without reallocating addresses.

Source Files

Jump to

Keyboard shortcuts

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