mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 23:56:37 +08:00
[优化] Phase2
This commit is contained in:
@@ -34,6 +34,7 @@ func registeredModels() []any {
|
||||
&ProxyRoute{},
|
||||
&ConfigVersion{},
|
||||
&Node{},
|
||||
&Tunnel{},
|
||||
&NodeSystemProfile{},
|
||||
&ApplyLog{},
|
||||
&NodeMetricSnapshot{},
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// v16 升级内容:新增 Tunnel 隧道表,为 nodes 增加 TunnelRelay 相关字段,为 proxy_routes 增加隧道上游支持字段。
|
||||
// 背景说明:引入 Tunnel 组件后,边缘节点可通过隧道中继回源,需要区分节点类型(edge_node/relay_node)、记录中继绑定端口,
|
||||
// 并在代理路由中支持 tunnel 上游类型以替代直连回源,同时需要独立的 tunnels 表管理隧道实例。
|
||||
package migrate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type nodeV16 struct {
|
||||
NodeType string `gorm:"column:node_type;not null;default:'edge_node'"`
|
||||
RelayBindPort int `gorm:"column:relay_bind_port"`
|
||||
}
|
||||
|
||||
type tunnelV16 struct{}
|
||||
|
||||
type proxyRouteV16 struct {
|
||||
UpstreamType string `gorm:"column:upstream_type;not null;default:'direct'"`
|
||||
TunnelID *uint `gorm:"column:tunnel_id"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
Register(V16())
|
||||
}
|
||||
|
||||
func V16() Migration {
|
||||
return Migration{
|
||||
FromVersion: 15,
|
||||
ToVersion: 16,
|
||||
Migrate: migrateV16,
|
||||
Validate: validateV16,
|
||||
}
|
||||
}
|
||||
|
||||
func (nodeV16) TableName() string {
|
||||
return "nodes"
|
||||
}
|
||||
|
||||
func (tunnelV16) TableName() string {
|
||||
return "tunnels"
|
||||
}
|
||||
|
||||
func (proxyRouteV16) TableName() string {
|
||||
return "proxy_routes"
|
||||
}
|
||||
|
||||
func migrateV16(ctx Context, db *gorm.DB, backend string) error {
|
||||
if err := ctx.ApplyCurrentSchema(db, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := db.Exec("UPDATE nodes SET node_type = 'edge_node' WHERE node_type = '' OR node_type IS NULL").Error; err != nil {
|
||||
return fmt.Errorf("backfill nodes.node_type: %w", err)
|
||||
}
|
||||
if err := db.Exec("UPDATE proxy_routes SET upstream_type = 'direct' WHERE upstream_type = '' OR upstream_type IS NULL").Error; err != nil {
|
||||
return fmt.Errorf("backfill proxy_routes.upstream_type: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateV16(ctx Context, db *gorm.DB, backend string) error {
|
||||
if err := ctx.ValidateDatabaseSchemaVersion(db, backend, 15); err != nil {
|
||||
return err
|
||||
}
|
||||
if db == nil || !db.Migrator().HasTable(&tunnelV16{}) {
|
||||
return fmt.Errorf("table tunnels is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&nodeV16{}, "node_type") {
|
||||
return fmt.Errorf("column nodes.node_type is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&proxyRouteV16{}, "upstream_type") {
|
||||
return fmt.Errorf("column proxy_routes.upstream_type is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&nodeV16{}, "relay_bind_port") {
|
||||
return fmt.Errorf("column nodes.relay_bind_port is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&proxyRouteV16{}, "tunnel_id") {
|
||||
return fmt.Errorf("column proxy_routes.tunnel_id is missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -74,6 +74,10 @@ func (databaseSchemaMigrationContext) ValidateDatabaseSchemaVersion(db *gorm.DB,
|
||||
return validateDatabaseSchemaV13(db, backend)
|
||||
case 14:
|
||||
return validateDatabaseSchemaV14(db, backend)
|
||||
case 15:
|
||||
return validateDatabaseSchemaV15(db, backend)
|
||||
case 16:
|
||||
return validateDatabaseSchemaV16(db, backend)
|
||||
default:
|
||||
return fmt.Errorf("database schema validation for v%d is not defined", version)
|
||||
}
|
||||
@@ -1140,6 +1144,32 @@ func validateDatabaseSchemaV14(db *gorm.DB, backend string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDatabaseSchemaV15(db *gorm.DB, backend string) error {
|
||||
if err := validateDatabaseSchemaV14(db, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
if !db.Migrator().HasColumn(&Node{}, "ip_manual_override") {
|
||||
return fmt.Errorf("column nodes.ip_manual_override is missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateDatabaseSchemaV16(db *gorm.DB, backend string) error {
|
||||
if err := validateDatabaseSchemaV15(db, backend); err != nil {
|
||||
return err
|
||||
}
|
||||
if !db.Migrator().HasTable(&Tunnel{}) {
|
||||
return fmt.Errorf("table tunnels is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&Node{}, "node_type") {
|
||||
return fmt.Errorf("column nodes.node_type is missing")
|
||||
}
|
||||
if !db.Migrator().HasColumn(&ProxyRoute{}, "upstream_type") {
|
||||
return fmt.Errorf("column proxy_routes.upstream_type is missing")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func databaseSchemaMigrations() []databaseSchemaMigration {
|
||||
ctx := databaseSchemaMigrationContext{}
|
||||
migrations := []databaseSchemaMigration{}
|
||||
|
||||
@@ -28,6 +28,18 @@ type Node struct {
|
||||
LastError string `json:"last_error" gorm:"type:text"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
// Node type: edge_node (default) | tunnel_relay
|
||||
NodeType string `json:"node_type" gorm:"size:32;not null;default:'edge_node'"`
|
||||
// TunnelRelay specific fields
|
||||
RelayBindPort int `json:"relay_bind_port" gorm:"not null;default:0"`
|
||||
RelayVhostHTTPPort int `json:"relay_vhost_http_port" gorm:"not null;default:0"`
|
||||
RelayAuthToken string `json:"-" gorm:"size:128"`
|
||||
RelayAgentAccessAddr string `json:"relay_agent_access_addr" gorm:"size:255"`
|
||||
RelayClientAccessAddr string `json:"relay_client_access_addr" gorm:"size:255"`
|
||||
RelayClientProxyURL string `json:"relay_client_proxy_url" gorm:"size:512"`
|
||||
RelayStatus string `json:"relay_status" gorm:"size:16;not null;default:'unknown'"`
|
||||
RelayFrpVersion string `json:"relay_frp_version" gorm:"size:64"`
|
||||
RelayVersion string `json:"relay_version" gorm:"size:64"`
|
||||
}
|
||||
|
||||
func ListNodes() (nodes []*Node, err error) {
|
||||
@@ -72,3 +84,8 @@ func (node *Node) Update() error {
|
||||
func (node *Node) Delete() error {
|
||||
return DB.Delete(node).Error
|
||||
}
|
||||
|
||||
func ListNodesByType(nodeType string) (nodes []*Node, err error) {
|
||||
err = DB.Where("node_type = ?", nodeType).Order("id desc").Find(&nodes).Error
|
||||
return nodes, err
|
||||
}
|
||||
|
||||
@@ -3,35 +3,39 @@ package model
|
||||
import "time"
|
||||
|
||||
type ProxyRoute struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
SiteName string `json:"site_name" gorm:"size:255;not null;default:''"`
|
||||
Domain string `json:"domain" gorm:"uniqueIndex;size:255;not null"`
|
||||
Domains string `json:"domains" gorm:"type:text;not null;default:'[]'"`
|
||||
OriginID *uint `json:"origin_id" gorm:"index"`
|
||||
OriginURL string `json:"origin_url" gorm:"size:2048;not null"`
|
||||
OriginHost string `json:"origin_host" gorm:"size:255"`
|
||||
Upstreams string `json:"upstreams" gorm:"type:text;not null;default:'[]'"`
|
||||
Enabled bool `json:"enabled" gorm:"not null;default:true"`
|
||||
EnableHTTPS bool `json:"enable_https" gorm:"column:enable_https;not null;default:false"`
|
||||
CertID *uint `json:"cert_id"`
|
||||
CertIDs string `json:"cert_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
DomainCertIDs string `json:"domain_cert_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
RedirectHTTP bool `json:"redirect_http" gorm:"not null;default:false"`
|
||||
LimitConnPerServer int `json:"limit_conn_per_server" gorm:"not null;default:0"`
|
||||
LimitConnPerIP int `json:"limit_conn_per_ip" gorm:"not null;default:0"`
|
||||
LimitRate string `json:"limit_rate" gorm:"size:32;not null;default:''"`
|
||||
CacheEnabled bool `json:"cache_enabled" gorm:"not null;default:false"`
|
||||
CachePolicy string `json:"cache_policy" gorm:"size:32;not null;default:''"`
|
||||
CacheRules string `json:"cache_rules" gorm:"type:text;not null;default:'[]'"`
|
||||
CustomHeaders string `json:"custom_headers" gorm:"type:text;not null;default:'[]'"`
|
||||
PoWEnabled bool `json:"pow_enabled" gorm:"column:pow_enabled;not null;default:false"`
|
||||
PoWConfig string `json:"pow_config" gorm:"column:pow_config;type:text;not null;default:'{}'"`
|
||||
BasicAuthEnabled bool `json:"basic_auth_enabled" gorm:"not null;default:false"`
|
||||
BasicAuthUsername string `json:"basic_auth_username" gorm:"size:255;not null;default:''"`
|
||||
BasicAuthPassword string `json:"basic_auth_password" gorm:"size:255;not null;default:''"`
|
||||
Remark string `json:"remark" gorm:"size:255"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
SiteName string `json:"site_name" gorm:"size:255;not null;default:''"`
|
||||
Domain string `json:"domain" gorm:"uniqueIndex;size:255;not null"`
|
||||
Domains string `json:"domains" gorm:"type:text;not null;default:'[]'"`
|
||||
OriginID *uint `json:"origin_id" gorm:"index"`
|
||||
OriginURL string `json:"origin_url" gorm:"size:2048;not null"`
|
||||
OriginHost string `json:"origin_host" gorm:"size:255"`
|
||||
Upstreams string `json:"upstreams" gorm:"type:text;not null;default:'[]'"`
|
||||
Enabled bool `json:"enabled" gorm:"not null;default:true"`
|
||||
EnableHTTPS bool `json:"enable_https" gorm:"column:enable_https;not null;default:false"`
|
||||
CertID *uint `json:"cert_id"`
|
||||
CertIDs string `json:"cert_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
DomainCertIDs string `json:"domain_cert_ids" gorm:"type:text;not null;default:'[]'"`
|
||||
RedirectHTTP bool `json:"redirect_http" gorm:"not null;default:false"`
|
||||
LimitConnPerServer int `json:"limit_conn_per_server" gorm:"not null;default:0"`
|
||||
LimitConnPerIP int `json:"limit_conn_per_ip" gorm:"not null;default:0"`
|
||||
LimitRate string `json:"limit_rate" gorm:"size:32;not null;default:''"`
|
||||
CacheEnabled bool `json:"cache_enabled" gorm:"not null;default:false"`
|
||||
CachePolicy string `json:"cache_policy" gorm:"size:32;not null;default:''"`
|
||||
CacheRules string `json:"cache_rules" gorm:"type:text;not null;default:'[]'"`
|
||||
CustomHeaders string `json:"custom_headers" gorm:"type:text;not null;default:'[]'"`
|
||||
PoWEnabled bool `json:"pow_enabled" gorm:"column:pow_enabled;not null;default:false"`
|
||||
PoWConfig string `json:"pow_config" gorm:"column:pow_config;type:text;not null;default:'{}'"`
|
||||
BasicAuthEnabled bool `json:"basic_auth_enabled" gorm:"not null;default:false"`
|
||||
BasicAuthUsername string `json:"basic_auth_username" gorm:"size:255;not null;default:''"`
|
||||
BasicAuthPassword string `json:"basic_auth_password" gorm:"size:255;not null;default:''"`
|
||||
Remark string `json:"remark" gorm:"size:255"`
|
||||
UpstreamType string `json:"upstream_type" gorm:"size:32;not null;default:'direct'"`
|
||||
TunnelID *uint `json:"tunnel_id" gorm:"index"`
|
||||
TunnelTargetAddr string `json:"tunnel_target_addr" gorm:"size:512"`
|
||||
TunnelTargetProtocol string `json:"tunnel_target_protocol" gorm:"size:16"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func ListProxyRoutes() (routes []*ProxyRoute, err error) {
|
||||
@@ -61,32 +65,36 @@ func (route *ProxyRoute) Insert() error {
|
||||
|
||||
func (route *ProxyRoute) Update() error {
|
||||
return DB.Model(&ProxyRoute{}).Where("id = ?", route.ID).Updates(map[string]any{
|
||||
"site_name": route.SiteName,
|
||||
"domain": route.Domain,
|
||||
"domains": route.Domains,
|
||||
"origin_id": route.OriginID,
|
||||
"origin_url": route.OriginURL,
|
||||
"origin_host": route.OriginHost,
|
||||
"upstreams": route.Upstreams,
|
||||
"enabled": route.Enabled,
|
||||
"enable_https": route.EnableHTTPS,
|
||||
"cert_id": route.CertID,
|
||||
"cert_ids": route.CertIDs,
|
||||
"domain_cert_ids": route.DomainCertIDs,
|
||||
"redirect_http": route.RedirectHTTP,
|
||||
"limit_conn_per_server": route.LimitConnPerServer,
|
||||
"limit_conn_per_ip": route.LimitConnPerIP,
|
||||
"limit_rate": route.LimitRate,
|
||||
"cache_enabled": route.CacheEnabled,
|
||||
"cache_policy": route.CachePolicy,
|
||||
"cache_rules": route.CacheRules,
|
||||
"custom_headers": route.CustomHeaders,
|
||||
"pow_enabled": route.PoWEnabled,
|
||||
"pow_config": route.PoWConfig,
|
||||
"basic_auth_enabled": route.BasicAuthEnabled,
|
||||
"basic_auth_username": route.BasicAuthUsername,
|
||||
"basic_auth_password": route.BasicAuthPassword,
|
||||
"remark": route.Remark,
|
||||
"site_name": route.SiteName,
|
||||
"domain": route.Domain,
|
||||
"domains": route.Domains,
|
||||
"origin_id": route.OriginID,
|
||||
"origin_url": route.OriginURL,
|
||||
"origin_host": route.OriginHost,
|
||||
"upstreams": route.Upstreams,
|
||||
"enabled": route.Enabled,
|
||||
"enable_https": route.EnableHTTPS,
|
||||
"cert_id": route.CertID,
|
||||
"cert_ids": route.CertIDs,
|
||||
"domain_cert_ids": route.DomainCertIDs,
|
||||
"redirect_http": route.RedirectHTTP,
|
||||
"limit_conn_per_server": route.LimitConnPerServer,
|
||||
"limit_conn_per_ip": route.LimitConnPerIP,
|
||||
"limit_rate": route.LimitRate,
|
||||
"cache_enabled": route.CacheEnabled,
|
||||
"cache_policy": route.CachePolicy,
|
||||
"cache_rules": route.CacheRules,
|
||||
"custom_headers": route.CustomHeaders,
|
||||
"pow_enabled": route.PoWEnabled,
|
||||
"pow_config": route.PoWConfig,
|
||||
"basic_auth_enabled": route.BasicAuthEnabled,
|
||||
"basic_auth_username": route.BasicAuthUsername,
|
||||
"basic_auth_password": route.BasicAuthPassword,
|
||||
"remark": route.Remark,
|
||||
"upstream_type": route.UpstreamType,
|
||||
"tunnel_id": route.TunnelID,
|
||||
"tunnel_target_addr": route.TunnelTargetAddr,
|
||||
"tunnel_target_protocol": route.TunnelTargetProtocol,
|
||||
}).Error
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type Tunnel struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
TunnelID string `json:"tunnel_id" gorm:"uniqueIndex;size:64;not null"`
|
||||
Name string `json:"name" gorm:"size:128;not null"`
|
||||
TunnelToken string `json:"-" gorm:"size:128;index"`
|
||||
Status string `json:"status" gorm:"size:16;not null;default:'offline'"`
|
||||
ClientVersion string `json:"client_version" gorm:"size:64"`
|
||||
FrpVersion string `json:"frp_version" gorm:"size:64"`
|
||||
LastSeenAt time.Time `json:"last_seen_at"`
|
||||
LastError string `json:"last_error" gorm:"type:text"`
|
||||
CurrentVersion string `json:"current_version" gorm:"size:32"`
|
||||
CurrentChecksum string `json:"current_checksum" gorm:"size:64"`
|
||||
ConnectedRelays string `json:"connected_relays" gorm:"type:text;not null;default:'[]'"`
|
||||
Remark string `json:"remark" gorm:"size:255"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func ListTunnels() (tunnels []*Tunnel, err error) {
|
||||
err = DB.Order("id desc").Find(&tunnels).Error
|
||||
return tunnels, err
|
||||
}
|
||||
|
||||
func GetTunnelByID(id uint) (*Tunnel, error) {
|
||||
tunnel := &Tunnel{}
|
||||
err := DB.First(tunnel, id).Error
|
||||
return tunnel, err
|
||||
}
|
||||
|
||||
func GetTunnelByTunnelID(tunnelID string) (*Tunnel, error) {
|
||||
tunnel := &Tunnel{}
|
||||
err := DB.Where("tunnel_id = ?", tunnelID).First(tunnel).Error
|
||||
return tunnel, err
|
||||
}
|
||||
|
||||
func GetTunnelByTunnelToken(token string) (*Tunnel, error) {
|
||||
tunnel := &Tunnel{}
|
||||
err := DB.Where("tunnel_token = ?", token).First(tunnel).Error
|
||||
return tunnel, err
|
||||
}
|
||||
|
||||
func (tunnel *Tunnel) Insert() error {
|
||||
return DB.Create(tunnel).Error
|
||||
}
|
||||
|
||||
func (tunnel *Tunnel) Update() error {
|
||||
return DB.Save(tunnel).Error
|
||||
}
|
||||
|
||||
func (tunnel *Tunnel) Delete() error {
|
||||
return DB.Delete(tunnel).Error
|
||||
}
|
||||
Reference in New Issue
Block a user