Documentation
¶
Index ¶
- Constants
- func IsLocalizedText(value any) bool
- func NormalizeThemeRedirectTarget(data any) (string, bool)
- type APIKey
- type Client
- type Clipboard
- type Configuration
- type EventMessage
- type GPURecord
- type LoadNotification
- type Log
- type MaintenanceWindow
- type ManagedThemeConfigurationItem
- type MessageSenderProvider
- type OfflineNotification
- type OidcProvider
- type PageType
- type PageVisibility
- type PingRecord
- type PingTask
- type Plugin
- type PluginConfiguration
- type PluginPage
- type PluginPermissions
- type Record
- type Session
- type StringArray
- type Task
- type TaskResult
- type Theme
- type ThemeConfiguration
- type TrafficReportNotification
- type UnlockReport
- type User
Constants ¶
const ( // LoadThresholdModeFixed 使用固定阈值(Threshold 字段),即历史行为。 LoadThresholdModeFixed = "fixed" // LoadThresholdModeBaseline 用「该指标自身的历史基线」算出阈值, // 从而不必为每台机器/每个指标手工调一个绝对值。 LoadThresholdModeBaseline = "baseline" )
负载通知的阈值模式。
const ( ThemeConfigurationManaged = "managed" ThemeConfigurationRaw = "raw" ThemeConfigurationRedirect = "redirect" )
Variables ¶
This section is empty.
Functions ¶
func IsLocalizedText ¶
IsLocalizedText reports whether a manifest text field has a usable value.
Types ¶
type APIKey ¶ added in v1.7.0
type APIKey struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
Name string `json:"name" gorm:"type:varchar(80);not null"`
TokenDigest string `json:"-" gorm:"type:varchar(64);not null;uniqueIndex"`
Scopes []string `json:"scopes" gorm:"serializer:json;type:text;not null"`
IssuedBy string `json:"issued_by" gorm:"type:varchar(36)"`
CreatedAt time.Time `json:"created_at"`
ExpiresAt time.Time `json:"expires_at" gorm:"index"`
RevokedAt *time.Time `json:"revoked_at"`
}
APIKey stores only a digest, never the token. Legacy archives/WAL may still contain the retired global credential and require normal secret retention.
type Client ¶
type Client struct {
UUID string `json:"uuid,omitempty" gorm:"type:varchar(36);primaryKey"`
Token string `json:"token,omitempty" gorm:"type:varchar(255);unique;not null"`
Name string `json:"name" gorm:"type:varchar(100)"`
CpuName string `json:"cpu_name" gorm:"type:varchar(100)"`
Virtualization string `json:"virtualization" gorm:"type:varchar(50)"`
Arch string `json:"arch" gorm:"type:varchar(50)"`
CpuCores int `json:"cpu_cores" gorm:"type:int"`
CpuPhysicalCores int `json:"cpu_physical_cores" gorm:"type:int"`
OS string `json:"os" gorm:"type:varchar(100)"`
KernelVersion string `json:"kernel_version" gorm:"type:varchar(100)"`
GpuName string `json:"gpu_name" gorm:"type:varchar(100)"`
IPv4 string `json:"ipv4,omitempty" gorm:"type:varchar(100)"`
IPv6 string `json:"ipv6,omitempty" gorm:"type:varchar(100)"`
Region string `json:"region" gorm:"type:varchar(100)"`
Remark string `json:"remark,omitempty" gorm:"type:longtext"`
PublicRemark string `json:"public_remark,omitempty" gorm:"type:longtext"`
MemTotal int64 `json:"mem_total" gorm:"type:bigint"`
SwapTotal int64 `json:"swap_total" gorm:"type:bigint"`
DiskTotal int64 `json:"disk_total" gorm:"type:bigint"`
Version string `json:"version,omitempty" gorm:"type:varchar(100)"`
// TargetVersion is the release the panel pinned this node to, or empty when
// the node should track releases again. It lives in the panel's database, not
// only in the agent's memory, so a rollout survives a panel restart: the panel
// re-sends it whenever the node connects (see web/api/client/report_v2.go).
TargetVersion string `json:"target_version,omitempty" gorm:"type:varchar(100)"`
// TargetVersionPendingClear records that a cleared rollout target has not
// reached the node yet, because the node was offline when the operator cleared
// it. The agent only keeps its target in memory, so nothing else would tell
// that node to stop tracking the old pin: it is re-sent an explicit empty
// target when it connects (see web/api/client/report_v2.go).
//
// Not part of the client JSON: it is panel bookkeeping, not a property of the
// node, and the admin agentVersions row exposes it explicitly instead.
TargetVersionPendingClear bool `json:"-" gorm:"default:false"`
// What the agent reported about this host's ICMP sockets: "raw", "ping",
// "none", or "" when an older agent has not said. The empty value means
// *unknown*, and the UI must render it as unknown rather than as a denial --
// an agent that has never reported is not a node that cannot probe.
ICMPCapability string `json:"icmp_capability,omitempty" gorm:"type:varchar(10)"`
Weight int `json:"weight" gorm:"type:int"`
Price float64 `json:"price"`
BillingCycle int `json:"billing_cycle"`
AutoRenewal bool `json:"auto_renewal" gorm:"default:false"` // 是否自动续费
Currency string `json:"currency" gorm:"type:varchar(20);default:'$'"`
ExpiredAt *time.Time `json:"expired_at" gorm:"type:timestamp"`
Group string `json:"group" gorm:"type:varchar(100)"`
Tags string `json:"tags" gorm:"type:text"` // split by ';'
Hidden bool `json:"hidden" gorm:"default:false"`
TrafficLimit int64 `json:"traffic_limit" gorm:"type:bigint"`
TrafficLimitType string `json:"traffic_limit_type" gorm:"type:varchar(10);default:'max'"` // 流量阈值类型:sum max min up down
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Client represents a registered client device
type Clipboard ¶
type Clipboard struct {
Id int `json:"id" gorm:"primaryKey;autoIncrement;unique"`
Text string `json:"text" gorm:"type:longtext"`
Name string `json:"name" gorm:"type:varchar(255)"`
Weight int `json:"weight" gorm:"type:int"`
Remark string `json:"remark" gorm:"type:text"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Configuration ¶
type EventMessage ¶
type GPURecord ¶
type GPURecord struct {
Client string `json:"client" gorm:"type:varchar(36);index"` // 客户端UUID
Time time.Time `json:"time" gorm:"index"` // 记录时间
DeviceIndex int `json:"device_index" gorm:"index"` // GPU设备索引 (0,1,2...)
DeviceName string `json:"device_name" gorm:"type:varchar(100)"` // GPU型号
MemTotal int64 `json:"mem_total" gorm:"type:bigint"` // 显存总量(字节)
MemUsed int64 `json:"mem_used" gorm:"type:bigint"` // 显存使用(字节)
Utilization float32 `json:"utilization" gorm:"type:decimal(5,2)"` // GPU使用率(%)
Temperature int `json:"temperature"` // GPU温度(°C)
}
GPURecord logs individual GPU metrics over time
type LoadNotification ¶
type LoadNotification struct {
Id uint `json:"id,omitempty" gorm:"primaryKey;autoIncrement"`
Name string `json:"name" gorm:"type:varchar(255)"`
Clients StringArray `json:"clients" gorm:"type:longtext"`
Metric string `json:"metric" gorm:"type:varchar(50);not null;default:'cpu'"` // 监控指标,如 cpu, ram, load
Threshold float32 `json:"threshold" gorm:"type:decimal(5,2);not null;default:80.00"` // 固定阈值;baseline 模式下作为【下限】使用
Ratio float32 `json:"ratio" gorm:"type:decimal(5,2);not null;default:0.80"` // 达标时间比
Interval int `json:"interval" gorm:"type:int;not null;default:15"` // 监测间隔(分钟)
LastNotified *time.Time `json:"last_notified"` // 上次通知时间
// --- 基线模式(Mode = "baseline")---
//
// 动机:固定阈值对不同的机器/指标要各自调参,且无法表达「这台机器今天明显
// 比它平时差」。基线模式改为与自身历史比较:
// 阈值 = max(P95(历史窗口) × Multiplier, Threshold)
// 其中 Threshold 退化为【下限】,用来避免基线接近 0 时产生噪声告警
// (例如丢包率平时为 0,可把下限设成 1 表示「有丢包就报」)。
Mode string `json:"mode" gorm:"type:varchar(12);not null;default:'fixed'"` // fixed | baseline
BaselineDays int `json:"baseline_days" gorm:"type:int;not null;default:7"` // 基线回看天数
Multiplier float32 `json:"multiplier" gorm:"type:decimal(6,2);not null;default:3.00"` // 超过基线的倍数即视为异常
// Tasks 仅在 Metric 为 ping 指标(ping_latency / ping_loss)时使用,
// 存放延迟监测任务的 id。告警会应用到「运行该任务的服务器」上,
// 因此不需要再单独选客户端;若同时选了客户端,则只在其中求值。
Tasks StringArray `json:"tasks" gorm:"type:longtext"`
}
LoadNotification 定义了基于资源占用达标时间比的负载通知规则
func (LoadNotification) BaselineWindow ¶
func (n LoadNotification) BaselineWindow() time.Duration
BaselineWindow 返回基线回看的时长(天 -> Duration),非法值回退到 7 天。
func (LoadNotification) EffectiveMultiplier ¶
func (n LoadNotification) EffectiveMultiplier() float32
EffectiveMultiplier 返回有效的倍数,非法值回退到 3。
func (LoadNotification) UsesBaseline ¶
func (n LoadNotification) UsesBaseline() bool
UsesBaseline 报告该规则是否按历史基线取阈值。
type Log ¶
type Log struct {
ID uint `json:"id,omitempty" gorm:"primaryKey;autoIncrement"`
IP string `json:"ip" gorm:"type:varchar(45);"` // IPv4 or IPv6
UUID string `json:"uuid" gorm:"type:varchar(36);"`
Message string `json:"message" gorm:"type:text;not null"`
MsgType string `json:"msg_type" gorm:"type:varchar(20);not null;index:idx_logs_msg_type_time,priority:1"`
Time time.Time `json:"time" gorm:"autoCreateTime;not null;index:idx_logs_time;index:idx_logs_msg_type_time,priority:2"`
}
type MaintenanceWindow ¶ added in v0.1.37
type MaintenanceWindow struct {
ID uint `json:"id,omitempty" gorm:"primaryKey;autoIncrement"`
Name string `json:"name" gorm:"type:varchar(100);not null"`
Start time.Time `json:"start" gorm:"not null;index"`
End time.Time `json:"end" gorm:"not null;index"`
Clients StringArray `json:"clients" gorm:"type:longtext"` // empty = every node
Reason string `json:"reason,omitempty" gorm:"type:varchar(255)"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
MaintenanceWindow is a scheduled period in which a node's alerts are suppressed.
Why this is scoped to *notification* and not to collection: the metrics keep recording through a window, because the point of a maintenance window is that the node is doing something real. A report that showed a gap in the charts afterwards would be lying about what happened, and a suppressed alert that also suppressed the data would hide the maintenance's effect.
Clients is deliberately "empty means every node" rather than "empty means none". A fleet-wide maintenance is the common case, and a window that applies to nothing should be deleted rather than stored — the alternative is a table where the meaning of the empty list has to be remembered. See internal/maintenance for the boundary rules.
type ManagedThemeConfigurationItem ¶
type ManagedThemeConfigurationItem struct {
Key string `json:"key"`
Name any `json:"name"`
Required bool `json:"required"`
Type string `json:"type"` // string number select switch title textbox richtext nodes pingtasks
Options string `json:"options"`
Default any `json:"default"`
Help any `json:"help"`
}
type MessageSenderProvider ¶
type OfflineNotification ¶
type OfflineNotification struct {
Client string `` /* 138-byte string literal not displayed */
ClientInfo Client `json:"client_info,omitempty" gorm:"foreignKey:Client;references:UUID"`
Enable bool `json:"enable" gorm:"type:boolean;default:false"`
//Cooldown int `json:"cooldown" gorm:"type:int;not null;default:1800"` // 冷却时间(秒),默认 30 分钟
GracePeriod int `json:"grace_period" gorm:"type:int;not null;default:180"` // 宽限期(秒),默认 3 分钟
LastNotified *time.Time `json:"last_notified"` // 上次通知时间
}
Notification 定义了通知相关的数据库模型
type OidcProvider ¶
type PageVisibility ¶
type PageVisibility string
PageVisibility controls who can reach a plugin page.
const ( // PageVisibilityAdmin limits the page to the admin sidebar. PageVisibilityAdmin PageVisibility = "admin" // PageVisibilityPublic serves the page through the public route without // navigation, mirroring the theme public behavior. PageVisibilityPublic PageVisibility = "public" )
type PingRecord ¶
type PingRecord struct {
Client string `json:"client" gorm:"type:varchar(36);not null;index"`
ClientInfo Client `json:"client_info" gorm:"foreignKey:Client;references:UUID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
TaskId uint `json:"task_id" gorm:"not null;index"`
Task PingTask `json:"task" gorm:"foreignKey:TaskId;references:Id;constraint:OnDelete:CASCADE,OnUpdate:CASCADE;"`
Time time.Time `json:"time" gorm:"index;not null"`
Value int `json:"value" gorm:"type:int;not null"` // Ping 值,单位毫秒
// ValueUS 是同一测量结果的微秒精度值(新 agent 上报的 value_us)。
//
// 它不是数据库列(gorm:"-"):落库形态仍然是 ping.latency_ms 那一条序列,只是值
// 从整数毫秒换成小数毫秒。这个字段只负责把精度从上报入口带到 metric store,并在
// 读回时把小数毫秒还原成微秒交给前端。0 表示「旧 agent 没有上报」,JSON 里省略。
ValueUS int64 `json:"value_us,omitempty" gorm:"-"`
// PingType 记录本条结果是用哪种协议测出来的(icmp/tcp/http)。
// 双协议并列探测(dual)会让同一个任务在同一周期产出多条结果,
// 靠这个字段区分,避免两条序列互相覆盖。
PingType string `json:"ping_type" gorm:"type:varchar(12);not null;default:'';index"`
// Role 区分本条结果测的是主目标(空/"")还是参考点("reference")。
Role string `json:"role" gorm:"type:varchar(12);not null;default:'';index"`
// Family 记录本条结果【实际使用】的地址族("ipv4"/"ipv6")。
// 目标是域名时由各节点自行解析,双栈域名在不同节点上可能落到不同族 ——
// 那种情况下同一任务的两条曲线描述的是两条不同的路径,不能当作可比数据。
// 空值表示未上报(旧 agent)或无法判断,此时保持改动前的行为。
Family string `json:"family" gorm:"type:varchar(8);not null;default:'';index"`
}
type PingTask ¶
type PingTask struct {
Id uint `json:"id,omitempty" gorm:"primaryKey;autoIncrement"`
Weight int `json:"weight" gorm:"type:int;not null;default:0;index"`
Name string `json:"name" gorm:"type:varchar(255);not null;index"`
Clients StringArray `json:"clients" gorm:"type:longtext"`
DefaultOn bool `json:"default_on" gorm:"column:all_clients;not null;default:false"` // 新加入的服务器是否自动开启此监测;现有服务器不受此字段影响
Type string `json:"type" gorm:"type:varchar(12);not null;default:'icmp'"` // icmp tcp http auto dual
Target string `json:"target" gorm:"type:varchar(255);not null"` // Ping 目标地址
// Reference 是可选【参考目标】,用于路径归因:同一个探针在同一个周期里
// 既测目标也测参考点(典型做法是填本机网关或一个已知良好的节点)。
// 两者并列后就能区分「本机/内网出问题」还是「上游线路/代理出问题」,
// 而不是只看到一条延迟曲线在抖却不知道是哪一段。
Reference string `json:"reference" gorm:"type:varchar(255);not null;default:''"`
Interval int `json:"interval" gorm:"type:int;not null;default:60"` // 间隔时间
}
PingTask 表示一次延迟监测任务配置。
func (PingTask) AppliesToClient ¶
AppliesToClient 判断当前 PingTask 是否适用于指定服务器。
type Plugin ¶
type Plugin struct {
Name any `json:"name"`
Short string `json:"short"` // directory name under data/plugin
Description any `json:"description"`
Author any `json:"author"`
Version string `json:"version"`
URL string `json:"url"`
Icon string `json:"icon"`
Komari string `json:"komari"` // supported server version constraint, e.g. ">=0.0.1"
Entry string `json:"entry"` // entry script, defaults to "script.js"
Permissions PluginPermissions `json:"permissions"`
Configuration Configuration `json:"configuration"` // declared config items, same shape as themes
Pages []PluginPage `json:"pages,omitempty"` // injected admin pages
}
Plugin is the manifest of an installed plugin (komari-plugin.json).
Name, Description and Author accept either a plain string or an i18n map like {"zh_CN": "...", "en": "..."}, mirroring the theme manifest. The backend passes the value through and the frontend resolves it against the current locale.
type PluginConfiguration ¶
type PluginConfiguration struct {
Short string `json:"short" gorm:"primaryKey;unique;not null"`
Data string `json:"data" gorm:"type:longtext" default:"{}"`
}
PluginConfiguration stores the saved configuration values of one plugin, mirroring ThemeConfiguration.
type PluginPage ¶
type PluginPage struct {
File string `json:"file"`
Title any `json:"title"`
Icon string `json:"icon"`
Type PageType `json:"type,omitempty"`
URL string `json:"url,omitempty"`
Visibility PageVisibility `json:"visibility,omitempty"`
}
PluginPage declares a page provided by the plugin. An iframe page is served from the plugin directory (File); a redirect page navigates to an internal site path (URL). Visibility defaults to admin so existing packages keep their current behavior.
type PluginPermissions ¶
type PluginPermissions struct {
Node bool `json:"node"` // Node.js compatibility modules (runtime setting, not approval-relevant)
AllowSystemRPC bool `json:"allowSystemRPC"` // server.call: call system RPC methods with admin authority
AllowRoutes bool `json:"allowRoutes"` // server.route: register HTTP routes on the host engine
AllowHooks bool `json:"allowHooks"` // server.hook: modify HTTP requests/responses and intercept WebSocket connections/frames
AllowHTMLInject bool `json:"allowHTMLInject"` // server.injectHTML: embed CSS/JS into every HTML response
AllowExec bool `json:"allowExec"` // child_process: execute child processes
AllowListen bool `json:"allowListen"` // net/http servers: listen on local ports
AllowAllFileAccess bool `json:"allowAllFileAccess"` // access files outside the plugin directory
MaxHTTPBodyBytes int64 `json:"maxHTTPBodyBytes"` // runtime limit, not approval-relevant
MaxChildOutputBytes int `json:"maxChildOutputBytes"` // runtime limit, not approval-relevant
TimeoutSeconds int `json:"timeout"` // per-turn execution timeout in seconds, not approval-relevant
}
PluginPermissions declares the plugin capabilities that require admin approval. Every field defaults to its zero value: nothing is granted unless declared.
The following capabilities are always granted and never require approval: reading the plugin's own configuration (server.getConfig), registering plugin-owned RPC methods (server.registerRPC), and reading/writing files inside the plugin directory.
type Record ¶
type Record struct {
Client string `json:"client" gorm:"type:varchar(36);index"`
Time time.Time `json:"time" gorm:"index"`
Cpu float32 `json:"cpu" gorm:"type:decimal(5,2)"` // e.g., 75.50%
Gpu float32 `json:"gpu" gorm:"type:decimal(5,2)"`
Ram int64 `json:"ram" gorm:"type:bigint"`
RamTotal int64 `json:"ram_total" gorm:"type:bigint"`
Swap int64 `json:"swap" gorm:"type:bigint"`
SwapTotal int64 `json:"swap_total" gorm:"type:bigint"`
Load float32 `json:"load" gorm:"type:decimal(5,2)"`
Temp float32 `json:"temp" gorm:"type:decimal(5,2)"`
Disk int64 `json:"disk" gorm:"type:bigint"`
DiskTotal int64 `json:"disk_total" gorm:"type:bigint"`
NetIn int64 `json:"net_in" gorm:"type:bigint"`
NetOut int64 `json:"net_out" gorm:"type:bigint"`
NetTotalUp int64 `json:"net_total_up" gorm:"type:bigint"`
NetTotalDown int64 `json:"net_total_down" gorm:"type:bigint"`
TrafficUp int64 `json:"traffic_up" gorm:"type:bigint"`
TrafficDown int64 `json:"traffic_down" gorm:"type:bigint"`
Process int `json:"process"`
Connections int `json:"connections"`
ConnectionsUdp int `json:"connections_udp"`
// BackupAge 距最后一次成功备份的秒数(-1 = 未知)。
// 命名保持与其它字段一致,使负载告警的反射取值路径可直接使用 "BackupAge"。
BackupAge int64 `json:"backup_age" gorm:"type:bigint"`
// BackupOk 1=最近一次备份成功,0=失败或未知。
BackupOk int `json:"backup_ok" gorm:"type:int"`
}
Record logs client metrics over time
type Session ¶
type Session struct {
UUID string `json:"uuid" gorm:"type:varchar(36)"`
Session string `json:"session" gorm:"type:varchar(255);primaryKey;uniqueIndex:idx_sessions_session;not null"`
UserAgent string `json:"user_agent" gorm:"type:text"`
Ip string `json:"ip" gorm:"type:varchar(100)"`
LoginMethod string `json:"login_method" gorm:"type:varchar(50)"`
LatestOnline time.Time `json:"latest_online" gorm:"type:timestamp"`
LatestUserAgent string `json:"latest_user_agent" gorm:"type:text"`
LatestIp string `json:"latest_ip" gorm:"type:varchar(100)"`
Expires time.Time `json:"expires" gorm:"not null"`
CreatedAt time.Time `json:"created_at"`
}
Session manages user sessions
type StringArray ¶
type StringArray []string
StringArray represents a slice of strings stored as JSON in the database StringArray 存储为 JSON 的字符串切片类型
func (*StringArray) Scan ¶
func (sa *StringArray) Scan(value interface{}) error
type Task ¶
type Task struct {
TaskId string `json:"task_id" gorm:"type:varchar(36);primaryKey;unique"`
Clients StringArray `json:"clients" gorm:"type:longtext"`
Command string `json:"command" gorm:"type:text"`
Results []TaskResult `gorm:"foreignKey:TaskId;references:TaskId;constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
}
type TaskResult ¶
type TaskResult struct {
TaskId string `json:"task_id" gorm:"type:varchar(36);index"`
Client string `json:"client" gorm:"type:varchar(36)"`
ClientInfo Client `json:"client_info" gorm:"foreignKey:Client;references:UUID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
Result string `json:"result" gorm:"type:longtext"`
ExitCode *int `json:"exit_code" gorm:"type:int"`
FinishedAt *time.Time `json:"finished_at" gorm:"type:timestamp"`
CreatedAt time.Time `json:"created_at" gorm:"type:timestamp"`
}
type Theme ¶
type Theme struct {
Name any `json:"name"` // 主题名称,支持字符串或多语言对象
Short string `json:"short"` // 短名称,用作文件夹名
Description any `json:"description"` // 主题描述,支持字符串或多语言对象
Version string `json:"version"` // 版本号
Author any `json:"author"` // 作者,支持字符串或多语言对象
URL string `json:"url"` // 主题URL
Preview string `json:"preview"` // 预览图片相对路径
Configuration Configuration `json:"configuration"` // 声明配置项
}
Theme represents a komari theme information
func (Theme) ConfigurationType ¶
func (Theme) RedirectTarget ¶
func (Theme) ValidateConfiguration ¶
type ThemeConfiguration ¶
type TrafficReportNotification ¶
type TrafficReportNotification struct {
Client string `` /* 138-byte string literal not displayed */
ClientInfo Client `json:"client_info,omitempty" gorm:"foreignKey:Client;references:UUID"`
Enable bool `json:"enable" gorm:"type:boolean;default:false"`
Daily bool `json:"daily" gorm:"type:boolean;default:false"` // 日报
Weekly bool `json:"weekly" gorm:"type:boolean;default:false"` // 周报
Monthly bool `json:"monthly" gorm:"type:boolean;default:false"` // 月报
}
TrafficReportNotification 定义了流量定时报告的数据库模型
type UnlockReport ¶ added in v0.1.9
type UnlockReport struct {
UUID string `json:"uuid" gorm:"primaryKey;type:varchar(64)"`
EgressIP string `json:"egress_ip" gorm:"type:varchar(100)"`
EgressRegion string `json:"egress_region" gorm:"type:varchar(8)"`
ProbedAt time.Time `json:"probed_at"`
Results string `json:"results" gorm:"type:text"`
Routes string `json:"routes" gorm:"type:text"`
RoutesProbedAt *time.Time `json:"routes_probed_at"`
UpdatedAt time.Time `json:"updated_at"`
}
UnlockReport 是某个节点最近一次「流媒体 / AI 解锁」探测的结果。
按节点存而不是按 IP 存:面板问的是「这个节点能不能用」,而出口地址不一定是节点 自己的地址 —— 本机群里有主机是经由另一台节点出网的。所以出口地址单独存一份, 面板才能说清楚这份结论测的到底是哪个出口。
Results 是 JSON 数组,元素形状见 protocol/v2.UnlockItem。放 text 而不是拆表, 是因为它是每次探测整体替换的一份快照,没有按行查询的需求。
type User ¶
type User struct {
UUID string `json:"uuid,omitempty" gorm:"type:varchar(36);primaryKey"`
Username string `json:"username" gorm:"type:varchar(50);unique;not null"`
Passwd string `json:"passwd,omitempty" gorm:"type:varchar(255);not null"` // Hashed password
SSOType string `json:"sso_type" gorm:"type:varchar(20)"` // e.g., "github", "google"
SSOID string `json:"sso_id" gorm:"type:varchar(100)"` // OAuth provider's user ID
TwoFactor string `json:"two_factor,omitempty" gorm:"type:varchar(255)"` // 2FA secret
Sessions []Session `json:"sessions,omitempty" gorm:"foreignKey:UUID;references:UUID;constraint:OnDelete:CASCADE,OnUpdate:CASCADE"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
User represents an authenticated user