2025-09-20 07:35:50 +00:00
// Package model defines the database models and data structures used by the 3x-ui panel.
2023-02-09 19:18:06 +00:00
package model
import (
"fmt"
2024-03-10 21:31:24 +00:00
2025-09-19 08:05:43 +00:00
"github.com/mhsanaei/3x-ui/v2/util/json_util"
"github.com/mhsanaei/3x-ui/v2/xray"
2023-02-09 19:18:06 +00:00
)
2025-09-20 07:35:50 +00:00
// Protocol represents the protocol type for Xray inbounds.
2023-02-09 19:18:06 +00:00
type Protocol string
2025-09-20 07:35:50 +00:00
// Protocol constants for different Xray inbound protocols
2023-02-09 19:18:06 +00:00
const (
2024-08-10 22:47:44 +00:00
VMESS Protocol = "vmess"
2023-02-09 19:18:06 +00:00
VLESS Protocol = "vless"
2025-09-09 11:57:40 +00:00
Tunnel Protocol = "tunnel"
2024-05-28 13:16:29 +00:00
HTTP Protocol = "http"
2023-02-09 19:18:06 +00:00
Trojan Protocol = "trojan"
Shadowsocks Protocol = "shadowsocks"
2025-09-09 11:57:40 +00:00
Mixed Protocol = "mixed"
2024-05-28 13:16:29 +00:00
WireGuard Protocol = "wireguard"
2023-02-09 19:18:06 +00:00
)
2025-09-20 07:35:50 +00:00
// User represents a user account in the 3x-ui panel.
2023-02-09 19:18:06 +00:00
type User struct {
2025-05-08 14:20:58 +00:00
Id int ` json:"id" gorm:"primaryKey;autoIncrement" `
Username string ` json:"username" `
Password string ` json:"password" `
2023-02-09 19:18:06 +00:00
}
2025-09-20 07:35:50 +00:00
// Inbound represents an Xray inbound configuration with traffic statistics and settings.
2023-02-09 19:18:06 +00:00
type Inbound struct {
2025-09-20 07:35:50 +00:00
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
2023-02-09 19:18:06 +00:00
2025-09-20 07:35:50 +00:00
// Xray configuration fields
2023-02-09 19:18:06 +00:00
Listen string ` json:"listen" form:"listen" `
2024-01-17 12:51:28 +00:00
Port int ` json:"port" form:"port" `
2023-02-09 19:18:06 +00:00
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" `
2026-01-05 21:12:53 +00:00
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
2023-02-09 19:18:06 +00:00
}
2024-01-29 20:37:20 +00:00
2025-09-20 07:35:50 +00:00
// OutboundTraffics tracks traffic statistics for Xray outbound connections.
2024-01-29 20:37:20 +00:00
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" `
}
2025-09-20 07:35:50 +00:00
// InboundClientIps stores IP addresses associated with inbound clients for access control.
2023-02-28 19:54:29 +00:00
type InboundClientIps struct {
2023-04-09 19:43:18 +00:00
Id int ` json:"id" gorm:"primaryKey;autoIncrement" `
2023-02-28 19:54:29 +00:00
ClientEmail string ` json:"clientEmail" form:"clientEmail" gorm:"unique" `
2023-04-09 19:43:18 +00:00
Ips string ` json:"ips" form:"ips" `
2023-02-28 19:54:29 +00:00
}
2023-02-09 19:18:06 +00:00
2025-09-20 07:35:50 +00:00
// HistoryOfSeeders tracks which database seeders have been executed to prevent re-running.
2025-05-03 09:27:53 +00:00
type HistoryOfSeeders struct {
Id int ` json:"id" gorm:"primaryKey;autoIncrement" `
SeederName string ` json:"seederName" `
}
2025-09-20 07:35:50 +00:00
// GenXrayInboundConfig generates an Xray inbound configuration from the Inbound model.
2023-02-09 19:18:06 +00:00
func ( i * Inbound ) GenXrayInboundConfig ( ) * xray . InboundConfig {
listen := i . Listen
2026-01-09 19:22:33 +00:00
// 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"
2023-02-09 19:18:06 +00:00
}
2026-01-09 19:22:33 +00:00
listen = fmt . Sprintf ( "\"%v\"" , listen )
2023-02-09 19:18:06 +00:00
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 ) ,
}
}
2025-09-20 07:35:50 +00:00
// Setting stores key-value configuration settings for the 3x-ui panel.
2023-02-09 19:18:06 +00:00
type Setting struct {
Id int ` json:"id" form:"id" gorm:"primaryKey;autoIncrement" `
Key string ` json:"key" form:"key" `
Value string ` json:"value" form:"value" `
}
2023-02-18 12:37:32 +00:00
2025-09-20 07:35:50 +00:00
// Client represents a client configuration for Xray inbounds with traffic limits and settings.
2026-01-09 12:36:14 +00:00
// This is a legacy struct used for JSON parsing from inbound Settings.
// For database operations, use ClientEntity instead.
2023-02-09 19:18:06 +00:00
type Client struct {
2025-09-20 07:35:50 +00:00
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
2023-02-18 12:37:32 +00:00
}
2026-01-05 21:12:53 +00:00
2026-01-09 12:36:14 +00:00
// 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
2026-01-11 21:12:14 +00:00
TotalGB float64 ` json:"totalGB" form:"totalGB" ` // Total traffic limit in GB (supports decimal values like 0.01 for MB)
2026-01-09 12:36:14 +00:00
ExpiryTime int64 ` json:"expiryTime" form:"expiryTime" ` // Expiration timestamp
Enable bool ` json:"enable" form:"enable" ` // Whether the client is enabled
2026-01-11 21:12:14 +00:00
Status string ` json:"status" form:"status" gorm:"default:active" ` // Client status: active, expired_traffic, expired_time
2026-01-09 12:36:14 +00:00
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
2026-01-11 21:12:14 +00:00
// 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
2026-01-09 12:36:14 +00:00
// 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)
}
2026-01-05 21:12:53 +00:00
// 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
2026-01-10 19:51:10 +00:00
Address string ` json:"address" form:"address" ` // Node API address (e.g., "http://192.168.1.100:8080" or "https://...")
2026-01-05 21:12:53 +00:00
ApiKey string ` json:"apiKey" form:"apiKey" ` // API key for authentication
2026-01-12 02:01:31 +00:00
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
2026-01-10 19:51:10 +00:00
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)
2026-01-05 21:12:53 +00:00
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
2026-01-09 12:36:14 +00:00
}
// 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"
2026-01-05 21:12:53 +00:00
}