Files
elmprodvpn/selective-vpn-api/app/vpn_locations_cache_store.go

125 lines
2.6 KiB
Go

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)
}