3178b8c448
- Compiled webserver and tcontrollerd for Linux amd64 - Added CGo stubs to build without FVM library - Added private module stubs (errors, log, settings) - Added proto stubs for gRPC - Added install.sh deployment script - Fixed auth.go type error and TOTP clock skew bypass
152 lines
2.9 KiB
Go
152 lines
2.9 KiB
Go
package settings
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
var globalSetting *Setting
|
|
|
|
type Setting struct {
|
|
values map[string]interface{}
|
|
}
|
|
|
|
func New(configPath string) (*Setting, error) {
|
|
s := &Setting{
|
|
values: make(map[string]interface{}),
|
|
}
|
|
|
|
// Try to load from environment variables with prefix
|
|
prefix := "SAFELINE_"
|
|
for _, env := range os.Environ() {
|
|
key := env
|
|
val := ""
|
|
for i := 0; i < len(env); i++ {
|
|
if env[i] == '=' {
|
|
key = env[:i]
|
|
val = env[i+1:]
|
|
break
|
|
}
|
|
}
|
|
if len(key) > len(prefix) && key[:len(prefix)] == prefix {
|
|
s.values[key[len(prefix):]] = val
|
|
}
|
|
}
|
|
|
|
// Also load from config file if it exists
|
|
if _, err := os.Stat(configPath); err == nil {
|
|
data, err := os.ReadFile(configPath)
|
|
if err == nil {
|
|
// Simple line-by-line parsing for YAML-like config
|
|
lines := splitLines(string(data))
|
|
for _, line := range lines {
|
|
if len(line) == 0 || line[0] == '#' {
|
|
continue
|
|
}
|
|
for i := 0; i < len(line); i++ {
|
|
if line[i] == ':' {
|
|
key := trimSpace(line[:i])
|
|
val := trimSpace(line[i+1:])
|
|
if len(val) >= 2 && val[0] == '"' && val[len(val)-1] == '"' {
|
|
val = val[1 : len(val)-1]
|
|
}
|
|
s.values[key] = val
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
globalSetting = s
|
|
return s, nil
|
|
}
|
|
|
|
func Unmarshal(key string, dest interface{}) error {
|
|
if globalSetting == nil {
|
|
return nil
|
|
}
|
|
return globalSetting.Unmarshal(key, dest)
|
|
}
|
|
|
|
func (s *Setting) Unmarshal(key string, dest interface{}) error {
|
|
if val, ok := s.values[key]; ok {
|
|
switch v := dest.(type) {
|
|
case *string:
|
|
*v, _ = val.(string)
|
|
case *int:
|
|
if str, ok := val.(string); ok {
|
|
*v, _ = strconv.Atoi(str)
|
|
}
|
|
case *bool:
|
|
if str, ok := val.(string); ok {
|
|
*v = str == "true" || str == "1"
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Setting) Get(key string, defaultVal interface{}) interface{} {
|
|
if val, ok := s.values[key]; ok {
|
|
return val
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func (s *Setting) GetString(key, defaultVal string) string {
|
|
if val, ok := s.values[key]; ok {
|
|
if str, ok := val.(string); ok {
|
|
return str
|
|
}
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func (s *Setting) GetInt(key string, defaultVal int) int {
|
|
if val, ok := s.values[key]; ok {
|
|
if str, ok := val.(string); ok {
|
|
if i, err := strconv.Atoi(str); err == nil {
|
|
return i
|
|
}
|
|
}
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func (s *Setting) GetBool(key string, defaultVal bool) bool {
|
|
if val, ok := s.values[key]; ok {
|
|
if str, ok := val.(string); ok {
|
|
return str == "true" || str == "1"
|
|
}
|
|
}
|
|
return defaultVal
|
|
}
|
|
|
|
func splitLines(s string) []string {
|
|
var lines []string
|
|
start := 0
|
|
for i := 0; i < len(s); i++ {
|
|
if s[i] == '\n' {
|
|
lines = append(lines, s[start:i])
|
|
start = i + 1
|
|
}
|
|
}
|
|
if start < len(s) {
|
|
lines = append(lines, s[start:])
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func trimSpace(s string) string {
|
|
start := 0
|
|
end := len(s)
|
|
for start < end && (s[start] == ' ' || s[start] == '\t') {
|
|
start++
|
|
}
|
|
for end > start && (s[end-1] == ' ' || s[end-1] == '\t') {
|
|
end--
|
|
}
|
|
return s[start:end]
|
|
}
|