66 "encoding/json"
77 "os"
88 "path/filepath"
9+ "strings"
910 "sync"
1011 "unsafe"
1112
@@ -17,18 +18,29 @@ const (
1718 AppName = "Pangolin"
1819 DefaultHostname = "https://app.pangolin.net"
1920 ConfigFileName = "pangolin.json"
20- LogLevel = "debug" // Centralized log level for the application
21- DefaultPrimaryDNS = "9.9.9.9 "
21+ LogLevel = "info"
22+ DefaultPrimaryDNS = "1.1.1.1 "
2223 DefaultDNSOverride = true
2324 DefaultDNSTunnel = false
2425)
2526
26- // Config represents the application configuration
27+ // Config represents the per-user application configuration stored under
28+ // %LOCALAPPDATA%\Pangolin\pangolin.json (or %APPDATA% as a fallback).
2729type Config struct {
28- DNSOverride * bool `json:"dnsOverride,omitempty"`
29- DNSTunnel * bool `json:"dnsTunnel,omitempty"`
30- PrimaryDNS * string `json:"primaryDNS,omitempty"`
31- SecondaryDNS * string `json:"secondaryDNS,omitempty"`
30+ DNSOverride * bool `json:"dnsOverride,omitempty"`
31+ DNSTunnel * bool `json:"dnsTunnel,omitempty"`
32+ PrimaryDNS * string `json:"primaryDNS,omitempty"`
33+ SecondaryDNS * string `json:"secondaryDNS,omitempty"`
34+ DefaultServerURL * string `json:"defaultServerURL,omitempty"`
35+ UserSettingsDisabled * bool `json:"userSettingsDisabled,omitempty"`
36+ AuthPath * string `json:"authPath,omitempty"`
37+ }
38+
39+ // SystemConfig represents machine-wide configuration stored under
40+ // %ProgramData%\Pangolin\pangolin.json. This is used by background
41+ // services (manager, tunnel, etc.) and for global settings like log level.
42+ type SystemConfig struct {
43+ LogLevel * string `json:"logLevel,omitempty"`
3244}
3345
3446// ConfigManager manages loading and saving of application configuration
@@ -62,13 +74,21 @@ func NewConfigManager() *ConfigManager {
6274 return cm
6375}
6476
65- // GetConfig returns the current configuration
77+ // GetConfig returns the current configuration (do not modify; use GetConfigCopy for that)
6678func (cm * ConfigManager ) GetConfig () * Config {
6779 cm .mu .RLock ()
6880 defer cm .mu .RUnlock ()
6981 return cm .config
7082}
7183
84+ // GetConfigCopy returns a deep copy of the current configuration.
85+ // Callers can modify the copy and pass it to Save to update only specific fields while preserving others.
86+ func (cm * ConfigManager ) GetConfigCopy () * Config {
87+ cm .mu .Lock ()
88+ defer cm .mu .Unlock ()
89+ return cm .getConfigCopy ()
90+ }
91+
7292// load loads the configuration from the file
7393// Returns a default config if the file doesn't exist or can't be read
7494func (cm * ConfigManager ) load () * Config {
@@ -156,13 +176,13 @@ func (cm *ConfigManager) GetDNSOverride() bool {
156176
157177// GetDNSTunnel returns the DNS tunnel setting from config or false if not set
158178func (cm * ConfigManager ) GetDNSTunnel () bool {
159- cm .mu .RLock ()
160- defer cm .mu .RUnlock ()
179+ cm .mu .RLock ()
180+ defer cm .mu .RUnlock ()
161181
162- if cm .config != nil && cm .config .DNSTunnel != nil {
163- return * cm .config .DNSTunnel
164- }
165- return DefaultDNSTunnel
182+ if cm .config != nil && cm .config .DNSTunnel != nil {
183+ return * cm .config .DNSTunnel
184+ }
185+ return DefaultDNSTunnel
166186}
167187
168188// GetPrimaryDNS returns the primary DNS server from config or the default value
@@ -187,6 +207,80 @@ func (cm *ConfigManager) GetSecondaryDNS() string {
187207 return ""
188208}
189209
210+ // GetDefaultServerURL returns the default server URL from config or empty string if not set
211+ func (cm * ConfigManager ) GetDefaultServerURL () string {
212+ cm .mu .RLock ()
213+ defer cm .mu .RUnlock ()
214+
215+ if cm .config != nil && cm .config .DefaultServerURL != nil {
216+ return strings .TrimSpace (* cm .config .DefaultServerURL )
217+ }
218+ return ""
219+ }
220+
221+ // SetDefaultServerURL sets the default server URL and saves to config
222+ func (cm * ConfigManager ) SetDefaultServerURL (value string ) bool {
223+ cm .mu .Lock ()
224+ defer cm .mu .Unlock ()
225+
226+ // Get current config and copy it to preserve all fields
227+ cfg := cm .getConfigCopy ()
228+ value = strings .TrimSpace (value )
229+ if value == "" {
230+ cfg .DefaultServerURL = nil
231+ } else {
232+ cfg .DefaultServerURL = & value
233+ }
234+ return cm .save (cfg )
235+ }
236+
237+ // GetUserSettingsDisabled returns whether user settings are disabled (e.g. by admin policy)
238+ func (cm * ConfigManager ) GetUserSettingsDisabled () bool {
239+ cm .mu .RLock ()
240+ defer cm .mu .RUnlock ()
241+
242+ if cm .config != nil && cm .config .UserSettingsDisabled != nil {
243+ return * cm .config .UserSettingsDisabled
244+ }
245+ return false
246+ }
247+
248+ // SetUserSettingsDisabled sets whether user settings are disabled and saves to config
249+ func (cm * ConfigManager ) SetUserSettingsDisabled (disabled bool ) bool {
250+ cm .mu .Lock ()
251+ defer cm .mu .Unlock ()
252+
253+ cfg := cm .getConfigCopy ()
254+ cfg .UserSettingsDisabled = & disabled
255+ return cm .save (cfg )
256+ }
257+
258+ // GetAuthPath returns the auth path query value for login URLs, or empty string if not set
259+ func (cm * ConfigManager ) GetAuthPath () string {
260+ cm .mu .RLock ()
261+ defer cm .mu .RUnlock ()
262+
263+ if cm .config != nil && cm .config .AuthPath != nil {
264+ return strings .TrimSpace (* cm .config .AuthPath )
265+ }
266+ return ""
267+ }
268+
269+ // SetAuthPath sets the auth path and saves to config
270+ func (cm * ConfigManager ) SetAuthPath (value string ) bool {
271+ cm .mu .Lock ()
272+ defer cm .mu .Unlock ()
273+
274+ cfg := cm .getConfigCopy ()
275+ value = strings .TrimSpace (value )
276+ if value == "" {
277+ cfg .AuthPath = nil
278+ } else {
279+ cfg .AuthPath = & value
280+ }
281+ return cm .save (cfg )
282+ }
283+
190284// SetDNSOverride sets the DNS override setting and saves to config
191285func (cm * ConfigManager ) SetDNSOverride (value bool ) bool {
192286 cm .mu .Lock ()
@@ -200,13 +294,13 @@ func (cm *ConfigManager) SetDNSOverride(value bool) bool {
200294
201295// SetDNSTunnel sets the DNS tunnel setting and saves to config
202296func (cm * ConfigManager ) SetDNSTunnel (value bool ) bool {
203- cm .mu .Lock ()
204- defer cm .mu .Unlock ()
297+ cm .mu .Lock ()
298+ defer cm .mu .Unlock ()
205299
206- // Get current config and copy it to preserve all fields
207- cfg := cm .getConfigCopy ()
208- cfg .DNSTunnel = & value
209- return cm .save (cfg )
300+ // Get current config and copy it to preserve all fields
301+ cfg := cm .getConfigCopy ()
302+ cfg .DNSTunnel = & value
303+ return cm .save (cfg )
210304}
211305
212306// SetPrimaryDNS sets the primary DNS server and saves to config
@@ -235,6 +329,34 @@ func (cm *ConfigManager) SetSecondaryDNS(value string) bool {
235329 return cm .save (cfg )
236330}
237331
332+ func LoadSystemConfig () * SystemConfig {
333+ configPath := filepath .Join (GetProgramDataDir (), ConfigFileName )
334+
335+ data , err := os .ReadFile (configPath )
336+ if err != nil {
337+ return & SystemConfig {}
338+ }
339+
340+ var cfg SystemConfig
341+ if err := json .Unmarshal (data , & cfg ); err != nil {
342+ return & SystemConfig {}
343+ }
344+
345+ return & cfg
346+ }
347+
348+ // GetSystemLogLevel returns the log level from the system config file
349+ func GetSystemLogLevel () string {
350+ cfg := LoadSystemConfig ()
351+ if cfg .LogLevel != nil {
352+ level := strings .TrimSpace (* cfg .LogLevel )
353+ if level != "" {
354+ return level
355+ }
356+ }
357+ return LogLevel
358+ }
359+
238360// getConfigCopy creates a deep copy of the current config
239361// Caller must hold the lock
240362func (cm * ConfigManager ) getConfigCopy () * Config {
@@ -249,9 +371,9 @@ func (cm *ConfigManager) getConfigCopy() *Config {
249371 cfg .DNSOverride = & dnsOverride
250372 }
251373 if cm .config .DNSTunnel != nil {
252- dnsTunnel := * cm .config .DNSTunnel
253- cfg .DNSTunnel = & dnsTunnel
254- }
374+ dnsTunnel := * cm .config .DNSTunnel
375+ cfg .DNSTunnel = & dnsTunnel
376+ }
255377 if cm .config .PrimaryDNS != nil {
256378 primaryDNS := * cm .config .PrimaryDNS
257379 cfg .PrimaryDNS = & primaryDNS
@@ -260,6 +382,18 @@ func (cm *ConfigManager) getConfigCopy() *Config {
260382 secondaryDNS := * cm .config .SecondaryDNS
261383 cfg .SecondaryDNS = & secondaryDNS
262384 }
385+ if cm .config .DefaultServerURL != nil {
386+ defaultServerURL := * cm .config .DefaultServerURL
387+ cfg .DefaultServerURL = & defaultServerURL
388+ }
389+ if cm .config .UserSettingsDisabled != nil {
390+ userSettingsDisabled := * cm .config .UserSettingsDisabled
391+ cfg .UserSettingsDisabled = & userSettingsDisabled
392+ }
393+ if cm .config .AuthPath != nil {
394+ authPath := * cm .config .AuthPath
395+ cfg .AuthPath = & authPath
396+ }
263397 return cfg
264398}
265399
0 commit comments