mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-14 06:47:00 +10:00
bdfb854602
* fix: resolve local data directory to /app/data in production Docker In the official Docker image, cwd is /app/apps/web (set via WORKDIR), but the data volume is mounted at /app/data. Without pnpm-workspace.yaml present in the runtime image, findWorkspaceRoot() returns null, so getLocalDataDirectory() fell back to <cwd>/data = /app/apps/web/data, which the node user has no permission to create. This caused the storage healthcheck to fail with EACCES. Add a production fallback: when cwd ends in apps/web, resolve the data directory to two levels up (matching /app/data in the official image). Re-resolves #2990. https://claude.ai/code/session_015pSTtukxf7mFTty2Y6PHZf * fix: replace apps/web heuristic with LOCAL_STORAGE_PATH env var The previous fix special-cased a cwd ending in apps/web to land on /app/data, but the heuristic could false-positive on any path with that suffix and was fragile to Dockerfile changes. pnpm-workspace.yaml is never copied into the runtime image, so the workspace-root walk was also dead code in production. Replace the heuristic with an explicit LOCAL_STORAGE_PATH env var: - Set LOCAL_STORAGE_PATH=/app/data in the Dockerfile (single source of truth). - Add LOCAL_STORAGE_PATH to the env schema; storage and statistics services pass it through to getLocalDataDirectory. - getLocalDataDirectory now uses the override when set, else workspace root (dev), else cwd/data. - New Nitro plugin validates the resolved local data directory at startup and refuses to boot with a clear error if it isn't writable, surfacing permission issues immediately instead of at first upload/healthcheck. - Document the new variable in .env.example and the Docker self-hosting docs. https://claude.ai/code/session_015pSTtukxf7mFTty2Y6PHZf * fix: address review feedback on storage path handling - apps/web/plugins/2.storage.ts: use the default-import style for node:fs/promises (matches the rest of the repo, sidesteps any named-export concerns for fs.constants). - packages/env/src/server.ts: reject relative LOCAL_STORAGE_PATH values via a zod refinement. Relative paths would be resolved against cwd, which differs between dev and Docker — exactly the same surprise the original bug had. Failing fast at config validation time gives a clear error before the server boots. https://claude.ai/code/session_015pSTtukxf7mFTty2Y6PHZf * fix: update data volume configuration in Docker Compose and enhance Nitro plugin * fix: remove "Can I customize the templates?" FAQ entry from multiple language files --------- Co-authored-by: Claude <noreply@anthropic.com>
82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
import { isAbsolute, join } from "node:path";
|
|
import { createEnv } from "@t3-oss/env-core";
|
|
import { config } from "dotenv";
|
|
import { z } from "zod";
|
|
import { findWorkspaceRoot } from "@reactive-resume/utils/monorepo.node";
|
|
|
|
const workspaceRoot = findWorkspaceRoot();
|
|
|
|
if (workspaceRoot) {
|
|
config({ path: join(workspaceRoot, ".env"), quiet: true });
|
|
}
|
|
|
|
export const env = createEnv({
|
|
server: {
|
|
// Application
|
|
APP_URL: z.url({ protocol: /https?/ }),
|
|
|
|
// Database
|
|
DATABASE_URL: z.url({ protocol: /postgres(ql)?/ }),
|
|
|
|
// Authentication
|
|
AUTH_SECRET: z.string().min(1),
|
|
BETTER_AUTH_API_KEY: z.string().min(1).optional(),
|
|
|
|
// Social Auth (Google)
|
|
GOOGLE_CLIENT_ID: z.string().min(1).optional(),
|
|
GOOGLE_CLIENT_SECRET: z.string().min(1).optional(),
|
|
|
|
// Social Auth (GitHub)
|
|
GITHUB_CLIENT_ID: z.string().min(1).optional(),
|
|
GITHUB_CLIENT_SECRET: z.string().min(1).optional(),
|
|
|
|
// Social Auth (LinkedIn)
|
|
LINKEDIN_CLIENT_ID: z.string().min(1).optional(),
|
|
LINKEDIN_CLIENT_SECRET: z.string().min(1).optional(),
|
|
|
|
// Custom OAuth Provider
|
|
OAUTH_PROVIDER_NAME: z.string().min(1).optional(),
|
|
OAUTH_CLIENT_ID: z.string().min(1).optional(),
|
|
OAUTH_CLIENT_SECRET: z.string().min(1).optional(),
|
|
OAUTH_DISCOVERY_URL: z.url({ protocol: /https?/ }).optional(),
|
|
OAUTH_AUTHORIZATION_URL: z.url({ protocol: /https?/ }).optional(),
|
|
OAUTH_TOKEN_URL: z.url({ protocol: /https?/ }).optional(),
|
|
OAUTH_USER_INFO_URL: z.url({ protocol: /https?/ }).optional(),
|
|
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS: z.string().optional(),
|
|
OAUTH_SCOPES: z
|
|
.string()
|
|
.min(1)
|
|
.transform((value) => value.split(" "))
|
|
.default(["openid", "profile", "email"]),
|
|
|
|
// Email (SMTP)
|
|
SMTP_HOST: z.string().min(1).optional(),
|
|
SMTP_PORT: z.coerce.number().int().min(1).max(65535).default(587),
|
|
SMTP_USER: z.string().min(1).optional(),
|
|
SMTP_PASS: z.string().min(1).optional(),
|
|
SMTP_FROM: z.string().min(1).optional(),
|
|
SMTP_SECURE: z.stringbool().default(false),
|
|
|
|
// Storage (Optional)
|
|
LOCAL_STORAGE_PATH: z.string().min(1).refine(isAbsolute, "LOCAL_STORAGE_PATH must be an absolute path").optional(),
|
|
S3_ACCESS_KEY_ID: z.string().min(1).optional(),
|
|
S3_SECRET_ACCESS_KEY: z.string().min(1).optional(),
|
|
S3_REGION: z.string().default("us-east-1"),
|
|
S3_ENDPOINT: z.url({ protocol: /https?/ }).optional(),
|
|
S3_BUCKET: z.string().min(1).optional(),
|
|
S3_FORCE_PATH_STYLE: z.stringbool().default(false),
|
|
|
|
// Feature Flags
|
|
FLAG_DISABLE_SIGNUPS: z.stringbool().default(false),
|
|
FLAG_DISABLE_EMAIL_AUTH: z.stringbool().default(false),
|
|
FLAG_DISABLE_IMAGE_PROCESSING: z.stringbool().default(false),
|
|
|
|
// Crowdin (optional, for translation tooling)
|
|
CROWDIN_PROJECT_ID: z.string().optional(),
|
|
CROWDIN_API_TOKEN: z.string().optional(),
|
|
GOOGLE_CLOUD_API_KEY: z.string().optional(),
|
|
},
|
|
runtimeEnv: process.env,
|
|
emptyStringAsUndefined: true,
|
|
});
|