// Package model defines the database models and data structures used by the 3x-ui panel. package model import ( "fmt" "github.com/mhsanaei/3x-ui/v2/util/json_util" "github.com/mhsanaei/3x-ui/v2/xray" ) // Protocol represents the protocol type for Xray inbounds. type Protocol string // Protocol constants for different Xray inbound protocols const ( VMESS Protocol = "vmess" VLESS Protocol = "vless" Tunnel Protocol = "tunnel" HTTP Protocol = "http" Trojan Protocol = "trojan" Shadowsocks Protocol = "shadowsocks" Mixed Protocol = "mixed" WireGuard Protocol = "wireguard" ) // User represents a user account in the 3x-ui panel. type User struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` Username string `json:"username"` Password string `json:"password"` } // Inbound represents an Xray inbound configuration with traffic statistics and settings. type Inbound struct { Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier UserId int `json:"-"` // Associated user ID Up int64 `json:"up" form:"up"` // Upload traffic in bytes Down int64 `json:"down" form:"down"` // Download traffic in bytes Total int64 `json:"total" form:"total"` // Total traffic limit in bytes AllTime int64 `json:"allTime" form:"allTime" gorm:"default:0"` // All-time traffic usage Remark string `json:"remark" form:"remark"` // Human-readable remark Enable bool `json:"enable" form:"enable" gorm:"index:idx_enable_traffic_reset,priority:1"` // Whether the inbound is enabled ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp TrafficReset string `json:"trafficReset" form:"trafficReset" gorm:"default:never;index:idx_enable_traffic_reset,priority:2"` // Traffic reset schedule LastTrafficResetTime int64 `json:"lastTrafficResetTime" form:"lastTrafficResetTime" gorm:"default:0"` // Last traffic reset timestamp ClientStats []xray.ClientTraffic `gorm:"foreignKey:InboundId;references:Id" json:"clientStats" form:"clientStats"` // Client traffic statistics // Xray configuration fields Listen string `json:"listen" form:"listen"` Port int `json:"port" form:"port"` Protocol Protocol `json:"protocol" form:"protocol"` Settings string `json:"settings" form:"settings"` StreamSettings string `json:"streamSettings" form:"streamSettings"` Tag string `json:"tag" form:"tag" gorm:"unique"` Sniffing string `json:"sniffing" form:"sniffing"` NodeId *int `json:"nodeId,omitempty" form:"-" gorm:"-"` // Node ID (not stored in Inbound table, from mapping) - DEPRECATED: kept only for backward compatibility with old clients, use NodeIds instead NodeIds []int `json:"nodeIds,omitempty" form:"-" gorm:"-"` // Node IDs array (not stored in Inbound table, from mapping) - use this for multi-node support } // OutboundTraffics tracks traffic statistics for Xray outbound connections. type OutboundTraffics struct { Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Tag string `json:"tag" form:"tag" gorm:"unique"` Up int64 `json:"up" form:"up" gorm:"default:0"` Down int64 `json:"down" form:"down" gorm:"default:0"` Total int64 `json:"total" form:"total" gorm:"default:0"` } // InboundClientIps stores IP addresses associated with inbound clients for access control. type InboundClientIps struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` ClientEmail string `json:"clientEmail" form:"clientEmail" gorm:"unique"` Ips string `json:"ips" form:"ips"` } // HistoryOfSeeders tracks which database seeders have been executed to prevent re-running. type HistoryOfSeeders struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` SeederName string `json:"seederName"` } // GenXrayInboundConfig generates an Xray inbound configuration from the Inbound model. func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig { listen := i.Listen // Default to 0.0.0.0 (all interfaces) when listen is empty // This ensures proper dual-stack IPv4/IPv6 binding in systems where bindv6only=0 if listen == "" { listen = "0.0.0.0" } listen = fmt.Sprintf("\"%v\"", listen) return &xray.InboundConfig{ Listen: json_util.RawMessage(listen), Port: i.Port, Protocol: string(i.Protocol), Settings: json_util.RawMessage(i.Settings), StreamSettings: json_util.RawMessage(i.StreamSettings), Tag: i.Tag, Sniffing: json_util.RawMessage(i.Sniffing), } } // Setting stores key-value configuration settings for the 3x-ui panel. type Setting struct { Id int `json:"id" form:"id" gorm:"primaryKey;autoIncrement"` Key string `json:"key" form:"key"` Value string `json:"value" form:"value"` } // Client represents a client configuration for Xray inbounds with traffic limits and settings. // This is a legacy struct used for JSON parsing from inbound Settings. // For database operations, use ClientEntity instead. type Client struct { ID string `json:"id"` // Unique client identifier Security string `json:"security"` // Security method (e.g., "auto", "aes-128-gcm") Password string `json:"password"` // Client password Flow string `json:"flow"` // Flow control (XTLS) Email string `json:"email"` // Client email identifier LimitIP int `json:"limitIp"` // IP limit for this client TotalGB int64 `json:"totalGB" form:"totalGB"` // Total traffic limit in GB ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp Enable bool `json:"enable" form:"enable"` // Whether the client is enabled TgID int64 `json:"tgId" form:"tgId"` // Telegram user ID for notifications SubID string `json:"subId" form:"subId"` // Subscription identifier Comment string `json:"comment" form:"comment"` // Client comment Reset int `json:"reset" form:"reset"` // Reset period in days CreatedAt int64 `json:"created_at,omitempty"` // Creation timestamp UpdatedAt int64 `json:"updated_at,omitempty"` // Last update timestamp } // ClientEntity represents a client as a separate database entity. // Clients can be assigned to multiple inbounds. type ClientEntity struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier UserId int `json:"userId" gorm:"index"` // Associated user ID Email string `json:"email" form:"email" gorm:"uniqueIndex:idx_user_email"` // Client email identifier (unique per user) UUID string `json:"uuid" form:"uuid"` // UUID/ID for VMESS/VLESS Security string `json:"security" form:"security"` // Security method (e.g., "auto", "aes-128-gcm") Password string `json:"password" form:"password"` // Client password (for Trojan/Shadowsocks) Flow string `json:"flow" form:"flow"` // Flow control (XTLS) LimitIP int `json:"limitIp" form:"limitIp"` // IP limit for this client TotalGB float64 `json:"totalGB" form:"totalGB"` // Total traffic limit in GB (supports decimal values like 0.01 for MB) ExpiryTime int64 `json:"expiryTime" form:"expiryTime"` // Expiration timestamp Enable bool `json:"enable" form:"enable"` // Whether the client is enabled Status string `json:"status" form:"status" gorm:"default:active"` // Client status: active, expired_traffic, expired_time TgID int64 `json:"tgId" form:"tgId"` // Telegram user ID for notifications SubID string `json:"subId" form:"subId" gorm:"index"` // Subscription identifier Comment string `json:"comment" form:"comment"` // Client comment Reset int `json:"reset" form:"reset"` // Reset period in days CreatedAt int64 `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp // Relations (not stored in DB, loaded via joins) InboundIds []int `json:"inboundIds,omitempty" form:"-" gorm:"-"` // Inbound IDs this client is assigned to // Traffic statistics (stored directly in ClientEntity table) Up int64 `json:"up,omitempty" form:"-" gorm:"default:0"` // Upload traffic in bytes Down int64 `json:"down,omitempty" form:"-" gorm:"default:0"` // Download traffic in bytes AllTime int64 `json:"allTime,omitempty" form:"-" gorm:"default:0"` // All-time traffic usage LastOnline int64 `json:"lastOnline,omitempty" form:"-" gorm:"default:0"` // Last online timestamp // HWID (Hardware ID) restrictions HWIDEnabled bool `json:"hwidEnabled" form:"hwidEnabled" gorm:"column:hwid_enabled;default:false"` // Whether HWID restriction is enabled for this client MaxHWID int `json:"maxHwid" form:"maxHwid" gorm:"column:max_hwid;default:1"` // Maximum number of allowed HWID devices (0 = unlimited) HWIDs []*ClientHWID `json:"hwids,omitempty" form:"-" gorm:"-"` // Registered HWIDs for this client (loaded from client_hwids table, not stored in ClientEntity table) } // Node represents a worker node in multi-node architecture. type Node struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier Name string `json:"name" form:"name"` // Node name/identifier Address string `json:"address" form:"address"` // Node API address (e.g., "http://192.168.1.100:8080" or "https://...") ApiKey string `json:"apiKey" form:"apiKey"` // API key for authentication Status string `json:"status" gorm:"default:unknown"` // Status: online, offline, unknown LastCheck int64 `json:"lastCheck" gorm:"default:0"` // Last health check timestamp ResponseTime int64 `json:"responseTime" gorm:"default:0"` // Response time in milliseconds (0 = not measured or error) UseTLS bool `json:"useTls" form:"useTls" gorm:"column:use_tls;default:false"` // Whether to use TLS/HTTPS for API calls CertPath string `json:"certPath" form:"certPath" gorm:"column:cert_path"` // Path to certificate file (optional, for custom CA) KeyPath string `json:"keyPath" form:"keyPath" gorm:"column:key_path"` // Path to private key file (optional, for custom CA) InsecureTLS bool `json:"insecureTls" form:"insecureTls" gorm:"column:insecure_tls;default:false"` // Skip certificate verification (not recommended) CreatedAt int64 `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp } // InboundNodeMapping maps inbounds to nodes in multi-node mode. type InboundNodeMapping struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier InboundId int `json:"inboundId" form:"inboundId" gorm:"uniqueIndex:idx_inbound_node"` // Inbound ID NodeId int `json:"nodeId" form:"nodeId" gorm:"uniqueIndex:idx_inbound_node"` // Node ID } // ClientInboundMapping maps clients to inbounds (many-to-many relationship). type ClientInboundMapping struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier ClientId int `json:"clientId" form:"clientId" gorm:"uniqueIndex:idx_client_inbound"` // Client ID InboundId int `json:"inboundId" form:"inboundId" gorm:"uniqueIndex:idx_client_inbound"` // Inbound ID } // Host represents a proxy/balancer host configuration for multi-node mode. // Hosts can override the node address when generating subscription links. type Host struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier UserId int `json:"userId" gorm:"index"` // Associated user ID Name string `json:"name" form:"name"` // Host name/identifier Address string `json:"address" form:"address"` // Host address (IP or domain) Port int `json:"port" form:"port"` // Host port (0 means use inbound port) Protocol string `json:"protocol" form:"protocol"` // Protocol override (optional) Remark string `json:"remark" form:"remark"` // Host remark/description Enable bool `json:"enable" form:"enable"` // Whether the host is enabled CreatedAt int64 `json:"createdAt" gorm:"autoCreateTime"` // Creation timestamp UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime"` // Last update timestamp // Relations (not stored in DB, loaded via joins) InboundIds []int `json:"inboundIds,omitempty" form:"-" gorm:"-"` // Inbound IDs this host applies to } // HostInboundMapping maps hosts to inbounds (many-to-many relationship). type HostInboundMapping struct { Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier HostId int `json:"hostId" form:"hostId" gorm:"uniqueIndex:idx_host_inbound"` // Host ID InboundId int `json:"inboundId" form:"inboundId" gorm:"uniqueIndex:idx_host_inbound"` // Inbound ID } // ClientHWID represents a hardware ID (HWID) associated with a client. // HWID is provided explicitly by client applications via HTTP headers (x-hwid). // Server MUST NOT generate or derive HWID from IP, User-Agent, or access logs. type ClientHWID struct { // TableName specifies the table name for GORM // GORM by default would use "client_hwids" but the actual table is "client_hw_ids" Id int `json:"id" gorm:"primaryKey;autoIncrement"` // Unique identifier ClientId int `json:"clientId" form:"clientId" gorm:"column:client_id;index:idx_client_hwid"` // Client ID HWID string `json:"hwid" form:"hwid" gorm:"column:hwid;index:idx_client_hwid"` // Hardware ID (unique per client, provided by client via x-hwid header) DeviceName string `json:"deviceName" form:"deviceName" gorm:"column:device_name"` // Optional device name/description (deprecated, use DeviceModel instead) DeviceOS string `json:"deviceOs" form:"deviceOs" gorm:"column:device_os"` // Device operating system (from x-device-os header) DeviceModel string `json:"deviceModel" form:"deviceModel" gorm:"column:device_model"` // Device model (from x-device-model header) OSVersion string `json:"osVersion" form:"osVersion" gorm:"column:os_version"` // OS version (from x-ver-os header) FirstSeenAt int64 `json:"firstSeenAt" gorm:"column:first_seen_at;autoCreateTime"` // First time this HWID was seen (timestamp) LastSeenAt int64 `json:"lastSeenAt" gorm:"column:last_seen_at;autoUpdateTime"` // Last time this HWID was used (timestamp) FirstSeenIP string `json:"firstSeenIp" form:"firstSeenIp" gorm:"column:first_seen_ip"` // IP address when first seen IsActive bool `json:"isActive" form:"isActive" gorm:"column:is_active;default:true"` // Whether this HWID is currently active IPAddress string `json:"ipAddress" form:"ipAddress" gorm:"column:ip_address"` // Last known IP address for this HWID UserAgent string `json:"userAgent" form:"userAgent" gorm:"column:user_agent"` // User agent or client identifier (if available) BlockedAt *int64 `json:"blockedAt,omitempty" form:"blockedAt" gorm:"column:blocked_at"` // Timestamp when HWID was blocked (null if not blocked) BlockReason string `json:"blockReason,omitempty" form:"blockReason" gorm:"column:block_reason"` // Reason for blocking (e.g., "HWID limit exceeded") // Legacy fields (deprecated, kept for backward compatibility) FirstSeen int64 `json:"firstSeen,omitempty" gorm:"-"` // Deprecated: use FirstSeenAt LastSeen int64 `json:"lastSeen,omitempty" gorm:"-"` // Deprecated: use LastSeenAt } // TableName specifies the table name for ClientHWID. // GORM by default would use "client_hwids" but the actual table is "client_hw_ids" func (ClientHWID) TableName() string { return "client_hw_ids" }