package config import ( "fmt" "os" "path" "path/filepath" "runtime" "strings" log "github.com/sirupsen/logrus" ) type Config struct { // Server Config Version string ListenPort string // DB Configuration DBType string DBName string // Data Paths ConfigPath string DataPath string // Miscellaneous Settings RegistrationEnabled bool SearchEnabled bool DemoMode bool LogLevel string // Cookie Settings CookieAuthKey string CookieEncKey string CookieSecure bool CookieHTTPOnly bool } type customFormatter struct { log.Formatter } // Force UTC & Set type (app) func (cf customFormatter) Format(e *log.Entry) ([]byte, error) { if e.Data["type"] == nil { e.Data["type"] = "app" } e.Time = e.Time.UTC() return cf.Formatter.Format(e) } // Set at runtime var version string = "develop" func Load() *Config { c := &Config{ Version: version, ConfigPath: getEnv("CONFIG_PATH", "/config"), DataPath: getEnv("DATA_PATH", "/data"), ListenPort: getEnv("LISTEN_PORT", "8585"), DBType: trimLowerString(getEnv("DATABASE_TYPE", "SQLite")), DBName: trimLowerString(getEnv("DATABASE_NAME", "antholume")), RegistrationEnabled: trimLowerString(getEnv("REGISTRATION_ENABLED", "false")) == "true", DemoMode: trimLowerString(getEnv("DEMO_MODE", "false")) == "true", SearchEnabled: trimLowerString(getEnv("SEARCH_ENABLED", "false")) == "true", CookieAuthKey: trimLowerString(getEnv("COOKIE_AUTH_KEY", "")), CookieEncKey: trimLowerString(getEnv("COOKIE_ENC_KEY", "")), LogLevel: trimLowerString(getEnv("LOG_LEVEL", "info")), CookieSecure: trimLowerString(getEnv("COOKIE_SECURE", "true")) == "true", CookieHTTPOnly: trimLowerString(getEnv("COOKIE_HTTP_ONLY", "true")) == "true", } // Parse log level logLevel, err := log.ParseLevel(c.LogLevel) if err != nil { logLevel = log.InfoLevel } // Create custom formatter logFormatter := &customFormatter{&log.JSONFormatter{ CallerPrettyfier: prettyCaller, }} // Create log rotator rotateFileHook, err := NewRotateFileHook(RotateFileConfig{ Filename: path.Join(c.ConfigPath, "logs/antholume.log"), MaxSize: 50, MaxBackups: 3, MaxAge: 30, Level: logLevel, Formatter: logFormatter, }) if err != nil { log.Fatal("Unable to initialize file rotate hook") } // Rotate now rotateFileHook.Rotate() // Set logger settings log.SetLevel(logLevel) log.SetFormatter(logFormatter) log.SetReportCaller(true) log.AddHook(rotateFileHook) // Ensure directories exist c.EnsureDirectories() return c } // Ensures needed directories exist func (c *Config) EnsureDirectories() { os.Mkdir(c.ConfigPath, 0755) os.Mkdir(c.DataPath, 0755) docDir := filepath.Join(c.DataPath, "documents") coversDir := filepath.Join(c.DataPath, "covers") backupDir := filepath.Join(c.DataPath, "backups") os.Mkdir(docDir, 0755) os.Mkdir(coversDir, 0755) os.Mkdir(backupDir, 0755) } func getEnv(key, fallback string) string { if value, ok := os.LookupEnv(key); ok { return value } return fallback } func trimLowerString(val string) string { return strings.ToLower(strings.TrimSpace(val)) } func prettyCaller(f *runtime.Frame) (function string, file string) { purgePrefix := "reichard.io/antholume/" pathName := strings.Replace(f.Func.Name(), purgePrefix, "", 1) parts := strings.Split(pathName, ".") filepath, line := f.Func.FileLine(f.PC) splitFilePath := strings.Split(filepath, "/") fileName := fmt.Sprintf("%s/%s@%d", parts[0], splitFilePath[len(splitFilePath)-1], line) functionName := strings.Replace(pathName, parts[0]+".", "", 1) // Exclude GIN Logger if functionName == "NewApi.apiLogger.func1" { fileName = "" functionName = "" } return functionName, fileName }