fix: resolves #2990, revert the local storage path to /app/data

This commit is contained in:
Amruth Pillai
2026-05-08 11:10:24 +02:00
parent ed42f181ca
commit 9cbb30d3ba
7 changed files with 26 additions and 6 deletions
+1 -1
View File
@@ -63,7 +63,7 @@ SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
SMTP_SECURE="false"
# --- Storage (optional) ---
# If all keys are disabled, the app uses local filesystem (/data) to store uploads instead.
# If all S3 keys are disabled, the app uses local filesystem storage instead.
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
# Seaweedfs
+3 -2
View File
@@ -105,7 +105,7 @@ SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
SMTP_SECURE="false"
# --- Storage (optional) ---
# If all keys are disabled, the app uses local filesystem (usually /app/data) to store uploads instead.
# If all S3 keys are disabled, the app uses local filesystem storage instead.
# Make sure to mount this directory to a volume or the host filesystem to ensure data integrity.
S3_ACCESS_KEY_ID=""
S3_SECRET_ACCESS_KEY=""
@@ -327,8 +327,9 @@ openssl rand -hex 32
</Accordion>
<Accordion title="Storage (S3 or local)">
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `<cwd>/data` (usually `/app/data` in the official image).
- **Default (local)**: If all `S3_*` values are empty, uploads are stored under `/app/data` in the official image.
- Mount local uploads to persistent storage (for example `./data:/app/data`) or uploads can be lost on container recreation.
- **Rootless Docker**: `/app/data` remains the container path. Prefer the named volume from the example Compose file, or make sure a bind-mounted host directory is writable by the container's `node` user mapping.
- **S3/S3-compatible**: Configure `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_REGION`, `S3_ENDPOINT`, and `S3_BUCKET`.
- **`S3_FORCE_PATH_STYLE`** controls bucket addressing (defaults to `"false"`):
- `"true"` for path-style URLs (`https://endpoint/bucket`) common with MinIO/SeaweedFS.
@@ -0,0 +1,12 @@
import { basename, dirname, join } from "node:path";
export function getLocalDataDirectory(): string {
const cwd = process.cwd();
const parentDirectory = dirname(cwd);
if (basename(cwd) === "web" && basename(parentDirectory) === "apps") {
return join(dirname(parentDirectory), "data");
}
return join(cwd, "data");
}
+6 -1
View File
@@ -13,7 +13,12 @@ import { defaultResumeData } from "@reactive-resume/schema/resume/default";
import { applyResumePatches, ResumePatchError } from "@reactive-resume/utils/resume/patch";
import { generateId } from "@reactive-resume/utils/string";
import { grantResumeAccess, hasResumeAccess } from "../helpers/resume-access";
import { assertCanView, isOwner, redactResumeForViewer, shouldCountForStatistics } from "./resume-access-policy";
import {
assertCanView,
isOwner,
redactResumeForViewer,
shouldCountForStatistics,
} from "../helpers/resume-access-policy";
import { getStorageService } from "./storage";
const tags = {
+2 -1
View File
@@ -3,6 +3,7 @@ import { dirname, join } from "node:path";
import { count } from "drizzle-orm";
import { db } from "@reactive-resume/db/client";
import * as schema from "@reactive-resume/db/schema";
import { getLocalDataDirectory } from "../helpers/local-data-directory";
const CACHE_DURATION_MS = 6 * 60 * 60 * 1000; // 6 hours
const GITHUB_API_URL = "https://api.github.com/repos/amruthpillai/reactive-resume";
@@ -15,7 +16,7 @@ const LAST_KNOWN = {
stars: 34_073,
} as const;
const getCachePath = (key: string) => join(process.cwd(), "data", "statistics", `${key}.txt`);
const getCachePath = (key: string) => join(getLocalDataDirectory(), "statistics", `${key}.txt`);
const readCache = async (key: string): Promise<number | null> => {
try {
+2 -1
View File
@@ -9,6 +9,7 @@ import {
} from "@aws-sdk/client-s3";
import sharp from "sharp";
import { env } from "@reactive-resume/env/server";
import { getLocalDataDirectory } from "../helpers/local-data-directory";
interface StorageWriteInput {
key: string;
@@ -114,7 +115,7 @@ class LocalStorageService implements StorageService {
private rootDirectory: string;
constructor() {
this.rootDirectory = join(process.cwd(), "data");
this.rootDirectory = getLocalDataDirectory();
}
async list(prefix: string): Promise<string[]> {