317 lines
7.6 KiB
Go
317 lines
7.6 KiB
Go
package migration
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"s-ui/database/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type InboundData struct {
|
|
Id uint
|
|
Tag string
|
|
Addrs json.RawMessage
|
|
OutJson json.RawMessage
|
|
}
|
|
|
|
func moveJsonToDb(db *gorm.DB) error {
|
|
binFolderPath := os.Getenv("SUI_BIN_FOLDER")
|
|
if binFolderPath == "" {
|
|
binFolderPath = "bin"
|
|
}
|
|
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
configPath := dir + "/" + binFolderPath + "/config.json"
|
|
if _, err := os.Stat(configPath); errors.Is(err, os.ErrNotExist) {
|
|
return nil
|
|
}
|
|
|
|
data, err := os.ReadFile(configPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var oldConfig map[string]interface{}
|
|
err = json.Unmarshal(data, &oldConfig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldInbounds := oldConfig["inbounds"].([]interface{})
|
|
db.Migrator().DropTable(&model.Inbound{})
|
|
db.AutoMigrate(&model.Inbound{})
|
|
for _, inbound := range oldInbounds {
|
|
inbObj, _ := inbound.(map[string]interface{})
|
|
tag, _ := inbObj["tag"].(string)
|
|
if tlsObj, ok := inbObj["tls"]; ok {
|
|
var tls_id uint
|
|
err = db.Raw("SELECT id FROM tls WHERE inbounds like ?", `%"`+tag+`"%`).Find(&tls_id).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Bind or Create tls_id
|
|
if tls_id > 0 {
|
|
inbObj["tls_id"] = tls_id
|
|
} else {
|
|
tls_server, _ := json.MarshalIndent(tlsObj, "", " ")
|
|
if len(tls_server) > 5 {
|
|
newTls := &model.Tls{
|
|
Name: tag,
|
|
Server: tls_server,
|
|
Client: json.RawMessage("{}"),
|
|
}
|
|
err = db.Create(newTls).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
inbObj["tls_id"] = newTls.Id
|
|
}
|
|
}
|
|
}
|
|
|
|
var inbData InboundData
|
|
db.Raw("select id,addrs,out_json from inbound_data where tag = ?", tag).Find(&inbData)
|
|
if inbData.Id > 0 {
|
|
inbObj["out_json"] = inbData.OutJson
|
|
var addrs []map[string]interface{}
|
|
json.Unmarshal(inbData.Addrs, &addrs)
|
|
for index, addr := range addrs {
|
|
if tlsEnable, ok := addr["tls"].(bool); ok {
|
|
newTls := map[string]interface{}{
|
|
"enabled": tlsEnable,
|
|
}
|
|
if insecure, ok := addr["insecure"].(bool); ok {
|
|
newTls["insecure"] = insecure
|
|
delete(addrs[index], "insecure")
|
|
}
|
|
if sni, ok := addr["server_name"].(string); ok {
|
|
newTls["server_name"] = sni
|
|
delete(addrs[index], "server_name")
|
|
}
|
|
addrs[index]["tls"] = newTls
|
|
}
|
|
}
|
|
inbObj["addrs"] = addrs
|
|
} else {
|
|
inbObj["out_json"] = json.RawMessage("{}")
|
|
inbObj["addrs"] = json.RawMessage("[]")
|
|
}
|
|
// Delete deprecated fields
|
|
delete(inbObj, "sniff")
|
|
delete(inbObj, "sniff_override_destination")
|
|
delete(inbObj, "sniff_timeout")
|
|
delete(inbObj, "domain_strategy")
|
|
inbJson, _ := json.Marshal(inbObj)
|
|
|
|
var newInbound model.Inbound
|
|
err = newInbound.UnmarshalJSON(inbJson)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = db.Create(&newInbound).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
delete(oldConfig, "inbounds")
|
|
|
|
blockOutboundTags := []string{}
|
|
dnsOutboundTags := []string{}
|
|
|
|
oldOutbounds := oldConfig["outbounds"].([]interface{})
|
|
db.Migrator().DropTable(&model.Outbound{}, &model.Endpoint{})
|
|
db.AutoMigrate(&model.Outbound{}, &model.Endpoint{})
|
|
for _, outbound := range oldOutbounds {
|
|
outType, _ := outbound.(map[string]interface{})["type"].(string)
|
|
outboundRaw, _ := json.MarshalIndent(outbound, "", " ")
|
|
if outType == "wireguard" { // Check if it is Entrypoint
|
|
var newEntrypoint model.Endpoint
|
|
err = newEntrypoint.UnmarshalJSON(outboundRaw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = db.Create(&newEntrypoint).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else { // It is Outbound
|
|
var newOutbound model.Outbound
|
|
err = newOutbound.UnmarshalJSON(outboundRaw)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Delete deprecated fields
|
|
if newOutbound.Type == "direct" {
|
|
var options map[string]interface{}
|
|
json.Unmarshal(newOutbound.Options, &options)
|
|
delete(options, "override_address")
|
|
delete(options, "override_port")
|
|
newOutbound.Options, _ = json.Marshal(options)
|
|
}
|
|
|
|
switch newOutbound.Type {
|
|
case "dns":
|
|
dnsOutboundTags = append(dnsOutboundTags, newOutbound.Tag)
|
|
case "block":
|
|
blockOutboundTags = append(blockOutboundTags, newOutbound.Tag)
|
|
default:
|
|
err = db.Create(&newOutbound).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete(oldConfig, "outbounds")
|
|
|
|
// Check routing rules
|
|
if routingRules, ok := oldConfig["route"].(map[string]interface{}); ok {
|
|
if rules, hasRules := routingRules["rules"].([]interface{}); hasRules {
|
|
hasDns := false
|
|
for index, rule := range rules {
|
|
ruleObj, _ := rule.(map[string]interface{})
|
|
isBlock := false
|
|
isDns := false
|
|
outboundTag, _ := ruleObj["outbound"].(string)
|
|
for _, tag := range blockOutboundTags {
|
|
if tag == outboundTag {
|
|
isBlock = true
|
|
delete(ruleObj, "outbound")
|
|
ruleObj["action"] = "reject"
|
|
break
|
|
}
|
|
}
|
|
for _, tag := range dnsOutboundTags {
|
|
if tag == outboundTag {
|
|
isDns = true
|
|
hasDns = true
|
|
delete(ruleObj, "outbound")
|
|
ruleObj["action"] = "hijack-dns"
|
|
break
|
|
}
|
|
}
|
|
if !isBlock && !isDns {
|
|
ruleObj["action"] = "route"
|
|
}
|
|
rules[index] = ruleObj
|
|
}
|
|
if hasDns {
|
|
rules = append(rules, map[string]interface{}{"action": "sniff"})
|
|
}
|
|
routingRules["rules"] = rules
|
|
}
|
|
oldConfig["route"] = routingRules
|
|
}
|
|
|
|
// Remove v2rayapi and clashapi from experimental config
|
|
experimental := oldConfig["experimental"].(map[string]interface{})
|
|
delete(experimental, "v2ray_api")
|
|
delete(experimental, "clash_api")
|
|
oldConfig["experimental"] = experimental
|
|
|
|
// Save the other configs
|
|
var otherConfigs json.RawMessage
|
|
otherConfigs, err = json.MarshalIndent(oldConfig, "", " ")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return db.Save(&model.Setting{
|
|
Key: "config",
|
|
Value: string(otherConfigs),
|
|
}).Error
|
|
}
|
|
|
|
func migrateTls(db *gorm.DB) error {
|
|
if !db.Migrator().HasColumn(&model.Tls{}, "inbounds") {
|
|
return nil
|
|
}
|
|
err := db.Migrator().DropColumn(&model.Tls{}, "inbounds")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var tlsConfig []model.Tls
|
|
err = db.Model(model.Tls{}).Scan(&tlsConfig).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for index, tls := range tlsConfig {
|
|
var tlsClient map[string]interface{}
|
|
err = json.Unmarshal(tls.Client, &tlsClient)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
for key := range tlsClient {
|
|
switch key {
|
|
case "insecure", "disable_sni", "utls", "ech", "reality":
|
|
continue
|
|
default:
|
|
delete(tlsClient, key)
|
|
}
|
|
}
|
|
tlsConfig[index].Client, _ = json.MarshalIndent(tlsClient, "", " ")
|
|
}
|
|
|
|
return db.Save(&tlsConfig).Error
|
|
}
|
|
|
|
func dropInboundData(db *gorm.DB) error {
|
|
if !db.Migrator().HasTable(&InboundData{}) {
|
|
return nil
|
|
}
|
|
return db.Migrator().DropTable(&InboundData{})
|
|
}
|
|
|
|
func migrateClients(db *gorm.DB) error {
|
|
var oldClients []model.Client
|
|
err := db.Model(model.Client{}).Scan(&oldClients).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for index, oldClient := range oldClients {
|
|
var old_inbounds []string
|
|
err = json.Unmarshal(oldClient.Inbounds, &old_inbounds)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var inbound_ids []uint
|
|
err = db.Raw("SELECT id FROM inbounds WHERE tag in ?", old_inbounds).Find(&inbound_ids).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
oldClients[index].Inbounds, _ = json.Marshal(inbound_ids)
|
|
}
|
|
return db.Save(oldClients).Error
|
|
}
|
|
|
|
func migrateChanges(db *gorm.DB) error {
|
|
return db.Migrator().DropColumn(&model.Changes{}, "index")
|
|
}
|
|
|
|
func to1_2(db *gorm.DB) error {
|
|
err := moveJsonToDb(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = migrateTls(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = dropInboundData(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = migrateClients(db)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return migrateChanges(db)
|
|
}
|