platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
124
selective-vpn-api/app/vpn_locations_cache_store.go
Normal file
124
selective-vpn-api/app/vpn_locations_cache_store.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func cloneVPNLocations(in []vpnLocationItem) []vpnLocationItem {
|
||||
if len(in) == 0 {
|
||||
return []vpnLocationItem{}
|
||||
}
|
||||
out := make([]vpnLocationItem, 0, len(in))
|
||||
for _, row := range in {
|
||||
item, ok := normalizeVPNLocationItem(row)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func equalVPNLocations(a, b []vpnLocationItem) bool {
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i].ISO != b[i].ISO || a[i].Label != b[i].Label || a[i].Target != b[i].Target {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func loadVPNLocationsCache(path string) ([]vpnLocationItem, time.Time, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
|
||||
var disk vpnLocationsCacheDisk
|
||||
if err := json.Unmarshal(data, &disk); err != nil {
|
||||
return nil, time.Time{}, err
|
||||
}
|
||||
|
||||
var updatedAt time.Time
|
||||
if ts := strings.TrimSpace(disk.UpdatedAt); ts != "" {
|
||||
parsed, parseErr := time.Parse(time.RFC3339, ts)
|
||||
if parseErr == nil {
|
||||
updatedAt = parsed
|
||||
}
|
||||
}
|
||||
|
||||
return cloneVPNLocations(disk.Locations), updatedAt, nil
|
||||
}
|
||||
|
||||
func saveVPNLocationsCache(path string, locs []vpnLocationItem, updatedAt time.Time) error {
|
||||
if err := os.MkdirAll(stateDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
disk := vpnLocationsCacheDisk{
|
||||
Locations: cloneVPNLocations(locs),
|
||||
}
|
||||
if !updatedAt.IsZero() {
|
||||
disk.UpdatedAt = updatedAt.UTC().Format(time.RFC3339)
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(disk, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmp := path + ".tmp"
|
||||
if err := os.WriteFile(tmp, data, 0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmp, path)
|
||||
}
|
||||
|
||||
func normalizeVPNLocationItem(in vpnLocationItem) (vpnLocationItem, bool) {
|
||||
iso := strings.ToUpper(strings.TrimSpace(in.ISO))
|
||||
label := strings.TrimSpace(in.Label)
|
||||
target := strings.TrimSpace(in.Target)
|
||||
|
||||
if iso == "" || label == "" {
|
||||
return vpnLocationItem{}, false
|
||||
}
|
||||
|
||||
if target == "" {
|
||||
target = inferVPNLocationTargetFromLabel(label, iso)
|
||||
}
|
||||
if target == "" {
|
||||
target = iso
|
||||
}
|
||||
|
||||
return vpnLocationItem{
|
||||
Label: label,
|
||||
ISO: iso,
|
||||
Target: target,
|
||||
}, true
|
||||
}
|
||||
|
||||
func inferVPNLocationTargetFromLabel(label, iso string) string {
|
||||
trimmed := strings.TrimSpace(label)
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
if idx := strings.LastIndex(trimmed, "("); idx > 0 && strings.HasSuffix(trimmed, " ms)") {
|
||||
trimmed = strings.TrimSpace(trimmed[:idx])
|
||||
}
|
||||
|
||||
prefix := strings.ToUpper(strings.TrimSpace(iso))
|
||||
if prefix != "" {
|
||||
withSpace := prefix + " "
|
||||
if strings.HasPrefix(strings.ToUpper(trimmed), withSpace) {
|
||||
trimmed = strings.TrimSpace(trimmed[len(withSpace):])
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(trimmed)
|
||||
}
|
||||
Reference in New Issue
Block a user