From 9cc970a3ea0ce93bdb3ea214ba168bd0c661ffff Mon Sep 17 00:00:00 2001 From: Steven Date: Wed, 21 Jan 2026 08:02:25 +0800 Subject: [PATCH] chore: fix data directory handling --- internal/profile/profile.go | 30 ++++++++++++++++-------------- scripts/Dockerfile | 8 +++++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/internal/profile/profile.go b/internal/profile/profile.go index eb8d9764a..30579a313 100644 --- a/internal/profile/profile.go +++ b/internal/profile/profile.go @@ -37,8 +37,9 @@ type Profile struct { func checkDataDir(dataDir string) (string, error) { // Convert to absolute path if relative path is supplied. if !filepath.IsAbs(dataDir) { - relativeDir := filepath.Join(filepath.Dir(os.Args[0]), dataDir) - absDir, err := filepath.Abs(relativeDir) + // Use current working directory, not the binary's directory + // This ensures we use the actual working directory where the process runs + absDir, err := filepath.Abs(dataDir) if err != nil { return "", err } @@ -56,23 +57,24 @@ func checkDataDir(dataDir string) (string, error) { func (p *Profile) Validate() error { // Set default data directory if not specified if p.Data == "" { - if p.Demo { - // In demo mode, use current directory - p.Data = "." + if runtime.GOOS == "windows" { + p.Data = filepath.Join(os.Getenv("ProgramData"), "memos") } else { - // In production mode, use system directory - if runtime.GOOS == "windows" { - p.Data = filepath.Join(os.Getenv("ProgramData"), "memos") - } else { - // On Linux/macOS, check if /var/opt/memos exists (Docker scenario) - // If not, fall back to current directory to avoid permission issues - if _, err := os.Stat("/var/opt/memos"); err == nil { + // On Linux/macOS, check if /var/opt/memos exists and is writable (Docker scenario) + if info, err := os.Stat("/var/opt/memos"); err == nil && info.IsDir() { + // Check if we can write to this directory + testFile := filepath.Join("/var/opt/memos", ".write-test") + if err := os.WriteFile(testFile, []byte("test"), 0600); err == nil { + os.Remove(testFile) p.Data = "/var/opt/memos" } else { - slog.Warn("default production data directory /var/opt/memos not accessible, using current directory. " + - "Consider using --data flag to specify a data directory.") + // /var/opt/memos exists but is not writable, use current directory + slog.Warn("/var/opt/memos is not writable, using current directory") p.Data = "." } + } else { + // /var/opt/memos doesn't exist, use current directory (local development) + p.Data = "." } } } diff --git a/scripts/Dockerfile b/scripts/Dockerfile index d8b85cf12..9aad358f6 100644 --- a/scripts/Dockerfile +++ b/scripts/Dockerfile @@ -27,22 +27,24 @@ RUN --mount=type=cache,target=/go/pkg/mod \ # Use minimal Alpine with security updates FROM alpine:3.21 AS monolithic -WORKDIR /usr/local/memos # Install runtime dependencies and create non-root user in single layer RUN apk add --no-cache tzdata ca-certificates && \ addgroup -g 10001 -S nonroot && \ adduser -u 10001 -S -G nonroot -h /var/opt/memos nonroot && \ - mkdir -p /var/opt/memos && \ + mkdir -p /var/opt/memos /usr/local/memos && \ chown -R nonroot:nonroot /var/opt/memos -# Copy binary and entrypoint +# Copy binary and entrypoint to /usr/local/memos COPY --from=backend /backend-build/memos /usr/local/memos/memos COPY --from=backend --chmod=755 /backend-build/scripts/entrypoint.sh /usr/local/memos/entrypoint.sh # Switch to non-root user USER nonroot:nonroot +# Set working directory to the writable volume +WORKDIR /var/opt/memos + # Data directory VOLUME /var/opt/memos