Files
Wormhole/internal/store/models.go
T
rose_cat707 612d68f56f feat: Wormhole 内网穿透与反向代理网关
Go + Vue 管理台,SQLite 持久化;支持隧道/域名穿透、域名转发插件链、访客认证、
IP 规则、API Key、Docker 单镜像部署;配置通过 Web 系统设置管理,无需 YAML 文件。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-23 12:15:02 +08:00

237 lines
11 KiB
Go

package store
import (
"database/sql/driver"
"encoding/json"
"fmt"
"strings"
"time"
)
type User struct {
ID uint `gorm:"primaryKey" json:"id"`
Username string `gorm:"uniqueIndex;size:64;not null" json:"username"`
PasswordHash string `gorm:"size:255;not null" json:"-"`
Role string `gorm:"size:16;not null;default:visitor" json:"role"`
Status bool `gorm:"not null;default:true" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Client struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128;not null" json:"name"`
VerifyKey string `gorm:"uniqueIndex;size:64;not null" json:"verify_key"`
OwnerUserID uint `gorm:"index;not null" json:"owner_user_id"`
RateLimit int64 `gorm:"default:0" json:"rate_limit"`
MaxConn int `gorm:"default:0" json:"max_conn"`
ExpireAt *time.Time `json:"expire_at"`
Status bool `gorm:"not null;default:true" json:"status"`
IPForwardMode string `gorm:"size:16;default:replace" json:"ip_forward_mode"` // replace, append
CustomHeaders string `gorm:"type:text" json:"custom_headers"` // JSON map
InletFlow int64 `gorm:"default:0" json:"inlet_flow"`
ExportFlow int64 `gorm:"default:0" json:"export_flow"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Tunnel struct {
ID uint `gorm:"primaryKey" json:"id"`
ClientID uint `gorm:"index;not null" json:"client_id"`
Name string `gorm:"size:128" json:"name"`
Mode string `gorm:"size:16;not null" json:"mode"` // tcp, udp
ListenIP string `gorm:"size:64;default:0.0.0.0" json:"listen_ip"`
ListenPort int `gorm:"not null;uniqueIndex" json:"listen_port"`
TargetAddr string `gorm:"size:256;not null" json:"target_addr"`
IPForwardMode string `gorm:"size:16;default:replace" json:"ip_forward_mode"` // replace, append
CustomHeaders string `gorm:"type:text" json:"custom_headers"` // JSON map
Status bool `gorm:"not null;default:true" json:"status"`
RunStatus bool `gorm:"not null;default:false" json:"run_status"`
InletFlow int64 `gorm:"default:0" json:"inlet_flow"`
ExportFlow int64 `gorm:"default:0" json:"export_flow"`
VisitorAuthEnabled bool `gorm:"not null;default:false" json:"visitor_auth_enabled"`
VisitorBindMode string `gorm:"size:16;default:all" json:"visitor_bind_mode"` // all, selected
VisitorTTLSeconds int `gorm:"default:7200" json:"visitor_ttl_seconds"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type Host struct {
ID uint `gorm:"primaryKey" json:"id"`
ClientID uint `gorm:"index;not null" json:"client_id"`
Host string `gorm:"size:256;not null;uniqueIndex:idx_host_route" json:"host"`
Location string `gorm:"size:256;default:/;uniqueIndex:idx_host_route" json:"location"`
Scheme string `gorm:"size:16;default:all" json:"scheme"` // http, https, all
TargetAddr string `gorm:"size:256;not null" json:"target_addr"`
CertPath string `gorm:"size:512" json:"cert_path"`
KeyPath string `gorm:"size:512" json:"key_path"`
AutoHTTPS bool `gorm:"default:false" json:"auto_https"`
IPForwardMode string `gorm:"size:16;default:replace" json:"ip_forward_mode"` // replace, append
CustomHeaders string `gorm:"type:text" json:"custom_headers"` // JSON map
Status bool `gorm:"not null;default:true" json:"status"`
InletFlow int64 `gorm:"default:0" json:"inlet_flow"`
ExportFlow int64 `gorm:"default:0" json:"export_flow"`
VisitorAuthEnabled bool `gorm:"not null;default:false" json:"visitor_auth_enabled"`
VisitorBindMode string `gorm:"size:16;default:all" json:"visitor_bind_mode"` // all, selected
VisitorTTLSeconds int `gorm:"default:7200" json:"visitor_ttl_seconds"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ResourceVisitor struct {
ID uint `gorm:"primaryKey" json:"id"`
ResourceType string `gorm:"size:16;not null;uniqueIndex:idx_rv_bind" json:"resource_type"`
ResourceID uint `gorm:"not null;uniqueIndex:idx_rv_bind" json:"resource_id"`
UserID uint `gorm:"not null;uniqueIndex:idx_rv_bind" json:"user_id"`
CreatedAt time.Time `json:"created_at"`
}
type IPRule struct {
ID uint `gorm:"primaryKey" json:"id"`
Scope string `gorm:"size:16;not null;index" json:"scope"` // global, client, host, tunnel
ResourceID uint `gorm:"index;default:0" json:"resource_id"`
Type string `gorm:"size:8;not null" json:"type"` // allow, deny
Pattern string `gorm:"size:512;not null" json:"pattern"`
PatternKind string `gorm:"size:16;not null;default:exact" json:"pattern_kind"` // exact, cidr, regex
Priority int `gorm:"default:0" json:"priority"`
Remark string `gorm:"size:256" json:"remark"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type AuthPolicy struct {
ID uint `gorm:"primaryKey" json:"id"`
ResourceType string `gorm:"size:16;not null;index" json:"resource_type"` // host, tunnel
ResourceID uint `gorm:"index;not null" json:"resource_id"`
Type string `gorm:"size:16;not null" json:"type"` // basic, form, callback
Password string `gorm:"size:256" json:"-"`
CallbackURL string `gorm:"size:512" json:"callback_url"`
Enabled bool `gorm:"not null;default:true" json:"enabled"`
TTLSeconds int `gorm:"default:7200" json:"ttl_seconds"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type FlowStat struct {
ID uint `gorm:"primaryKey" json:"id"`
ResourceType string `gorm:"size:16;not null;uniqueIndex:idx_flow_resource" json:"resource_type"`
ResourceID uint `gorm:"not null;uniqueIndex:idx_flow_resource" json:"resource_id"`
InletBytes int64 `gorm:"default:0" json:"inlet_bytes"`
ExportBytes int64 `gorm:"default:0" json:"export_bytes"`
UpdatedAt time.Time `json:"updated_at"`
}
type RequestIP struct {
ID uint `gorm:"primaryKey" json:"id"`
ResourceType string `gorm:"size:16;not null;uniqueIndex:idx_req_ip" json:"resource_type"`
ResourceID uint `gorm:"not null;uniqueIndex:idx_req_ip" json:"resource_id"`
IP string `gorm:"size:64;not null;uniqueIndex:idx_req_ip" json:"ip"`
DirectIP string `gorm:"size:64" json:"direct_ip"`
HitCount int64 `gorm:"default:0" json:"hit_count"`
LastSeenAt time.Time `json:"last_seen_at"`
}
type Session struct {
ID uint `gorm:"primaryKey" json:"id"`
Token string `gorm:"uniqueIndex;size:512;not null" json:"token"`
UserID uint `gorm:"index;not null" json:"user_id"`
ExpiresAt time.Time `gorm:"index" json:"expires_at"`
CreatedAt time.Time `json:"created_at"`
}
type APIKey struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:128;not null" json:"name"`
KeyPrefix string `gorm:"size:16;not null" json:"key_prefix"`
KeyHash string `gorm:"size:64;not null;uniqueIndex" json:"-"`
UserID uint `gorm:"index;not null" json:"user_id"`
Status bool `gorm:"not null;default:true" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// VisitorAccessLog records visitor login and resource access (max 200 per user retained).
type VisitorAccessLog struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"index:idx_va_user_time;not null" json:"user_id"`
ResourceType string `gorm:"size:16;not null" json:"resource_type"`
ResourceID uint `gorm:"not null" json:"resource_id"`
Action string `gorm:"size:16;not null" json:"action"` // login, access
Method string `gorm:"size:16" json:"method"`
Path string `gorm:"size:512" json:"path"`
IP string `gorm:"size:64" json:"ip"`
UserAgent string `gorm:"size:256" json:"user_agent"`
CreatedAt time.Time `gorm:"index:idx_va_user_time" json:"created_at"`
}
// DomainForward maps a public source domain to another URL (redirect or reverse proxy).
// Unlike Host (penetration), this is handled entirely on the server without Agent.
type DomainForward struct {
ID uint `gorm:"primaryKey" json:"id"`
SourceHost string `gorm:"size:256;not null;uniqueIndex:idx_df_route" json:"source_host"`
SourceLocation string `gorm:"size:256;default:/;uniqueIndex:idx_df_route" json:"source_location"`
SourceScheme string `gorm:"size:16;default:all" json:"source_scheme"` // http, https, all
Mode string `gorm:"size:16;not null" json:"mode"` // redirect, proxy
TargetURL string `gorm:"size:1024;not null" json:"target_url"`
RedirectMode string `gorm:"size:16;default:preserve" json:"redirect_mode"` // preserve, fixed
RedirectCode int `gorm:"default:302" json:"redirect_code"`
ProxyType string `gorm:"size:16;default:http" json:"proxy_type"` // http(direct), socks5, http_proxy
Socks5Addr string `gorm:"size:256" json:"socks5_addr"`
Socks5User string `gorm:"size:128" json:"socks5_user"`
Socks5Pass string `gorm:"size:128" json:"-"`
HTTPProxyAddr string `gorm:"size:512" json:"http_proxy_addr"`
HTTPProxyUser string `gorm:"size:128" json:"http_proxy_user"`
HTTPProxyPass string `gorm:"size:128" json:"-"`
PreserveHost bool `gorm:"default:false" json:"preserve_host"`
Plugins ForwardPlugins `gorm:"type:text" json:"plugins"`
Status bool `gorm:"not null;default:true" json:"status"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type ForwardPluginEntry struct {
Type string `json:"type"`
Enabled bool `json:"enabled"`
Config json.RawMessage `json:"config"`
}
type ForwardPlugins []ForwardPluginEntry
func (p ForwardPlugins) Value() (driver.Value, error) {
if len(p) == 0 {
return "[]", nil
}
b, err := json.Marshal([]ForwardPluginEntry(p))
if err != nil {
return nil, err
}
return string(b), nil
}
func (p *ForwardPlugins) Scan(value interface{}) error {
if value == nil {
*p = nil
return nil
}
var raw string
switch v := value.(type) {
case string:
raw = v
case []byte:
raw = string(v)
default:
return fmt.Errorf("unsupported plugins value type %T", value)
}
raw = strings.TrimSpace(raw)
if raw == "" || raw == "null" {
*p = nil
return nil
}
var items []ForwardPluginEntry
if err := json.Unmarshal([]byte(raw), &items); err != nil {
return err
}
*p = items
return nil
}