api

package
v0.1.7 Latest Latest
Warning

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

Go to latest
Published: Aug 3, 2026 License: GPL-3.0 Imports: 2 Imported by: 0

Documentation

Index

Constants

View Source
const (
	UserNotModified = "users not modified"
	NodeNotModified = "node not modified"
	RuleNotModified = "rules not modified"
)

Panel GET responses that hit an unchanged ETag surface these sentinel errors.

View Source
const (
	// PanelSSpanel — the traditional SSpanel. Ships nodes as the 6-segment
	// `server` string, and has no dedup on any of its POST endpoints.
	PanelSSpanel = "SSpanel"
	// PanelRigel — Rigel. Ships nodes as `custom_config` (either the
	// XrayR-flat shape or a sing-box-native inbound body, chosen per node by
	// its protocol — that split stays payload-driven, see
	// sspanel.ParseSSPanelNodeInfo).
	PanelRigel = "Rigel"
	// PanelPhoenixLegacy is what PanelRigel was called before the panel was
	// renamed Phoenix → Rigel. Still accepted: this value lives in config.yml
	// on every already-deployed node, and a panel-side rename must not require
	// touching them in the same maintenance window. NormalizePanelType folds it
	// onto PanelRigel; nothing downstream compares against it.
	PanelPhoenixLegacy = "Phoenix"
)

Panel types accepted in config.yml's Nodes[].PanelType. Both speak the mod_mu wire protocol; what differs is the node payload each one sends and, consequently, how safe it is to re-send a POST (doc/09 §5.1, doc/14 C-08).

View Source
const HeaderReportID = "X-Sing2-Report-Id"

HeaderReportID carries the accounting-period id on a traffic report, so a panel can drop a re-send without dropping a genuinely new report. Sent to Rigel only (doc/10 §7.1, doc/14 C-08); the traditional panel has no dedup to hang it on.

View Source
const HeaderReportIDAck = "X-Sing2-Report-Id-Ack"

HeaderReportIDAck is the panel's answer: present on a traffic response means this panel really did key its dedup on the HeaderReportID we sent, so a re-send of that id is provably free.

It exists to make a deploy-order mistake impossible rather than merely documented. The node re-sends a failed traffic report ONLY because it believes the panel dedups; a node pointed at a Rigel that has not run the migration yet would otherwise double-bill on every retry — the exact bug this mechanism closes. No ack, no retry (doc/10 §7.1).

Variables

This section is empty.

Functions

func NormalizePanelType added in v0.1.7

func NormalizePanelType(s string) (canonical string, legacy, ok bool)

NormalizePanelType folds an operator-written Nodes[].PanelType onto the canonical constant. Returns legacy=true when the value was an accepted but deprecated spelling (so the caller can warn once), and ok=false when it is not a recognised panel type at all.

Canonicalising once, up front, is what keeps the alias from leaking: every downstream comparison stays a plain `== PanelRigel`, with no second spelling to remember at each site.

Types

type API

type API interface {
	GetNodeInfo() (*NodeInfo, error)
	GetUserList() ([]UserInfo, error)
	GetNodeRule() ([]DetectRule, error)

	// ReportUserTraffic carries reset-then-report semantics (doc/10 §7.1).
	// windowID names the accounting period and MUST be identical on any re-send
	// of the same payload, so a panel that dedups on it can drop a duplicate
	// without dropping a genuinely new report (doc/14 C-08).
	ReportUserTraffic(userTraffic []UserTraffic, windowID string) error

	ReportNodeOnlineUsers(onlineUsers []OnlineUser) error // deduped online IPs (T8)
	ReportIllegal(detectResults []DetectResult) error     // SSpanel /mod_mu/users/detectlog (T6)
	ReportNodeStatus(nodeStatus *NodeStatus) error

	Describe() ClientInfo
	Debug()
}

API is the panel client interface. The three GET endpoints (GetNodeInfo/GetUserList/GetNodeRule) support ETag incremental sync and return the NodeNotModified/UserNotModified/RuleNotModified sentinel errors on HTTP 304. AccessLog reporting is a separate optional interface (AccessLogReporter, api/accesslog.go), discovered by type assertion.

type AccessLogEntry

type AccessLogEntry struct {
	UserID       *int    `json:"user_id"`
	Email        *string `json:"email"`
	Ts           int64   `json:"ts"`
	SrcIP        string  `json:"src_ip"`
	SrcPort      *int    `json:"src_port"`
	Action       string  `json:"action"` // accepted / rejected
	Network      *string `json:"network"`
	DestHost     *string `json:"dest_host"`
	DestPort     *int    `json:"dest_port"`
	RejectReason *string `json:"reject_reason"`
}

AccessLogEntry is a single access-log record reported to the panel. Nullable fields use pointers so they marshal to JSON null when absent (doc/10 §3.9).

type AccessLogReporter

type AccessLogReporter interface {
	ReportAccessLog(entries []AccessLogEntry) error
}

AccessLogReporter is an optional interface implemented by panel API clients that support access-log ingestion (mod_mu / sspanel-compatible). The billing/accesslog manager type-asserts each node's API client against it; clients that don't implement it are skipped (doc/10 §2.2, §6.4; doc/11 §13).

type ClientInfo

type ClientInfo struct {
	APIHost  string
	NodeID   int
	Key      string
	NodeType string
}

ClientInfo is returned by an API client's Describe() (doc/10 §3.7).

type Config

type Config struct {
	// PanelType is NOT read from the ApiConfig block — it lives one level up at
	// Nodes[].PanelType and the panel layer copies it in. Declared here so the
	// api client can see it without widening every constructor.
	PanelType string `mapstructure:"-"`

	APIHost      string  `mapstructure:"ApiHost"`
	NodeID       int     `mapstructure:"NodeID"`
	Key          string  `mapstructure:"ApiKey"`
	NodeType     string  `mapstructure:"NodeType"`
	EnableVless  bool    `mapstructure:"EnableVless"`
	VlessFlow    string  `mapstructure:"VlessFlow"`
	Timeout      int     `mapstructure:"Timeout"`
	SpeedLimit   float64 `mapstructure:"SpeedLimit"`
	DeviceLimit  int     `mapstructure:"DeviceLimit"`
	RuleListPath string  `mapstructure:"RuleListPath"`
	// DisableCustomConfig is XrayR compatibility: parsed, warned about, never
	// applied (2026-07-29). It used to force the legacy `server`-string parser
	// on a panel that also offered custom_config. PanelType now decides that
	// outright — SSpanel is always the server string, Rigel is always
	// custom_config — so there is nothing left for it to switch.
	DisableCustomConfig bool `mapstructure:"DisableCustomConfig"`
}

Config is the per-node panel API config (doc/09 §5, mapstructure keys match config.yml). Panel scope is SSpanel/mod_mu only (2026-07-22 decision).

type DetectResult

type DetectResult struct {
	UID    int // 命中用户(join key 尾部 uid)
	RuleID int // 命中的规则 ID
}

DetectResult is one audit-rule hit (doc/10 §3.6).

type DetectRule

type DetectRule struct {
	ID      int
	Pattern *regexp.Regexp
}

DetectRule is a compiled audit regex (doc/10 §3.6). ID -1 marks a local rule (never reported upstream).

type Dialect

type Dialect uint8

Dialect identifies which panel wire format produced a NodeInfo (doc/13 §3.1). It selects the translation path: the two flat dialects share the field-by-field translator, while DialectSingboxNative carries an opaque sing-box inbound body that is handed to the base fork's own unmarshaler.

const (
	// DialectLegacy — traditional SSpanel (< 2021.11), 6-segment `server` string.
	// Zero value, so an un-tagged NodeInfo keeps the historical behaviour.
	DialectLegacy Dialect = iota
	// DialectXrayRFlat — SSpanel >= 2021.11 with XrayR's flat `custom_config`
	// (offset_port_node / network / security / enable_vless / reality-opts).
	DialectXrayRFlat
	// DialectSingboxNative — `custom_config` shaped like a sing-box inbound
	// (Rigel). Carried opaquely in NativeRaw; see NativeType.
	DialectSingboxNative
)

func (Dialect) String

func (d Dialect) String() string

type InFlightCanceler added in v0.1.0

type InFlightCanceler interface {
	CancelInFlight()
}

InFlightCanceler aborts panel requests that are already waiting on the network. Optional, discovered by type assertion like AccessLogReporter — the method set of API itself stays as doc/10 §2.2 froze it.

It exists so shutdown does not have to WAIT OUT a slow panel. A sync cycle's worst case is minutes (retrying GETs at the API timeout, then the reports), while the shutdown wait is necessarily short, so without cancellation the wait is not a guarantee at all — and everything built on it (the pre-DelNode settlement, the teardown ordering) quietly stops holding exactly when the panel is sick, which is when shutdowns happen (doc/14 C-15).

type NameServer

type NameServer struct {
	Address   string
	Port      int
	Domains   []string // 命中域名 → 走此上游
	ExpectIPs []string // 期望 IP 段(分流)
}

NameServer is the neutral DNS upstream (doc/10 §3.8), replacing xray's conf.NameServerConfig. The translation layer emits option.DNSServerOptions.

type NodeInfo

type NodeInfo struct {
	// 方言
	Dialect Dialect

	// NativeType is the sing-box inbound type ("vless", "hysteria2", …) resolved
	// from custom_config.type. Only set for DialectSingboxNative.
	NativeType string

	// NativeRaw is the panel's custom_config with the panel-only keys removed
	// (`type`, `client`) — i.e. the inbound body verbatim. Only set for
	// DialectSingboxNative. The translation layer merges tag/listen/users into it
	// and lets the base fork unmarshal the result, so every field the fork
	// understands works without a per-field mapping here (doc/13 §2.3).
	NativeRaw json.RawMessage

	// 身份
	NodeType string
	NodeID   int
	Port     uint32 // 节点实际监听端口

	// OutsidePort 是「订阅下发端口」——客户端实际拨号的端口,与 Port 之间由
	// 运维在主机上做 NAT/端口转发(传统 SSpanel 的 `outside_port`,doc/13 §2.7)。
	// 0 表示未设置(等同于 Port)。**不参与监听**,仅用于日志/诊断:面板自己已把
	// 订阅端口改写好了(Tools.php:538-544),节点端如果拿它去 listen 反而会错。
	OutsidePort uint32

	// 限速 / 协议
	SpeedLimit   uint64
	AlterID      uint16 // vmess
	EnableVless  bool
	VlessFlow    string
	CypherMethod string // shadowsocks
	Method       string // ss method
	ServerKey    string // ss2022

	// 传输层
	TransportProtocol   string // tcp/ws/grpc/h2/...
	FakeType            string // tcp header 伪装
	Host                string // ws/h2 host
	Path                string // ws/h2 path
	ServiceName         string // gRPC serviceName
	Authority           string // gRPC/h2
	Header              json.RawMessage
	Headers             map[string]string
	HttpHeaders         map[string][]string // 原 map[string]*conf.StringList
	AcceptProxyProtocol bool
	// ProxyProtoLoopbackPort is set by core/singbox, not by any panel: when
	// Sing2 owns the public port for PROXY protocol, the base inbound has to
	// move somewhere else, and both the translation layer and the relay need to
	// agree on where. 0 means the inbound keeps the real port (native mode).
	ProxyProtoLoopbackPort uint16
	// ListenIP is the address the inbound binds, taken from the node's
	// ControllerConfig.ListenIP — node-local, never from a panel, and injected
	// by the controller the same way the REALITY private key and the Mieru
	// user-hint override are (see Controller.applyLocalListen).
	//
	// Empty means 0.0.0.0, which is what it has always been. That matters: the
	// value is IPv4-only, so a node that must also serve IPv6 has to say "::"
	// explicitly. Until 2026-07-30 the whole field was hardcoded and no
	// configuration could reach it, which made IPv6 unreachable on every
	// protocol except Mieru — mieru binds a wildcard of its own and never
	// consults this (doc/14 C-09).
	ListenIP  string
	EnableTFO bool

	// TLS
	EnableTLS        bool
	RejectUnknownSni bool

	// REALITY(统一到嵌套结构,去扁平重复)
	EnableREALITY bool
	REALITYConfig *REALITYConfig

	// DNS(中立化)
	NameServers []NameServer // 原 []*conf.NameServerConfig

	// Mieru 专有(传统 SSpanel 无 custom_config,只能走 server 串的 extra 段;
	// Rigel 走 native 方言时这些字段不使用,参数直接在 NativeRaw 里)。
	MieruListenPorts []string // listen_ports,端口段如 "2090-2099";非空则取代 Port
	MieruTrafficPat  string   // traffic_pattern(base64)
	MieruUserHint    bool     // user_hint_is_mandatory

	// 嗅探 / 杂项
	EnableSniffing bool
	RouteOnly      bool
	Flow           string
	Security       string
	Key            string
}

NodeInfo is the neutralized node descriptor (doc/10 §3.1): xray conf types removed (HttpHeaders → map[string][]string, DNS → []NameServer) and REALITY params collapsed into the nested *REALITYConfig (no flat duplicates).

Two shapes share this struct. The flat dialects populate the individual fields below; DialectSingboxNative populates only the identity fields plus NativeType/NativeRaw and leaves the rest zero (doc/13 §3.2).

type NodeStatus

type NodeStatus struct {
	CPU    float64
	Mem    float64
	Disk   float64
	Uptime uint64
}

NodeStatus is periodic node load reported to the panel (doc/10 §3.7).

type OnlineUser

type OnlineUser struct {
	UID int
	IP  string
}

OnlineUser is a user's currently-connected source IP (doc/10 §3.4).

type REALITYConfig

type REALITYConfig struct {
	Dest             string
	ProxyProtocolVer uint64
	ServerNames      []string
	PrivateKey       string // 敏感
	MinClientVer     string
	MaxClientVer     string
	MaxTimeDiff      uint64
	ShortIds         []string
}

REALITYConfig carries REALITY handshake params (doc/10 §3.2).

type UserInfo

type UserInfo struct {
	UID         int
	Email       string // SSpanel 面板直接下发,无需合成
	UUID        string // vmess/vless id
	Passwd      string // trojan/ss 密码(敏感)
	Port        uint32 // 单端口多用户面板用
	AlterID     uint16
	Method      string // ss method
	SpeedLimit  uint64 // per-user 限速 (Bps)
	DeviceLimit int    // per-user 设备上限
}

UserInfo is a single panel user (doc/10 §3.3). No standalone join-key field: NodeTag + Email + UID are joined downstream into "NodeTag|email|uid".

type UserTraffic

type UserTraffic struct {
	UID      int
	Email    string
	Upload   int64
	Download int64
}

UserTraffic is per-user byte accounting (doc/10 §3.5). Upload=uplink, Download=downlink, matching billing/stats counter direction (doc/03 T2).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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