From d16599fa1d58edcd54df2b9260011458831f2aa5 Mon Sep 17 00:00:00 2001 From: hkfires <10558748+hkfires@users.noreply.github.com> Date: Sun, 19 Oct 2025 10:19:55 +0800 Subject: [PATCH] feat: prefer util.WritablePath() for logs and local storage --- cmd/server/main.go | 25 +++++++++++++++++++----- internal/api/handlers/management/logs.go | 4 ++++ internal/api/server.go | 12 ++++++++++-- internal/logging/global_logger.go | 6 +++++- internal/managementasset/updater.go | 4 ++++ internal/util/util.go | 14 +++++++++++++ 6 files changed, 57 insertions(+), 8 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index d710825e..a84d2d21 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -147,6 +147,7 @@ func main() { } return "", false } + writableBase := util.WritablePath() if value, ok := lookupEnv("PGSTORE_DSN", "pgstore_dsn"); ok { usePostgresStore = true pgStoreDSN = value @@ -158,6 +159,13 @@ func main() { if value, ok := lookupEnv("PGSTORE_LOCAL_PATH", "pgstore_local_path"); ok { pgStoreLocalPath = value } + if pgStoreLocalPath == "" { + if writableBase != "" { + pgStoreLocalPath = writableBase + } else { + pgStoreLocalPath = wd + } + } useGitStore = false } if value, ok := lookupEnv("GITSTORE_GIT_URL", "gitstore_git_url"); ok { @@ -229,11 +237,14 @@ func main() { log.Infof("postgres-backed token store enabled, workspace path: %s", pgStoreInst.WorkDir()) } } else if useObjectStore { - objectStoreRoot := objectStoreLocalPath - if objectStoreRoot == "" { - objectStoreRoot = wd + if objectStoreLocalPath == "" { + if writableBase != "" { + objectStoreLocalPath = writableBase + } else { + objectStoreLocalPath = wd + } } - objectStoreRoot = filepath.Join(objectStoreRoot, "objectstore") + objectStoreRoot := filepath.Join(objectStoreLocalPath, "objectstore") resolvedEndpoint := strings.TrimSpace(objectStoreEndpoint) useSSL := true if strings.Contains(resolvedEndpoint, "://") { @@ -289,7 +300,11 @@ func main() { } } else if useGitStore { if gitStoreLocalPath == "" { - gitStoreLocalPath = wd + if writableBase != "" { + gitStoreLocalPath = writableBase + } else { + gitStoreLocalPath = wd + } } gitStoreRoot = filepath.Join(gitStoreLocalPath, "gitstore") authDir := filepath.Join(gitStoreRoot, "auths") diff --git a/internal/api/handlers/management/logs.go b/internal/api/handlers/management/logs.go index 9f7f904f..4aba9992 100644 --- a/internal/api/handlers/management/logs.go +++ b/internal/api/handlers/management/logs.go @@ -13,6 +13,7 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/util" ) const ( @@ -145,6 +146,9 @@ func (h *Handler) logDirectory() string { if h.logDir != "" { return h.logDir } + if base := util.WritablePath(); base != "" { + return filepath.Join(base, "logs") + } if h.configFilePath != "" { dir := filepath.Dir(h.configFilePath) if dir != "" && dir != "." { diff --git a/internal/api/server.go b/internal/api/server.go index 4a99316f..6c8915ca 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -52,7 +52,11 @@ type serverOptionConfig struct { type ServerOption func(*serverOptionConfig) func defaultRequestLoggerFactory(cfg *config.Config, configPath string) logging.RequestLogger { - return logging.NewFileRequestLogger(cfg.RequestLog, "logs", filepath.Dir(configPath)) + configDir := filepath.Dir(configPath) + if base := util.WritablePath(); base != "" { + return logging.NewFileRequestLogger(cfg.RequestLog, filepath.Join(base, "logs"), configDir) + } + return logging.NewFileRequestLogger(cfg.RequestLog, "logs", configDir) } // WithMiddleware appends additional Gin middleware during server construction. @@ -233,7 +237,11 @@ func NewServer(cfg *config.Config, authManager *auth.Manager, accessManager *sdk if optionState.localPassword != "" { s.mgmt.SetLocalPassword(optionState.localPassword) } - s.mgmt.SetLogDirectory(filepath.Join(s.currentPath, "logs")) + logDir := filepath.Join(s.currentPath, "logs") + if base := util.WritablePath(); base != "" { + logDir = filepath.Join(base, "logs") + } + s.mgmt.SetLogDirectory(logDir) s.localPassword = optionState.localPassword // Setup routes diff --git a/internal/logging/global_logger.go b/internal/logging/global_logger.go index 75505343..9d4e1fc9 100644 --- a/internal/logging/global_logger.go +++ b/internal/logging/global_logger.go @@ -10,6 +10,7 @@ import ( "sync" "github.com/gin-gonic/gin" + "github.com/router-for-me/CLIProxyAPI/v6/internal/util" log "github.com/sirupsen/logrus" "gopkg.in/natefinch/lumberjack.v2" ) @@ -72,7 +73,10 @@ func ConfigureLogOutput(loggingToFile bool) error { defer writerMu.Unlock() if loggingToFile { - const logDir = "logs" + logDir := "logs" + if base := util.WritablePath(); base != "" { + logDir = filepath.Join(base, "logs") + } if err := os.MkdirAll(logDir, 0o755); err != nil { return fmt.Errorf("logging: failed to create log directory: %w", err) } diff --git a/internal/managementasset/updater.go b/internal/managementasset/updater.go index 7fd9f59c..22aa9dea 100644 --- a/internal/managementasset/updater.go +++ b/internal/managementasset/updater.go @@ -64,6 +64,10 @@ func StaticDir(configFilePath string) string { return cleaned } + if writable := util.WritablePath(); writable != "" { + return filepath.Join(writable, "static") + } + configFilePath = strings.TrimSpace(configFilePath) if configFilePath == "" { return "" diff --git a/internal/util/util.go b/internal/util/util.go index d14f1637..17536ac1 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -84,3 +84,17 @@ func CountAuthFiles(authDir string) int { } return count } + +// WritablePath returns the cleaned WRITABLE_PATH environment variable when it is set. +// It accepts both uppercase and lowercase variants for compatibility with existing conventions. +func WritablePath() string { + for _, key := range []string{"WRITABLE_PATH", "writable_path"} { + if value, ok := os.LookupEnv(key); ok { + trimmed := strings.TrimSpace(value) + if trimmed != "" { + return filepath.Clean(trimmed) + } + } + } + return "" +}