mirror of
https://github.com/qdm12/gluetun.git
synced 2026-05-07 20:40:13 +02:00
25f67cd170
- new directory structure containing manifest.json and one json file per provider, by default. - the manifest.json file can specify a filepath for each vpn provider - each vpn provider json data file can contain the `"preferred": true` field to enforce it is used even if outdated, unless there is a version mismatch - `STORAGE_SERVERS_DIRECTORY_PATH` replaces `STORAGE_FILEPATH` (which is now a migration source only). It sets the directory where server manifest and per-provider JSON files are stored (default: `/gluetun/servers/`). - First-run migration: On startup, gluetun checks for the old /gluetun/servers.json file; if found and no new manifest exists, it automatically migrates all data to /gluetun/servers/ directory structure - Silent fallback: If legacy file isn't found, uses the new directory path normally - Legacy cleanup: After successful migration, attempts to remove the old fat JSON file (logs warning only if removal fails, e.g., read-only bind mounts) Co-authored-by: Copilot <copilot@github.com>
71 lines
1.9 KiB
Go
71 lines
1.9 KiB
Go
package storage
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"github.com/qdm12/gluetun/internal/models"
|
|
)
|
|
|
|
type Storage struct {
|
|
mergedServers models.AllServers
|
|
mergedMutex sync.RWMutex
|
|
// this is stored in memory to avoid re-parsing
|
|
// the embedded JSON file on every call to the
|
|
// SyncServers method.
|
|
hardcodedServers models.AllServers
|
|
logger Logger
|
|
directoryPath string
|
|
legacyFilepath string
|
|
}
|
|
|
|
const manifestFilename = "manifest.json"
|
|
|
|
type Logger interface {
|
|
Info(s string)
|
|
Infof(format string, args ...any)
|
|
Warn(s string)
|
|
}
|
|
|
|
// New creates a new storage and reads the servers from the
|
|
// embedded servers files and the files on disk.
|
|
// Passing an empty directoryPath disables the reading and writing of
|
|
// servers.
|
|
func New(logger Logger, directoryPath, legacyFilepath string) (storage *Storage, err error) {
|
|
// A unit test prevents [parseHardcodedServers] from ever failing,
|
|
// and ensures all providers are part of the servers returned.
|
|
hardcodedServers := parseHardcodedServers()
|
|
|
|
storage = &Storage{
|
|
hardcodedServers: hardcodedServers,
|
|
mergedServers: hardcodedServers,
|
|
logger: logger,
|
|
directoryPath: directoryPath,
|
|
legacyFilepath: legacyFilepath,
|
|
}
|
|
|
|
if directoryPath != "" {
|
|
if err := storage.syncServers(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return storage, nil
|
|
}
|
|
|
|
// hasLegacy returns true if the legacy file `legacyFilepath` exists AND is
|
|
// different from the manifest file defined by `directoryPath`/[manifestFilename].
|
|
// This is used to determine if the legacy file should be read and removed after flushing servers data.
|
|
func (s *Storage) hasLegacy() bool {
|
|
if s.legacyFilepath == "" {
|
|
return false
|
|
}
|
|
if filepath.Clean(filepath.Join(s.directoryPath, manifestFilename)) ==
|
|
filepath.Clean(s.legacyFilepath) {
|
|
return false
|
|
}
|
|
stat, err := os.Stat(s.legacyFilepath)
|
|
return err == nil && !stat.IsDir()
|
|
}
|