platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
138
selective-vpn-api/app/transport_singbox_profiles_eval.go
Normal file
138
selective-vpn-api/app/transport_singbox_profiles_eval.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
transportcfgpkg "selective-vpn-api/app/transportcfg"
|
||||
)
|
||||
|
||||
type singBoxProfileEvalResult struct {
|
||||
Valid bool
|
||||
Config map[string]any
|
||||
Digest string
|
||||
Diff SingBoxProfileRenderDiff
|
||||
Changed bool
|
||||
Errors []SingBoxProfileIssue
|
||||
Warnings []SingBoxProfileIssue
|
||||
}
|
||||
|
||||
func evaluateSingBoxProfile(profile SingBoxProfile, checkBinary bool) singBoxProfileEvalResult {
|
||||
deps := transportcfgpkg.SingBoxProfileEvalDeps{
|
||||
NormalizeMode: func(mode string) (string, bool) {
|
||||
nm, ok := normalizeSingBoxProfileMode(SingBoxProfileMode(mode))
|
||||
return string(nm), ok
|
||||
},
|
||||
CloneMapDeep: cloneMapDeep,
|
||||
AsString: asString,
|
||||
ProtocolSupported: transportcfgpkg.SingBoxTypedProtocolSupported,
|
||||
ParsePort: transportcfgpkg.ParsePort,
|
||||
}
|
||||
in := transportcfgpkg.SingBoxProfileInput{
|
||||
ID: profile.ID,
|
||||
Mode: string(profile.Mode),
|
||||
Protocol: profile.Protocol,
|
||||
RawConfig: profile.RawConfig,
|
||||
Typed: profile.Typed,
|
||||
}
|
||||
|
||||
pkgErrs, pkgWarns := transportcfgpkg.ValidateSingBoxProfile(in, deps)
|
||||
errs := singBoxIssuesFromPkg(pkgErrs)
|
||||
warns := singBoxIssuesFromPkg(pkgWarns)
|
||||
rendered, renderErr := transportcfgpkg.RenderSingBoxProfileConfig(in, deps)
|
||||
if renderErr == nil {
|
||||
rendered = transportcfgpkg.NormalizeSingBoxRenderedConfig(rendered, asString)
|
||||
}
|
||||
if renderErr != nil {
|
||||
errs = append(errs, SingBoxProfileIssue{
|
||||
Field: "profile",
|
||||
Severity: "error",
|
||||
Code: "SINGBOX_RENDER_INVALID",
|
||||
Message: renderErr.Error(),
|
||||
})
|
||||
}
|
||||
if checkBinary && renderErr == nil {
|
||||
if issue := validateSingBoxWithBinary(rendered); issue != nil {
|
||||
if issue.Severity == "warning" {
|
||||
warns = append(warns, *issue)
|
||||
} else {
|
||||
errs = append(errs, *issue)
|
||||
}
|
||||
}
|
||||
}
|
||||
valid := len(errs) == 0
|
||||
diff := SingBoxProfileRenderDiff{}
|
||||
digest := ""
|
||||
changed := false
|
||||
if rendered != nil {
|
||||
digest = transportcfgpkg.DigestJSONMap(rendered)
|
||||
prev := transportcfgpkg.ReadJSONMapFile(singBoxRenderedPath(profile.ID))
|
||||
pkgDiff, cfgChanged := transportcfgpkg.DiffConfigMaps(prev, rendered)
|
||||
diff = SingBoxProfileRenderDiff{
|
||||
Added: pkgDiff.Added,
|
||||
Removed: pkgDiff.Removed,
|
||||
Changed: pkgDiff.Changed,
|
||||
}
|
||||
changed = cfgChanged
|
||||
}
|
||||
return singBoxProfileEvalResult{
|
||||
Valid: valid,
|
||||
Config: rendered,
|
||||
Digest: digest,
|
||||
Diff: diff,
|
||||
Changed: changed,
|
||||
Errors: errs,
|
||||
Warnings: warns,
|
||||
}
|
||||
}
|
||||
|
||||
func singBoxIssuesFromPkg(in []transportcfgpkg.SingBoxIssue) []SingBoxProfileIssue {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]SingBoxProfileIssue, 0, len(in))
|
||||
for _, it := range in {
|
||||
out = append(out, SingBoxProfileIssue{
|
||||
Field: it.Field,
|
||||
Severity: it.Severity,
|
||||
Code: it.Code,
|
||||
Message: it.Message,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func validateSingBoxWithBinary(config map[string]any) *SingBoxProfileIssue {
|
||||
issue := transportcfgpkg.ValidateRenderedConfigWithBinary(
|
||||
config,
|
||||
runCommandTimeout,
|
||||
5*time.Second,
|
||||
"/usr/local/bin/sing-box",
|
||||
"/usr/bin/sing-box",
|
||||
"sing-box",
|
||||
)
|
||||
if issue == nil {
|
||||
return nil
|
||||
}
|
||||
return &SingBoxProfileIssue{
|
||||
Field: issue.Field,
|
||||
Severity: issue.Severity,
|
||||
Code: issue.Code,
|
||||
Message: issue.Message,
|
||||
}
|
||||
}
|
||||
|
||||
func writeSingBoxRenderedConfig(profileID string, config map[string]any) (string, error) {
|
||||
path := singBoxRenderedPath(profileID)
|
||||
if err := transportcfgpkg.WriteJSONConfigFile(path, config); err != nil {
|
||||
return path, err
|
||||
}
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func singBoxRenderedPath(profileID string) string {
|
||||
return transportcfgpkg.ProfileConfigPath(singBoxRenderedRootDir, profileID, sanitizeID)
|
||||
}
|
||||
|
||||
func defaultSingBoxAppliedConfigPath(profileID string) string {
|
||||
return transportcfgpkg.ProfileConfigPath(singBoxAppliedRootDir, profileID, sanitizeID)
|
||||
}
|
||||
Reference in New Issue
Block a user