feat: migrate runtime backend to node server
This commit is contained in:
162
server-node/src/config.ts
Normal file
162
server-node/src/config.ts
Normal file
@@ -0,0 +1,162 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
export type AppConfig = {
|
||||
nodeEnv: string;
|
||||
projectRoot: string;
|
||||
publicDir: string;
|
||||
logsDir: string;
|
||||
dataDir: string;
|
||||
sqlitePath: string;
|
||||
serverAddr: string;
|
||||
logLevel: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silent';
|
||||
jwtSecret: string;
|
||||
jwtExpiresIn: string;
|
||||
jwtIssuer: string;
|
||||
llm: {
|
||||
baseUrl: string;
|
||||
apiKey: string;
|
||||
model: string;
|
||||
};
|
||||
dashScope: {
|
||||
baseUrl: string;
|
||||
apiKey: string;
|
||||
imageModel: string;
|
||||
requestTimeoutMs: number;
|
||||
};
|
||||
};
|
||||
|
||||
type LoadConfigOptions = {
|
||||
env?: NodeJS.ProcessEnv;
|
||||
projectRoot?: string;
|
||||
};
|
||||
|
||||
function parseEnvContents(contents: string) {
|
||||
return contents
|
||||
.split(/\r?\n/u)
|
||||
.reduce<Record<string, string>>((envMap, rawLine) => {
|
||||
const line = rawLine.trim();
|
||||
if (!line || line.startsWith('#')) {
|
||||
return envMap;
|
||||
}
|
||||
|
||||
const separatorIndex = line.indexOf('=');
|
||||
if (separatorIndex < 0) {
|
||||
return envMap;
|
||||
}
|
||||
|
||||
const key = line.slice(0, separatorIndex).trim();
|
||||
let value = line.slice(separatorIndex + 1).trim();
|
||||
|
||||
if (
|
||||
(value.startsWith('"') && value.endsWith('"')) ||
|
||||
(value.startsWith("'") && value.endsWith("'"))
|
||||
) {
|
||||
value = value.slice(1, -1);
|
||||
}
|
||||
|
||||
envMap[key] = value;
|
||||
return envMap;
|
||||
}, {});
|
||||
}
|
||||
|
||||
function readEnvFile(filePath: string) {
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return parseEnvContents(fs.readFileSync(filePath, 'utf8'));
|
||||
}
|
||||
|
||||
function resolveDefaultProjectRoot() {
|
||||
const cwd = process.cwd();
|
||||
return path.basename(cwd) === 'server-node'
|
||||
? path.resolve(cwd, '..')
|
||||
: cwd;
|
||||
}
|
||||
|
||||
function readMergedEnv(projectRoot: string, processEnv: NodeJS.ProcessEnv) {
|
||||
return {
|
||||
...readEnvFile(path.join(projectRoot, '.env.example')),
|
||||
...readEnvFile(path.join(projectRoot, '.env.local')),
|
||||
...processEnv,
|
||||
};
|
||||
}
|
||||
|
||||
function readString(
|
||||
env: Record<string, string | undefined>,
|
||||
key: string,
|
||||
fallback: string,
|
||||
) {
|
||||
const value = env[key]?.trim();
|
||||
return value ? value : fallback;
|
||||
}
|
||||
|
||||
function readPositiveInt(
|
||||
env: Record<string, string | undefined>,
|
||||
key: string,
|
||||
fallback: number,
|
||||
) {
|
||||
const parsed = Number(env[key]);
|
||||
return Number.isFinite(parsed) && parsed > 0 ? Math.round(parsed) : fallback;
|
||||
}
|
||||
|
||||
export function loadConfig(options: LoadConfigOptions = {}): AppConfig {
|
||||
const projectRoot = options.projectRoot ?? resolveDefaultProjectRoot();
|
||||
const env = readMergedEnv(projectRoot, options.env ?? process.env);
|
||||
const logsDir = path.join(projectRoot, 'server-node', 'logs');
|
||||
const dataDir = path.join(projectRoot, 'server-node', 'data');
|
||||
|
||||
return {
|
||||
nodeEnv: readString(env, 'NODE_ENV', 'development'),
|
||||
projectRoot,
|
||||
publicDir: path.join(projectRoot, 'public'),
|
||||
logsDir,
|
||||
dataDir,
|
||||
sqlitePath: readString(
|
||||
env,
|
||||
'SQLITE_PATH',
|
||||
path.join(dataDir, 'genarrative.sqlite'),
|
||||
),
|
||||
serverAddr: readString(env, 'NODE_SERVER_ADDR', ':8081'),
|
||||
logLevel: readString(env, 'LOG_LEVEL', 'info') as AppConfig['logLevel'],
|
||||
jwtSecret: readString(env, 'JWT_SECRET', 'genarrative-dev-secret'),
|
||||
jwtExpiresIn: readString(env, 'JWT_EXPIRES_IN', '7d'),
|
||||
jwtIssuer: readString(env, 'JWT_ISSUER', 'genarrative-server-node'),
|
||||
llm: {
|
||||
baseUrl: readString(
|
||||
env,
|
||||
'LLM_BASE_URL',
|
||||
'https://ark.cn-beijing.volces.com/api/v3',
|
||||
),
|
||||
apiKey:
|
||||
env.LLM_API_KEY?.trim() ||
|
||||
env.ARK_API_KEY?.trim() ||
|
||||
env.VITE_LLM_API_KEY?.trim() ||
|
||||
'',
|
||||
model: readString(
|
||||
env,
|
||||
'LLM_MODEL',
|
||||
readString(
|
||||
env,
|
||||
'VITE_LLM_MODEL',
|
||||
'doubao-1-5-pro-32k-character-250715',
|
||||
),
|
||||
),
|
||||
},
|
||||
dashScope: {
|
||||
baseUrl: readString(
|
||||
env,
|
||||
'DASHSCOPE_BASE_URL',
|
||||
'https://dashscope.aliyuncs.com/api/v1',
|
||||
),
|
||||
apiKey: env.DASHSCOPE_API_KEY?.trim() || '',
|
||||
imageModel: readString(env, 'DASHSCOPE_IMAGE_MODEL', 'wan2.2-t2i-flash'),
|
||||
requestTimeoutMs: readPositiveInt(
|
||||
env,
|
||||
'DASHSCOPE_IMAGE_REQUEST_TIMEOUT_MS',
|
||||
150000,
|
||||
),
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user