|
@@ -181,7 +181,7 @@ func runSeeders(isUsersEmpty bool) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if empty && isUsersEmpty {
|
|
if empty && isUsersEmpty {
|
|
|
- seeders := []string{"UserPasswordHash", "ClientsTable", "InboundClientsArrayFix", "InboundClientTgIdFix", "InboundClientSubIdFix"}
|
|
|
|
|
|
|
+ seeders := []string{"UserPasswordHash", "ClientsTable", "InboundClientsArrayFix", "InboundClientTgIdFix", "InboundClientSubIdFix", "FreedomFinalRulesReverseFix"}
|
|
|
for _, name := range seeders {
|
|
for _, name := range seeders {
|
|
|
if err := db.Create(&model.HistoryOfSeeders{SeederName: name}).Error; err != nil {
|
|
if err := db.Create(&model.HistoryOfSeeders{SeederName: name}).Error; err != nil {
|
|
|
return err
|
|
return err
|
|
@@ -255,6 +255,12 @@ func runSeeders(isUsersEmpty bool) error {
|
|
|
return err
|
|
return err
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ if !slices.Contains(seedersHistory, "FreedomFinalRulesReverseFix") {
|
|
|
|
|
+ if err := normalizeFreedomFinalRules(); err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -401,6 +407,101 @@ func normalizeInboundClientsArray() error {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+func normalizeFreedomFinalRules() error {
|
|
|
|
|
+ var setting model.Setting
|
|
|
|
|
+ err := db.Model(model.Setting{}).Where("key = ?", "xrayTemplateConfig").First(&setting).Error
|
|
|
|
|
+ if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
|
|
|
+ return db.Create(&model.HistoryOfSeeders{SeederName: "FreedomFinalRulesReverseFix"}).Error
|
|
|
|
|
+ }
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ updated, changed, rErr := rewriteFreedomFinalRules(setting.Value)
|
|
|
|
|
+ if rErr != nil {
|
|
|
|
|
+ log.Printf("FreedomFinalRulesReverseFix: skip (invalid xrayTemplateConfig json): %v", rErr)
|
|
|
|
|
+ return db.Create(&model.HistoryOfSeeders{SeederName: "FreedomFinalRulesReverseFix"}).Error
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return db.Transaction(func(tx *gorm.DB) error {
|
|
|
|
|
+ if changed {
|
|
|
|
|
+ if err := tx.Model(&model.Setting{}).Where("key = ?", "xrayTemplateConfig").
|
|
|
|
|
+ Update("value", updated).Error; err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return tx.Create(&model.HistoryOfSeeders{SeederName: "FreedomFinalRulesReverseFix"}).Error
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func rewriteFreedomFinalRules(raw string) (string, bool, error) {
|
|
|
|
|
+ if strings.TrimSpace(raw) == "" {
|
|
|
|
|
+ return raw, false, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ var cfg map[string]any
|
|
|
|
|
+ if err := json.Unmarshal([]byte(raw), &cfg); err != nil {
|
|
|
|
|
+ return raw, false, err
|
|
|
|
|
+ }
|
|
|
|
|
+ outbounds, ok := cfg["outbounds"].([]any)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return raw, false, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ changed := false
|
|
|
|
|
+ for _, ob := range outbounds {
|
|
|
|
|
+ obj, ok := ob.(map[string]any)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if proto, _ := obj["protocol"].(string); proto != "freedom" {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ settings, ok := obj["settings"].(map[string]any)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ if !isLegacyPrivateOnlyFinalRules(settings["finalRules"]) {
|
|
|
|
|
+ continue
|
|
|
|
|
+ }
|
|
|
|
|
+ settings["finalRules"] = []any{map[string]any{"action": "allow"}}
|
|
|
|
|
+ changed = true
|
|
|
|
|
+ }
|
|
|
|
|
+ if !changed {
|
|
|
|
|
+ return raw, false, nil
|
|
|
|
|
+ }
|
|
|
|
|
+ out, err := json.MarshalIndent(cfg, "", " ")
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return raw, false, err
|
|
|
|
|
+ }
|
|
|
|
|
+ return string(out), true, nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func isLegacyPrivateOnlyFinalRules(v any) bool {
|
|
|
|
|
+ rules, ok := v.([]any)
|
|
|
|
|
+ if !ok || len(rules) != 1 {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ rule, ok := rules[0].(map[string]any)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if action, _ := rule["action"].(string); action != "allow" {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ ips, ok := rule["ip"].([]any)
|
|
|
|
|
+ if !ok || len(ips) != 1 {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if s, _ := ips[0].(string); s != "geoip:private" {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ for k := range rule {
|
|
|
|
|
+ if k != "action" && k != "ip" {
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// normalizeClientJSONFields coerces loosely-typed numeric fields in a raw
|
|
// normalizeClientJSONFields coerces loosely-typed numeric fields in a raw
|
|
|
// settings.clients entry so json.Unmarshal into model.Client doesn't fail
|
|
// settings.clients entry so json.Unmarshal into model.Client doesn't fail
|
|
|
// when older rows wrote tgId/limitIp/totalGB/etc. as strings. Empty strings
|
|
// when older rows wrote tgId/limitIp/totalGB/etc. as strings. Empty strings
|