package node import ( "encoding/json" "time" "github.com/perfect-panel/server/pkg/logger" "github.com/pkg/errors" "gorm.io/gorm" ) type Server struct { Id int64 `gorm:"primary_key"` Name string `gorm:"type:varchar(100);not null;default:'';comment:Server Name"` Country string `gorm:"type:varchar(128);not null;default:'';comment:Country"` City string `gorm:"type:varchar(128);not null;default:'';comment:City"` Ratio float32 `gorm:"type:DECIMAL(4,2);not null;default:0;comment:Traffic Ratio"` Address string `gorm:"type:varchar(100);not null;default:'';comment:Server Address"` Sort int `gorm:"type:int;not null;default:0;comment:Sort"` Protocols string `gorm:"type:text;default:null;comment:Protocol"` LastReportedAt *time.Time `gorm:"comment:Last Reported Time"` CreatedAt time.Time `gorm:"<-:create;comment:Creation Time"` UpdatedAt time.Time `gorm:"comment:Update Time"` } func (*Server) TableName() string { return "servers" } func (m *Server) BeforeCreate(tx *gorm.DB) error { if m.Sort == 0 { var maxSort int if err := tx.Model(&Server{}).Select("COALESCE(MAX(sort), 0)").Scan(&maxSort).Error; err != nil { return err } m.Sort = maxSort + 1 } return nil } func (m *Server) BeforeDelete(tx *gorm.DB) error { if err := tx.Exec("UPDATE `servers` SET sort = sort - 1 WHERE sort > ?", m.Sort).Error; err != nil { return err } return nil } func (m *Server) BeforeUpdate(tx *gorm.DB) error { var count int64 if err := tx.Set("gorm:query_option", "FOR UPDATE").Model(&Server{}). Where("sort = ? AND id != ?", m.Sort, m.Id).Count(&count).Error; err != nil { return err } if count > 1 { // reorder sort if err := reorderSortWithServer(tx); err != nil { logger.Errorf("[Server] BeforeUpdate reorderSort error: %v", err.Error()) return err } // get max sort var maxSort int if err := tx.Model(&Server{}).Select("MAX(sort)").Scan(&maxSort).Error; err != nil { return err } m.Sort = maxSort + 1 } return nil } // MarshalProtocols Marshal server protocols to json func (m *Server) MarshalProtocols(list []Protocol) error { var validate = make(map[string]bool) for _, protocol := range list { if protocol.Type == "" { return errors.New("protocol type is required") } if _, exists := validate[protocol.Type]; exists { return errors.New("duplicate protocol type: " + protocol.Type) } validate[protocol.Type] = true } data, err := json.Marshal(list) if err != nil { return err } m.Protocols = string(data) return nil } // UnmarshalProtocols Unmarshal server protocols from json func (m *Server) UnmarshalProtocols() ([]Protocol, error) { var list []Protocol if m.Protocols == "" { return list, nil } err := json.Unmarshal([]byte(m.Protocols), &list) if err != nil { return nil, err } return list, nil } type Protocol struct { Type string `json:"type"` Port uint16 `json:"port"` Enable bool `json:"enable"` Security string `json:"security,omitempty"` SNI string `json:"sni,omitempty"` AllowInsecure bool `json:"allow_insecure,omitempty"` Fingerprint string `json:"fingerprint,omitempty"` RealityServerAddr string `json:"reality_server_addr,omitempty"` RealityServerPort int `json:"reality_server_port,omitempty"` RealityPrivateKey string `json:"reality_private_key,omitempty"` RealityPublicKey string `json:"reality_public_key,omitempty"` RealityShortId string `json:"reality_short_id,omitempty"` Transport string `json:"transport,omitempty"` Host string `json:"host,omitempty"` Path string `json:"path,omitempty"` ServiceName string `json:"service_name,omitempty"` Cipher string `json:"cipher,omitempty"` ServerKey string `json:"server_key,omitempty"` Flow string `json:"flow,omitempty"` HopPorts string `json:"hop_ports,omitempty"` HopInterval int `json:"hop_interval,omitempty"` ObfsPassword string `json:"obfs_password,omitempty"` DisableSNI bool `json:"disable_sni,omitempty"` ReduceRtt bool `json:"reduce_rtt,omitempty"` UDPRelayMode string `json:"udp_relay_mode,omitempty"` CongestionController string `json:"congestion_controller,omitempty"` Multiplex string `json:"multiplex,omitempty"` // mux, eg: off/low/medium/high PaddingScheme string `json:"padding_scheme,omitempty"` // padding scheme UpMbps int `json:"up_mbps,omitempty"` // upload speed limit DownMbps int `json:"down_mbps,omitempty"` // download speed limit Obfs string `json:"obfs,omitempty"` // obfs, 'none', 'http', 'tls' ObfsHost string `json:"obfs_host,omitempty"` // obfs host ObfsPath string `json:"obfs_path,omitempty"` // obfs path XhttpMode string `json:"xhttp_mode,omitempty"` // xhttp mode XhttpExtra string `json:"xhttp_extra,omitempty"` // xhttp extra path Encryption string `json:"encryption,omitempty"` // encryption,'none', 'mlkem768x25519plus' EncryptionMode string `json:"encryption_mode,omitempty"` // encryption mode,'native', 'xorpub', 'random' EncryptionRtt string `json:"encryption_rtt,omitempty"` // encryption rtt,'0rtt', '1rtt' EncryptionTicket string `json:"encryption_ticket,omitempty"` // encryption ticket EncryptionServerPadding string `json:"encryption_server_padding,omitempty"` // encryption server padding EncryptionPrivateKey string `json:"encryption_private_key,omitempty"` // encryption private key EncryptionClientPadding string `json:"encryption_client_padding,omitempty"` // encryption client padding EncryptionPassword string `json:"encryption_password,omitempty"` // encryption password } // Marshal protocol to json func (m *Protocol) Marshal() ([]byte, error) { type Alias Protocol return json.Marshal(&struct { *Alias }{ Alias: (*Alias)(m), }) } // Unmarshal json to protocol func (m *Protocol) Unmarshal(data []byte) error { type Alias Protocol aux := &struct { *Alias }{ Alias: (*Alias)(m), } return json.Unmarshal(data, &aux) } func reorderSortWithServer(tx *gorm.DB) error { var servers []Server if err := tx.Order("sort, id").Find(&servers).Error; err != nil { return err } for i, server := range servers { if server.Sort != i+1 { if err := tx.Exec("UPDATE `servers` SET sort = ? WHERE id = ?", i+1, server.Id).Error; err != nil { return err } } } return nil }