168 lines
5.7 KiB
JavaScript
168 lines
5.7 KiB
JavaScript
"use strict";
|
|
|
|
require("dotenv").config({ path: ".env.local", quiet: true });
|
|
require("dotenv").config({ path: ".env", quiet: true });
|
|
|
|
const { z } = require("zod");
|
|
|
|
function intFromEnv(name, fallback) {
|
|
const raw = process.env[name];
|
|
if (!raw) {
|
|
return fallback;
|
|
}
|
|
|
|
const parsed = Number.parseInt(raw, 10);
|
|
return Number.isInteger(parsed) ? parsed : fallback;
|
|
}
|
|
|
|
function strFromEnv(name, fallback) {
|
|
const raw = process.env[name];
|
|
return raw && String(raw).trim() ? String(raw).trim() : fallback;
|
|
}
|
|
|
|
function listFromEnv(name, fallback = []) {
|
|
const raw = process.env[name];
|
|
if (!raw) {
|
|
return fallback;
|
|
}
|
|
|
|
return String(raw)
|
|
.split(",")
|
|
.map((item) => item.trim())
|
|
.filter(Boolean);
|
|
}
|
|
|
|
function boolFromEnv(name, fallback) {
|
|
const raw = process.env[name];
|
|
if (!raw) {
|
|
return fallback;
|
|
}
|
|
|
|
const normalized = String(raw).trim().toLowerCase();
|
|
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
return true;
|
|
}
|
|
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
return false;
|
|
}
|
|
return fallback;
|
|
}
|
|
|
|
const parsed = {
|
|
nodeEnv: strFromEnv("NODE_ENV", "development"),
|
|
port: intFromEnv("PORT", 3000),
|
|
logLevel: strFromEnv("LOG_LEVEL", "info"),
|
|
appBaseUrl: strFromEnv("APP_BASE_URL", "http://localhost:3000"),
|
|
betterAuthSecret: strFromEnv("BETTER_AUTH_SECRET", "dev-better-auth-secret"),
|
|
betterAuthBasePath: strFromEnv("BETTER_AUTH_BASE_PATH", "/api/auth"),
|
|
xOAuthClientId: strFromEnv("X_OAUTH_CLIENT_ID", ""),
|
|
xOAuthClientSecret: strFromEnv("X_OAUTH_CLIENT_SECRET", ""),
|
|
internalApiToken: strFromEnv("INTERNAL_API_TOKEN", ""),
|
|
convexDeploymentUrl: strFromEnv("CONVEX_DEPLOYMENT_URL", strFromEnv("CONVEX_URL", "")),
|
|
convexAuthToken: strFromEnv("CONVEX_AUTH_TOKEN", ""),
|
|
convexStateQuery: strFromEnv("CONVEX_STATE_QUERY", "state:getLatestSnapshot"),
|
|
convexStateMutation: strFromEnv("CONVEX_STATE_MUTATION", "state:saveSnapshot"),
|
|
xWebhookSecret: process.env.X_WEBHOOK_SECRET || "dev-x-secret",
|
|
xBearerToken: strFromEnv("X_BEARER_TOKEN", ""),
|
|
xBotUserId: strFromEnv("X_BOT_USER_ID", ""),
|
|
polarWebhookSecret: process.env.POLAR_WEBHOOK_SECRET || "dev-polar-secret",
|
|
polarAccessToken: strFromEnv("POLAR_ACCESS_TOKEN", ""),
|
|
polarServer: strFromEnv("POLAR_SERVER", "production"),
|
|
polarProductIds: listFromEnv("POLAR_PRODUCT_IDS", []),
|
|
qwenTtsApiKey: strFromEnv("QWEN_TTS_API_KEY", ""),
|
|
qwenTtsBaseUrl: strFromEnv("QWEN_TTS_BASE_URL", "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"),
|
|
qwenTtsModel: strFromEnv("QWEN_TTS_MODEL", "qwen-tts-latest"),
|
|
qwenTtsVoice: strFromEnv("QWEN_TTS_VOICE", "Cherry"),
|
|
qwenTtsFormat: strFromEnv("QWEN_TTS_FORMAT", "mp3"),
|
|
minioEndPoint: strFromEnv("MINIO_ENDPOINT", ""),
|
|
minioPort: intFromEnv("MINIO_PORT", 443),
|
|
minioUseSSL: boolFromEnv("MINIO_USE_SSL", true),
|
|
minioBucket: strFromEnv("MINIO_BUCKET", ""),
|
|
minioRegion: strFromEnv("MINIO_REGION", "us-east-1"),
|
|
minioAccessKey: strFromEnv("MINIO_ACCESS_KEY", ""),
|
|
minioSecretKey: strFromEnv("MINIO_SECRET_KEY", ""),
|
|
minioSignedUrlTtlSec: intFromEnv("MINIO_SIGNED_URL_TTL_SEC", 3600),
|
|
rateLimits: {
|
|
webhookPerMinute: intFromEnv("WEBHOOK_RPM", 120),
|
|
authPerMinute: intFromEnv("AUTH_RPM", 30),
|
|
actionPerMinute: intFromEnv("ACTION_RPM", 60),
|
|
},
|
|
abuse: {
|
|
maxJobsPerUserPerDay: intFromEnv("ABUSE_MAX_JOBS_PER_USER_PER_DAY", 0),
|
|
cooldownSec: intFromEnv("ABUSE_COOLDOWN_SEC", 0),
|
|
denyUserIds: listFromEnv("ABUSE_DENY_USER_IDS", []),
|
|
},
|
|
credit: {
|
|
baseCredits: intFromEnv("BASE_CREDITS", 1),
|
|
includedChars: intFromEnv("INCLUDED_CHARS", 25000),
|
|
stepChars: intFromEnv("STEP_CHARS", 10000),
|
|
stepCredits: intFromEnv("STEP_CREDITS", 1),
|
|
maxCharsPerArticle: intFromEnv("MAX_CHARS_PER_ARTICLE", 120000),
|
|
},
|
|
};
|
|
|
|
parsed.allowInMemoryStateFallback = boolFromEnv(
|
|
"ALLOW_IN_MEMORY_STATE_FALLBACK",
|
|
parsed.nodeEnv !== "production",
|
|
);
|
|
|
|
const ConfigSchema = z.object({
|
|
nodeEnv: z.string().min(1),
|
|
port: z.number().int().positive(),
|
|
logLevel: z.enum(["fatal", "error", "warn", "info", "debug", "trace", "silent"]),
|
|
appBaseUrl: z.string().min(1),
|
|
betterAuthSecret: z.string().min(1),
|
|
betterAuthBasePath: z.string().min(1),
|
|
xOAuthClientId: z.string(),
|
|
xOAuthClientSecret: z.string(),
|
|
internalApiToken: z.string(),
|
|
convexDeploymentUrl: z.string(),
|
|
convexAuthToken: z.string(),
|
|
convexStateQuery: z.string().min(1),
|
|
convexStateMutation: z.string().min(1),
|
|
xWebhookSecret: z.string().min(1),
|
|
xBearerToken: z.string(),
|
|
xBotUserId: z.string(),
|
|
polarWebhookSecret: z.string().min(1),
|
|
polarAccessToken: z.string(),
|
|
polarServer: z.enum(["production", "sandbox"]),
|
|
polarProductIds: z.array(z.string().min(1)),
|
|
qwenTtsApiKey: z.string(),
|
|
qwenTtsBaseUrl: z.string().min(1),
|
|
qwenTtsModel: z.string().min(1),
|
|
qwenTtsVoice: z.string().min(1),
|
|
qwenTtsFormat: z.string().min(1),
|
|
minioEndPoint: z.string(),
|
|
minioPort: z.number().int().positive(),
|
|
minioUseSSL: z.boolean(),
|
|
minioBucket: z.string(),
|
|
minioRegion: z.string(),
|
|
minioAccessKey: z.string(),
|
|
minioSecretKey: z.string(),
|
|
minioSignedUrlTtlSec: z.number().int().positive(),
|
|
rateLimits: z.object({
|
|
webhookPerMinute: z.number().int().positive(),
|
|
authPerMinute: z.number().int().positive(),
|
|
actionPerMinute: z.number().int().positive(),
|
|
}),
|
|
abuse: z.object({
|
|
maxJobsPerUserPerDay: z.number().int().nonnegative(),
|
|
cooldownSec: z.number().int().nonnegative(),
|
|
denyUserIds: z.array(z.string().min(1)),
|
|
}),
|
|
credit: z.object({
|
|
baseCredits: z.number().int().positive(),
|
|
includedChars: z.number().int().positive(),
|
|
stepChars: z.number().int().positive(),
|
|
stepCredits: z.number().int().positive(),
|
|
maxCharsPerArticle: z.number().int().positive(),
|
|
}),
|
|
allowInMemoryStateFallback: z.boolean(),
|
|
});
|
|
|
|
const config = ConfigSchema.parse(parsed);
|
|
|
|
module.exports = {
|
|
config,
|
|
};
|