文件
hyb-oa/server/src/config.js
T

142 行
6.1 KiB
JavaScript

const path = require("node:path");
const ONE_DAY_SECONDS = 24 * 60 * 60;
const ONE_MINUTE_SECONDS = 60;
function requiredEnv(name) {
const value = process.env[name];
if (!value) {
throw new Error(`Missing required env: ${name}`);
}
return value;
}
function getConfig() {
const nodeEnv = process.env.NODE_ENV ?? "development";
const serverRoot = path.resolve(__dirname, "..");
const storageDir = process.env.STORAGE_DIR ?? "generated/storage";
const clientDistDir = process.env.CLIENT_DIST_DIR ?? "../client/dist";
return {
nodeEnv,
port: Number(process.env.PORT ?? 3000),
host: process.env.HOST ?? "0.0.0.0",
metricsEnabled: process.env.METRICS_ENABLED !== "false",
appOrigin: process.env.APP_ORIGIN ?? "",
jwtSecret: requiredEnv("JWT_SECRET"),
fieldEncryptionKey: process.env.FIELD_ENCRYPTION_KEY ?? "",
accessTokenTtl: "15m",
refreshTtlSeconds: 7 * ONE_DAY_SECONDS,
refreshCookieName: "refresh_token",
cookieSecure:
process.env.COOKIE_SECURE != null
? process.env.COOKIE_SECURE === "true"
: nodeEnv === "production",
passwordResetCodeTtlSeconds: Number(
process.env.PASSWORD_RESET_CODE_TTL_SECONDS ?? 10 * 60,
),
devShowPasswordResetCode: process.env.DEV_SHOW_PASSWORD_RESET_CODE === "true",
loginFailWindowSeconds: Number(
process.env.AUTH_LOGIN_FAIL_WINDOW_SECONDS ?? 15 * ONE_MINUTE_SECONDS,
),
loginFailMaxAttempts: Number(process.env.AUTH_LOGIN_FAIL_MAX_ATTEMPTS ?? 5),
loginLockSeconds: Number(process.env.AUTH_LOGIN_LOCK_SECONDS ?? 30 * ONE_MINUTE_SECONDS),
oidcEnabled: process.env.OIDC_ENABLED === "true",
oidcIssuerUrl: process.env.OIDC_ISSUER_URL ?? "",
oidcClientId: process.env.OIDC_CLIENT_ID ?? "",
oidcClientSecret: process.env.OIDC_CLIENT_SECRET ?? "",
oidcRedirectUri: process.env.OIDC_REDIRECT_URI ?? "",
oidcScopes: process.env.OIDC_SCOPES ?? "openid profile email",
appTimezone: process.env.APP_TIMEZONE ?? "Asia/Shanghai",
storageDir: path.resolve(serverRoot, storageDir),
storageDriver: process.env.STORAGE_DRIVER ?? "local",
s3Endpoint: process.env.S3_ENDPOINT ?? "",
s3Region: process.env.S3_REGION ?? "us-east-1",
s3Bucket: process.env.S3_BUCKET ?? "",
s3AccessKeyId: process.env.S3_ACCESS_KEY_ID ?? "",
s3SecretAccessKey: process.env.S3_SECRET_ACCESS_KEY ?? "",
s3ForcePathStyle: process.env.S3_FORCE_PATH_STYLE === "true",
serveClientDist: process.env.SERVE_CLIENT_DIST === "true",
openapiDocsEnabled: process.env.OPENAPI_DOCS_ENABLED === "true",
clientDistDir: path.resolve(serverRoot, clientDistDir),
signedUrlTtlSeconds: Number(process.env.SIGNED_URL_TTL_SECONDS ?? 600),
maxUploadSizeBytes: Number(process.env.MAX_UPLOAD_SIZE_BYTES ?? 10 * 1024 * 1024),
rateLimitEnabled:
process.env.RATE_LIMIT_ENABLED != null
? process.env.RATE_LIMIT_ENABLED === "true"
: nodeEnv === "production",
rateLimitLoginMax: Number(process.env.RATE_LIMIT_LOGIN_MAX ?? 10),
rateLimitLoginWindow: process.env.RATE_LIMIT_LOGIN_WINDOW ?? "1 minute",
delegationRevokeCron: process.env.DELEGATION_REVOKE_CRON ?? "0 * * * *",
jobDriver: process.env.JOB_DRIVER ?? "cron",
jobsEnabled: process.env.JOBS_ENABLED !== "false",
pgBossSchema: process.env.PG_BOSS_SCHEMA ?? "pgboss",
ocrProvider: process.env.OCR_PROVIDER ?? "off",
ocrBatchSize: Number(process.env.OCR_BATCH_SIZE ?? 10),
ocrLockTtlSeconds: Number(process.env.OCR_LOCK_TTL_SECONDS ?? 300),
ocrPollCron: process.env.OCR_POLL_CRON ?? "* * * * *",
ocrMaxAttempts: Number(process.env.OCR_MAX_ATTEMPTS ?? 3),
ocrTimeoutMs: Number(process.env.OCR_TIMEOUT_MS ?? 5000),
invoiceVerificationEnabled: process.env.INVOICE_VERIFICATION_ENABLED === "true",
leaveHolidayCalendarEnabled: process.env.LEAVE_HOLIDAY_CALENDAR_ENABLED === "true",
invoiceVerificationFlagEnv: process.env.INVOICE_VERIFICATION_ENABLED ?? "false",
verificationProvider: process.env.VERIFICATION_PROVIDER ?? "mock",
verificationPollCron: process.env.VERIFICATION_POLL_CRON ?? "* * * * *",
verificationMaxAttempts: Number(process.env.VERIFICATION_MAX_ATTEMPTS ?? 3),
verificationRetryCooldownSeconds: Number(
process.env.VERIFICATION_RETRY_COOLDOWN_SECONDS ?? 60,
),
verificationTimeoutMs: Number(process.env.VERIFICATION_TIMEOUT_MS ?? 5000),
budgetControlMode: process.env.BUDGET_CONTROL_MODE ?? "warn",
erpSyncEnabled: process.env.ERP_SYNC_ENABLED === "true",
erpSyncFlagEnv: process.env.ERP_SYNC_ENABLED ?? "false",
erpSyncProvider: process.env.ERP_SYNC_PROVIDER ?? "mock",
erpSyncEndpoint: process.env.ERP_SYNC_ENDPOINT ?? "",
erpSyncTimeoutMs: Number(process.env.ERP_SYNC_TIMEOUT_MS ?? 5000),
erpSyncAuthToken: process.env.ERP_SYNC_AUTH_TOKEN ?? "",
travelSyncEnabled: process.env.TRAVEL_SYNC_ENABLED === "true",
imNotifierEnabled: process.env.IM_NOTIFIER_ENABLED === "true",
integrationOutboxCron: process.env.INTEGRATION_OUTBOX_CRON ?? "* * * * *",
integrationOutboxBatchSize: Number(process.env.INTEGRATION_OUTBOX_BATCH_SIZE ?? 20),
integrationOutboxMaxAttempts: Number(process.env.INTEGRATION_OUTBOX_MAX_ATTEMPTS ?? 5),
integrationOutboxRetrySeconds: Number(process.env.INTEGRATION_OUTBOX_RETRY_SECONDS ?? 120),
invoiceArchiveCron: process.env.INVOICE_ARCHIVE_CRON ?? "30 2 * * *",
invoiceArchiveUnusedDays: Number(process.env.INVOICE_ARCHIVE_UNUSED_DAYS ?? 90),
invoiceArchiveBatchSize: Number(process.env.INVOICE_ARCHIVE_BATCH_SIZE ?? 200),
tencentSecretId: process.env.TENCENT_SECRET_ID ?? "",
tencentSecretKey: process.env.TENCENT_SECRET_KEY ?? "",
tencentOcrRegion: process.env.TENCENT_OCR_REGION ?? "ap-beijing",
emailEnabled: process.env.EMAIL_ENABLED === "true",
emailFrom: process.env.EMAIL_FROM ?? "[email protected]",
emailBaseUrl: process.env.EMAIL_BASE_URL ?? "",
smtpHost: process.env.SMTP_HOST ?? "",
smtpPort: Number(process.env.SMTP_PORT ?? 587),
smtpSecure: process.env.SMTP_SECURE === "true",
smtpUser: process.env.SMTP_USER ?? "",
smtpPass: process.env.SMTP_PASS ?? "",
otelTracesEnabled: process.env.OTEL_TRACES_ENABLED === "true",
};
}
module.exports = { getConfig };