139 lines
3.6 KiB
Go
139 lines
3.6 KiB
Go
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)
|
|
}
|