Refactor: Clean up code formatting and improve readability across multiple files (#387)

This commit is contained in:
samanhappy
2025-10-26 19:27:30 +08:00
committed by GitHub
parent f79028ed64
commit 9105507722
9 changed files with 111 additions and 109 deletions

View File

@@ -1,12 +1,12 @@
import dotenv from 'dotenv'
import fs from 'fs'
import { McpSettings, IUser } from '../types/index.js'
import { getConfigFilePath } from '../utils/path.js'
import { getPackageVersion } from '../utils/version.js'
import { getDataService } from '../services/services.js'
import { DataService } from '../services/dataService.js'
import dotenv from 'dotenv';
import fs from 'fs';
import { McpSettings, IUser } from '../types/index.js';
import { getConfigFilePath } from '../utils/path.js';
import { getPackageVersion } from '../utils/version.js';
import { getDataService } from '../services/services.js';
import { DataService } from '../services/dataService.js';
dotenv.config()
dotenv.config();
const defaultConfig = {
port: process.env.PORT || 3000,
@@ -15,74 +15,74 @@ const defaultConfig = {
readonly: 'true' === process.env.READONLY || false,
mcpHubName: 'mcphub',
mcpHubVersion: getPackageVersion(),
}
};
const dataService: DataService = getDataService()
const dataService: DataService = getDataService();
// Settings cache
let settingsCache: McpSettings | null = null
let settingsCache: McpSettings | null = null;
export const getSettingsPath = (): string => {
return getConfigFilePath('mcp_settings.json', 'Settings')
}
return getConfigFilePath('mcp_settings.json', 'Settings');
};
export const loadOriginalSettings = (): McpSettings => {
// If cache exists, return cached data directly
if (settingsCache) {
return settingsCache
return settingsCache;
}
const settingsPath = getSettingsPath()
const settingsPath = getSettingsPath();
// check if file exists
if (!fs.existsSync(settingsPath)) {
console.warn(`Settings file not found at ${settingsPath}, using default settings.`)
const defaultSettings = { mcpServers: {}, users: [] }
console.warn(`Settings file not found at ${settingsPath}, using default settings.`);
const defaultSettings = { mcpServers: {}, users: [] };
// Cache default settings
settingsCache = defaultSettings
return defaultSettings
settingsCache = defaultSettings;
return defaultSettings;
}
try {
// Read and parse settings file
const settingsData = fs.readFileSync(settingsPath, 'utf8')
const settings = JSON.parse(settingsData)
const settingsData = fs.readFileSync(settingsPath, 'utf8');
const settings = JSON.parse(settingsData);
// Update cache
settingsCache = settings
settingsCache = settings;
console.log(`Loaded settings from ${settingsPath}`)
return settings
console.log(`Loaded settings from ${settingsPath}`);
return settings;
} catch (error) {
throw new Error(`Failed to load settings from ${settingsPath}: ${error}`)
throw new Error(`Failed to load settings from ${settingsPath}: ${error}`);
}
}
};
export const loadSettings = (user?: IUser): McpSettings => {
return dataService.filterSettings!(loadOriginalSettings(), user)
}
return dataService.filterSettings!(loadOriginalSettings(), user);
};
export const saveSettings = (settings: McpSettings, user?: IUser): boolean => {
const settingsPath = getSettingsPath()
const settingsPath = getSettingsPath();
try {
const mergedSettings = dataService.mergeSettings!(loadOriginalSettings(), settings, user)
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2), 'utf8')
const mergedSettings = dataService.mergeSettings!(loadOriginalSettings(), settings, user);
fs.writeFileSync(settingsPath, JSON.stringify(mergedSettings, null, 2), 'utf8');
// Update cache after successful save
settingsCache = mergedSettings
settingsCache = mergedSettings;
return true
return true;
} catch (error) {
console.error(`Failed to save settings to ${settingsPath}:`, error)
return false
console.error(`Failed to save settings to ${settingsPath}:`, error);
return false;
}
}
};
/**
* Clear settings cache, force next loadSettings call to re-read from file
*/
export const clearSettingsCache = (): void => {
settingsCache = null
}
settingsCache = null;
};
/**
* Get current cache status (for debugging)
@@ -90,71 +90,71 @@ export const clearSettingsCache = (): void => {
export const getSettingsCacheInfo = (): { hasCache: boolean } => {
return {
hasCache: settingsCache !== null,
}
}
};
};
export function replaceEnvVars(input: Record<string, any>): Record<string, any>
export function replaceEnvVars(input: string[] | undefined): string[]
export function replaceEnvVars(input: string): string
export function replaceEnvVars(input: Record<string, any>): Record<string, any>;
export function replaceEnvVars(input: string[] | undefined): string[];
export function replaceEnvVars(input: string): string;
export function replaceEnvVars(
input: Record<string, any> | string[] | string | undefined,
): Record<string, any> | string[] | string {
// Handle object input - recursively expand all nested values
if (input && typeof input === 'object' && !Array.isArray(input)) {
const res: Record<string, any> = {}
const res: Record<string, any> = {};
for (const [key, value] of Object.entries(input)) {
if (typeof value === 'string') {
res[key] = expandEnvVars(value)
res[key] = expandEnvVars(value);
} else if (typeof value === 'object' && value !== null) {
// Recursively handle nested objects and arrays
res[key] = replaceEnvVars(value as any)
res[key] = replaceEnvVars(value as any);
} else {
// Preserve non-string, non-object values (numbers, booleans, etc.)
res[key] = value
res[key] = value;
}
}
return res
return res;
}
// Handle array input - recursively expand all elements
if (Array.isArray(input)) {
return input.map((item) => {
if (typeof item === 'string') {
return expandEnvVars(item)
return expandEnvVars(item);
} else if (typeof item === 'object' && item !== null) {
return replaceEnvVars(item as any)
return replaceEnvVars(item as any);
}
return item
})
return item;
});
}
// Handle string input
if (typeof input === 'string') {
return expandEnvVars(input)
return expandEnvVars(input);
}
// Handle undefined/null array input
if (input === undefined || input === null) {
return []
return [];
}
return input
return input;
}
export const expandEnvVars = (value: string): string => {
if (typeof value !== 'string') {
return String(value)
return String(value);
}
// Replace ${VAR} format
let result = value.replace(/\$\{([^}]+)\}/g, (_, key) => process.env[key] || '')
let result = value.replace(/\$\{([^}]+)\}/g, (_, key) => process.env[key] || '');
// Also replace $VAR format (common on Unix-like systems)
result = result.replace(/\$([A-Z_][A-Z0-9_]*)/g, (_, key) => process.env[key] || '')
return result
}
result = result.replace(/\$([A-Z_][A-Z0-9_]*)/g, (_, key) => process.env[key] || '');
return result;
};
export default defaultConfig
export default defaultConfig;
export function getNameSeparator(): string {
const settings = loadSettings()
return settings.systemConfig?.nameSeparator || '-'
const settings = loadSettings();
return settings.systemConfig?.nameSeparator || '-';
}

View File

@@ -69,10 +69,7 @@ export const login = async (req: Request, res: Response): Promise<void> => {
// Check if user is admin with default password
const version = getPackageVersion();
const isUsingDefaultPassword =
user.username === 'admin' &&
user.isAdmin &&
isDefaultPassword(password) &&
version !== 'dev';
user.username === 'admin' && user.isAdmin && isDefaultPassword(password) && version !== 'dev';
jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY }, (err, token) => {
if (err) throw err;

View File

@@ -126,21 +126,19 @@ export const handleOAuthCallback = async (req: Request, res: Response) => {
// Check for authorization errors
if (error) {
console.error(`OAuth authorization failed: ${error} - ${error_description || ''}`);
return res
.status(400)
.send(
generateHtmlResponse('error', t('oauthCallback.authorizationFailed'), '', [
{ label: t('oauthCallback.authorizationFailedError'), value: String(error) },
...(error_description
? [
{
label: t('oauthCallback.authorizationFailedDetails'),
value: String(error_description),
},
]
: []),
]),
);
return res.status(400).send(
generateHtmlResponse('error', t('oauthCallback.authorizationFailed'), '', [
{ label: t('oauthCallback.authorizationFailedError'), value: String(error) },
...(error_description
? [
{
label: t('oauthCallback.authorizationFailedDetails'),
value: String(error_description),
},
]
: []),
]),
);
}
// Validate required parameters

View File

@@ -529,7 +529,7 @@ export const updateSystemConfig = (req: Request, res: Response): void => {
typeof mcpRouter.referer !== 'string' &&
typeof mcpRouter.title !== 'string' &&
typeof mcpRouter.baseUrl !== 'string')) &&
(typeof nameSeparator !== 'string')
typeof nameSeparator !== 'string'
) {
res.status(400).json({
success: false,

View File

@@ -28,7 +28,7 @@ function getCurrentFileDir(): string {
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
return process.cwd();
}
try {
return getCurrentModuleDir();
} catch {

View File

@@ -333,7 +333,7 @@ export class MCPHubOAuthProvider implements OAuthClientProvider {
const updatedConfig = await persistTokens(this.serverName, {
accessToken: tokens.access_token,
refreshToken: refreshTokenProvided ? tokens.refresh_token ?? null : undefined,
refreshToken: refreshTokenProvided ? (tokens.refresh_token ?? null) : undefined,
clearPendingAuthorization: hadPending,
});
@@ -421,7 +421,9 @@ export class MCPHubOAuthProvider implements OAuthClientProvider {
async saveCodeVerifier(verifier: string): Promise<void> {
this._codeVerifier = verifier;
try {
const updatedConfig = await updatePendingAuthorization(this.serverName, { codeVerifier: verifier });
const updatedConfig = await updatePendingAuthorization(this.serverName, {
codeVerifier: verifier,
});
if (updatedConfig) {
this.serverConfig = updatedConfig;
}
@@ -490,7 +492,10 @@ export class MCPHubOAuthProvider implements OAuthClientProvider {
if (scope === 'client' || scope === 'all') {
const supportsDynamicClient = currentConfig.oauth.dynamicRegistration?.enabled === true;
if (supportsDynamicClient && (currentConfig.oauth.clientId || currentConfig.oauth.clientSecret)) {
if (
supportsDynamicClient &&
(currentConfig.oauth.clientId || currentConfig.oauth.clientSecret)
) {
removeRegisteredClient(this.serverName);
const updated = await clearOAuthData(this.serverName, 'client');
assignUpdatedConfig(updated);

View File

@@ -380,10 +380,10 @@ export const initializeClientsFromSettings = async (
try {
for (const conf of allServers) {
const { name } = conf;
// Expand environment variables in all configuration values
const expandedConf = replaceEnvVars(conf as any) as ServerConfigWithName;
// Skip disabled servers
if (expandedConf.enabled === false) {
console.log(`Skipping disabled server: ${name}`);

View File

@@ -18,18 +18,18 @@ function initializePackageRoot(): void {
if (process.env.NODE_ENV === 'test' || process.env.JEST_WORKER_ID !== undefined) {
return;
}
try {
// Try to get the current module's directory
const currentModuleDir = getCurrentModuleDir();
// This file is in src/utils/path.ts (or dist/utils/path.js when compiled)
// So package.json should be 2 levels up
const possibleRoots = [
path.resolve(currentModuleDir, '..', '..'), // dist -> package root
path.resolve(currentModuleDir, '..'), // dist/utils -> dist -> package root
];
for (const root of possibleRoots) {
const packageJsonPath = path.join(root, 'package.json');
if (fs.existsSync(packageJsonPath)) {
@@ -66,10 +66,10 @@ export const findPackageRoot = (startPath?: string): string | null => {
}
const debug = process.env.DEBUG === 'true';
// Possible locations for package.json relative to the search path
const possibleRoots: string[] = [];
if (startPath) {
// When start path is provided (from fileURLToPath(import.meta.url))
possibleRoots.push(
@@ -78,25 +78,30 @@ export const findPackageRoot = (startPath?: string): string | null => {
// When in dist/ (compiled code) - go up 1 level
path.resolve(startPath, '..'),
// Direct parent directories
path.resolve(startPath)
path.resolve(startPath),
);
}
// Try to use require.resolve to find the module location (works in CommonJS and ESM with createRequire)
try {
// In ESM, we can use import.meta.resolve, but it's async in some versions
// So we'll try to find the module by checking the node_modules structure
// Check if this file is in a node_modules installation
const currentFile = new Error().stack?.split('\n')[2]?.match(/\((.+?):\d+:\d+\)$/)?.[1];
if (currentFile) {
const nodeModulesIndex = currentFile.indexOf('node_modules');
if (nodeModulesIndex !== -1) {
// Extract the package path from node_modules
const afterNodeModules = currentFile.substring(nodeModulesIndex + 'node_modules'.length + 1);
const afterNodeModules = currentFile.substring(
nodeModulesIndex + 'node_modules'.length + 1,
);
const packageNameEnd = afterNodeModules.indexOf(path.sep);
if (packageNameEnd !== -1) {
const packagePath = currentFile.substring(0, nodeModulesIndex + 'node_modules'.length + 1 + packageNameEnd);
const packagePath = currentFile.substring(
0,
nodeModulesIndex + 'node_modules'.length + 1 + packageNameEnd,
);
possibleRoots.push(packagePath);
}
}
@@ -108,18 +113,15 @@ export const findPackageRoot = (startPath?: string): string | null => {
// Check module.filename location (works in Node.js when available)
if (typeof __filename !== 'undefined') {
const moduleDir = path.dirname(__filename);
possibleRoots.push(
path.resolve(moduleDir, '..', '..'),
path.resolve(moduleDir, '..')
);
possibleRoots.push(path.resolve(moduleDir, '..', '..'), path.resolve(moduleDir, '..'));
}
// Check common installation locations
possibleRoots.push(
// Current working directory (for development/tests)
process.cwd(),
// Parent of cwd
path.resolve(process.cwd(), '..')
path.resolve(process.cwd(), '..'),
);
if (debug) {
@@ -157,12 +159,12 @@ export const findPackageRoot = (startPath?: string): string | null => {
if (debug) {
console.warn('DEBUG: Could not find package root directory');
}
// Cache null result as well to avoid repeated searches
if (!startPath) {
cachedPackageRoot = null;
}
return null;
};

View File

@@ -11,13 +11,13 @@ export const getPackageVersion = (searchPath?: string): string => {
try {
// Use provided path or fallback to current working directory
const startPath = searchPath || process.cwd();
const packageRoot = findPackageRoot(startPath);
if (!packageRoot) {
console.warn('Could not find package root, using default version');
return 'dev';
}
const packageJsonPath = path.join(packageRoot, 'package.json');
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
const packageJson = JSON.parse(packageJsonContent);