Files
opsx-vpn/backend/internal/model/model.go
T

314 lines
17 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package model 定义平台数据模型(GORM + SQLite)
package model
import "time"
// IdentityProvider is the opt-in, single-tenant Feishu OIDC issuer configuration.
// Credentials are encrypted with the platform key and never serialized in API responses.
type IdentityProvider struct {
ID uint `gorm:"primaryKey" json:"-"`
Enabled bool `json:"enabled"`
AppID string `gorm:"size:128" json:"app_id"`
AppSecret string `gorm:"type:text" json:"-"`
TenantKey string `gorm:"size:128" json:"tenant_key"`
ClientID string `gorm:"size:128" json:"client_id"`
ClientSecret string `gorm:"type:text" json:"-"`
UserScopeVerifiedAt *time.Time `json:"-"` // Set only after a successful Feishu user-info callback.
UpdatedAt time.Time `json:"updated_at"`
}
// IdentityGrant stores only a hash of each browser state, authorization code,
// or access token. A consumed code cannot be exchanged a second time.
type IdentityGrant struct {
ID uint `gorm:"primaryKey"`
Kind string `gorm:"size:12;index"`
Digest string `gorm:"size:64;uniqueIndex"`
Payload string `gorm:"type:text"`
ExpiresAt time.Time `gorm:"index"`
Consumed bool `gorm:"default:false"`
}
// IdentityProfile is display metadata from a verified Feishu user-info response.
// It grants no VPN permissions and contains no upstream access token.
type IdentityProfile struct {
ID uint `gorm:"primaryKey"`
Issuer string `gorm:"uniqueIndex:idx_identity_profile;size:256"`
AppID string `gorm:"uniqueIndex:idx_identity_profile;size:128"`
Subject string `gorm:"uniqueIndex:idx_identity_profile;size:256"`
Name string `gorm:"size:256"`
UpdatedAt time.Time
}
// FeishuDirectorySync tracks a single application's import schedule and outcome.
type FeishuDirectorySync struct {
ID uint `gorm:"primaryKey"`
Enabled bool `json:"enabled"`
Issuer string `gorm:"size:256"`
AppID string `gorm:"size:128"`
LastAttempt *time.Time `json:"last_attempt"`
LastSuccess *time.Time `json:"last_success"`
LastError string `gorm:"size:512" json:"last_error"`
Departments int `json:"departments"`
Members int `json:"members"`
VisibleDepartments int `json:"visible_departments"`
VisibleUsers int `json:"visible_users"`
}
type FeishuDepartment struct {
ID uint `gorm:"primaryKey"`
Issuer string `gorm:"uniqueIndex:idx_feishu_department;size:256"`
AppID string `gorm:"uniqueIndex:idx_feishu_department;size:128"`
Department string `gorm:"uniqueIndex:idx_feishu_department;size:128"`
Parent string `gorm:"size:128"`
Name string `gorm:"size:256"`
LastSeenAt time.Time
}
type FeishuDirectoryMember struct {
ID uint `gorm:"primaryKey" json:"-"`
Issuer string `gorm:"uniqueIndex:idx_feishu_member;size:256" json:"-"`
AppID string `gorm:"uniqueIndex:idx_feishu_member;size:128" json:"-"`
OpenID string `gorm:"uniqueIndex:idx_feishu_member;size:128" json:"open_id"`
Name string `gorm:"size:256" json:"name"`
Departments string `gorm:"type:text" json:"department_ids"`
LastSeenAt time.Time `json:"last_seen_at"`
}
// FeishuTunnelGrant records explicit admission for a verified Feishu subject.
type FeishuTunnelGrant struct {
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"uniqueIndex:idx_feishu_tunnel_grant" json:"tunnel_id"`
Issuer string `gorm:"uniqueIndex:idx_feishu_tunnel_grant;size:256" json:"-"`
AppID string `gorm:"uniqueIndex:idx_feishu_tunnel_grant;size:128" json:"-"`
Subject string `gorm:"uniqueIndex:idx_feishu_tunnel_grant;size:256" json:"subject"`
CreatedAt time.Time `json:"created_at"`
}
// TrafficSample 流量时间序列采样(每 60 秒一次,用于趋势图) // Logan.Li
type TrafficSample struct {
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"index" json:"tunnel_id"`
RxBytes int64 `json:"rx_bytes"`
TxBytes int64 `json:"tx_bytes"`
DeltaRx int64 `json:"delta_rx"` // bytes since last sample
DeltaTx int64 `json:"delta_tx"`
CreatedAt time.Time `gorm:"index" json:"created_at"`
}
// User 平台账号
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"uniqueIndex;size:64" json:"username"`
Email string `gorm:"size:128" json:"email"`
Password string `gorm:"size:128" json:"-"` // 仅本地用户保存 bcrypt 哈希
Role string `gorm:"size:16;default:viewer" json:"role"` // admin / operator / viewer
Source string `gorm:"size:16;default:local" json:"source"` // local / ldap
Enabled bool `gorm:"default:true" json:"enabled"`
MustChangePassword bool `gorm:"default:false" json:"must_change_password"` // 首登/被重置后强制改密
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// LDAPConfig LDAP 身份源配置。BindPassword 由 store.EncryptSecret 加密后保存。
type LDAPConfig struct {
ID uint `gorm:"primaryKey" json:"id"`
URL string `gorm:"size:256" json:"url"`
BindDN string `gorm:"size:256" json:"bind_dn"`
BindPassword string `gorm:"type:text" json:"-"`
BaseDN string `gorm:"size:256" json:"base_dn"`
UserFilter string `gorm:"size:512" json:"user_filter"`
UsernameAttribute string `gorm:"size:64" json:"username_attribute"`
StartTLS bool `gorm:"default:false" json:"starttls"` // ldap:// 时升级为 StartTLS
Enabled bool `gorm:"default:false" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// MFADevice 保存管理平台用户的 TOTP 密钥和备用码(均加密保存)。
type MFADevice struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"uniqueIndex" json:"user_id"`
Secret string `gorm:"type:text" json:"-"`
BackupCodes string `gorm:"type:text" json:"-"`
Enabled bool `gorm:"default:false" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// VPNUser 是拨号身份,不等同于管理平台登录账号;可来自本地或 LDAP。
// Password 保存 bcrypt(OpenVPN 校验);DialSecret 保存 AES-GCM 可逆密文,
// 供 L2TP/IKEv2 等需要明文 CHAP/EAP 的协议重新生成认证文件。
// MFASecret / MFABackupCodes 为 OpenVPN 拨号 TOTP(AES-GCM 加密)。
type VPNUser struct {
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"index;uniqueIndex:idx_vpn_user" json:"tunnel_id"`
Username string `gorm:"size:128;uniqueIndex:idx_vpn_user" json:"username"`
DisplayName string `gorm:"size:128" json:"display_name"`
Email string `gorm:"size:128" json:"email"`
Source string `gorm:"size:16;default:local" json:"source"` // local / ldap
LDAPDN string `gorm:"size:512" json:"ldap_dn"`
Password string `gorm:"size:128" json:"-"`
DialSecret string `gorm:"type:text" json:"-"` // AES-GCM,L2TP/IKEv2 拨号用
MFASecret string `gorm:"type:text" json:"-"` // AES-GCM TOTP secret(base32,兼容主流 Authenticator 应用)
MFABackupCodes string `gorm:"type:text" json:"-"` // AES-GCM JSON 数组
MFAPin string `gorm:"type:text" json:"-"` // AES-GCM,可选静态 PIN(连接时第二因子 PIN+OTP)
MFAEnabled bool `gorm:"default:false" json:"mfa_enabled"`
MFAHasPin bool `gorm:"default:false" json:"mfa_has_pin"` // 是否设置了 PIN(不回显明文)
MFAFailCount int `gorm:"default:0" json:"mfa_fail_count"`
MFALockedUntil *time.Time `json:"mfa_locked_until"`
MFALastUsedAt *time.Time `json:"mfa_last_used_at"`
MFALastCounter int64 `gorm:"default:0" json:"-"` // 上次成功 TOTP 时间窗,防重放
Enabled bool `gorm:"default:true" json:"enabled"`
Certificate string `gorm:"size:128" json:"certificate"`
RxBytes int64 `gorm:"default:0" json:"rx_bytes"`
TxBytes int64 `gorm:"default:0" json:"tx_bytes"`
LastSeenAt *time.Time `json:"last_seen_at"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Tunnel 专线实例(所有协议统一存储,协议参数存 Config JSON)
type Tunnel struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128;index" json:"name"`
Protocol string `gorm:"size:32;index" json:"protocol"` // wireguard/ipsec/l2tp/frp/openvpn/custom:xxx
Remark string `gorm:"size:512" json:"remark"`
Config string `gorm:"type:text" json:"config"` // JSON,密钥字段加密存储
Secrets string `gorm:"type:text" json:"-"` // AES-GCM 加密的敏感字段 JSON
State string `gorm:"size:16;default:stopped" json:"state"` // running/stopped/error
AutoStart bool `gorm:"default:false" json:"auto_start"`
RxBytes int64 `gorm:"default:0" json:"rx_bytes"`
TxBytes int64 `gorm:"default:0" json:"tx_bytes"`
ExpireAt *time.Time `json:"expire_at"`
NodeID *uint `json:"node_id"`
Node *Node `json:"node,omitempty"`
LastError string `gorm:"size:1024" json:"last_error"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Node 中继节点(本机为内置节点),remote 节点支持 SSH 凭证验证 // Logan.Li
type Node struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128" json:"name"`
Address string `gorm:"size:256" json:"address"` // 节点地址(IP 或域名)
Port int `gorm:"default:22" json:"port"` // SSH 端口
Kind string `gorm:"size:32;default:local" json:"kind"` // local / remote
Username string `gorm:"size:64" json:"username"` // SSH 登录用户
AuthType string `gorm:"size:16;default:password" json:"auth_type"` // password / key
Secrets string `gorm:"type:text" json:"-"` // AES-GCM 加密的 {password, private_key} JSON
Status string `gorm:"size:16;default:unknown" json:"status"` // verified / failed / unknown
Remark string `gorm:"size:512" json:"remark"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
// Plugin 自定义协议插件(通过命令钩子实现 Protocol 契约)
type Plugin struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"uniqueIndex;size:64" json:"name"` // 协议标识
DisplayName string `gorm:"size:128" json:"display_name"`
Description string `gorm:"size:512" json:"description"`
// 命令钩子:平台将专线 Config JSON 通过 stdin 传入,插件按约定动作执行
CreateHook string `gorm:"size:512" json:"create_hook"`
StartHook string `gorm:"size:512" json:"start_hook"`
StopHook string `gorm:"size:512" json:"stop_hook"`
StatusHook string `gorm:"size:512" json:"status_hook"`
ConfigHook string `gorm:"size:512" json:"config_hook"` // 生成客户端配置
Enabled bool `gorm:"default:true" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
}
// AuditLog 操作审计
type AuditLog struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"size:64" json:"username"`
Action string `gorm:"size:64" json:"action"`
Target string `gorm:"size:256" json:"target"`
Detail string `gorm:"size:1024" json:"detail"`
CreatedAt time.Time `json:"created_at"`
}
// VPNSession 在线会话(跨协议统一记录拨号用户的连接状态) // Logan.Li
type VPNSession struct {
DisplayName string `gorm:"size:256" json:"display_name,omitempty"`
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"index" json:"tunnel_id"`
VPNUserID uint `gorm:"index" json:"vpn_user_id"`
Username string `gorm:"size:128" json:"username"`
Protocol string `gorm:"size:32" json:"protocol"`
RealAddress string `gorm:"size:64" json:"real_address"` // 客户端公网 IP:Port
VirtualAddress string `gorm:"size:64" json:"virtual_address"` // 分配的 VPN IP
ConnectedAt time.Time `json:"connected_at"`
DisconnectedAt *time.Time `json:"disconnected_at"`
RxBytes int64 `gorm:"default:0" json:"rx_bytes"`
TxBytes int64 `gorm:"default:0" json:"tx_bytes"`
Status string `gorm:"size:16;default:active" json:"status"` // active / disconnected
}
// ConnectionLog 连接审计日志(连接/断开/认证失败/配额超额/异常流量) // Logan.Li
type ConnectionLog struct {
DisplayName string `gorm:"size:256" json:"display_name,omitempty"`
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"index" json:"tunnel_id"`
Username string `gorm:"size:128" json:"username"`
Event string `gorm:"size:32" json:"event"` // connect / disconnect / auth_fail / quota_exceed / rate_alert
IPAddress string `gorm:"size:64" json:"ip_address"`
Detail string `gorm:"size:1024" json:"detail"`
CreatedAt time.Time `json:"created_at"`
}
// ClientDevice 终端用户设备(桌面/移动客户端注册)。
// 私钥只在客户端生成与保管,服务端仅保存公钥;撤销后不可恢复,需用新密钥重新注册。
type ClientDevice struct {
ID uint `gorm:"primaryKey" json:"id"`
VPNUserID uint `gorm:"index" json:"-"`
TunnelID uint `gorm:"index;index:idx_client_device_user" json:"tunnel_id"`
Username string `gorm:"size:128;index:idx_client_device_user" json:"username"` // 拨号用户名(VPNUser)
Name string `gorm:"size:128" json:"name"` // 设备名,如 "张三的 MacBook"
Platform string `gorm:"size:32" json:"platform"` // windows / macos / linux / android / ios
PeerID string `gorm:"size:64" json:"peer_id"` // WireGuard Peer ID(dev-<设备ID>)
PublicKey string `gorm:"size:64;uniqueIndex" json:"public_key"`
ClientAddress string `gorm:"size:64" json:"client_address"` // 分配的 VPN 地址,如 10.8.0.7/32
Revoked bool `gorm:"default:false" json:"revoked"`
RxBytes int64 `gorm:"default:0" json:"rx_bytes"`
TxBytes int64 `gorm:"default:0" json:"tx_bytes"`
LastSeenAt *time.Time `json:"last_seen_at"` // 最近一次握手时间
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// ClientSession 客户端长期访问令牌(只保存 SHA-256 哈希,滑动续期)。
// 与管理后台内存会话完全隔离,不携带任何管理权限。
type ClientSession struct {
ID uint `gorm:"primaryKey" json:"id"`
TokenHash string `gorm:"size:64;uniqueIndex" json:"-"`
Grants string `gorm:"type:text" json:"-"` // 授权的 VPNUser ID 与凭据指纹;旧会话无授权
Username string `gorm:"size:128;index" json:"username"`
DeviceName string `gorm:"size:128" json:"device_name"`
Platform string `gorm:"size:32" json:"platform"`
ExpireAt time.Time `gorm:"index" json:"expire_at"`
CreatedAt time.Time `json:"created_at"`
}
// AlertSetting 平台级突发流量阈值与飞书群机器人配置(单例)。
type AlertSetting struct {
ID uint `gorm:"primaryKey" json:"-"`
RateThresholdMbps int `gorm:"default:50" json:"rate_threshold_mbps"`
Enabled bool `gorm:"default:false" json:"enabled"`
WebhookEncrypted string `gorm:"type:text" json:"-"`
SigningEncrypted string `gorm:"type:text" json:"-"`
}
// TrafficPolicy 流量配额与限速策略(挂在 Tunnel 上) // Logan.Li
type TrafficPolicy struct {
ID uint `gorm:"primaryKey" json:"id"`
TunnelID uint `gorm:"uniqueIndex" json:"tunnel_id"`
QuotaBytes int64 `gorm:"default:0" json:"quota_bytes"` // 总配额,0=不限
RateLimitMbps int `gorm:"default:0" json:"rate_limit_mbps"` // 限速 Mbps,0=不限
UsedBytes int64 `gorm:"default:0" json:"used_bytes"`
ResetCycle string `gorm:"size:16;default:none" json:"reset_cycle"` // daily / monthly / none
LastResetAt time.Time `json:"last_reset_at"`
ActionOnExceed string `gorm:"size:16;default:block" json:"action_on_exceed"` // warn / block / throttle
}