feat: enhance configuration file handling and dynamic frontend path resolution (#40)

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
samanhappy
2025-04-30 22:38:21 +08:00
committed by GitHub
parent 7887a3a5f9
commit 0a6259decf
8 changed files with 400 additions and 43 deletions

View File

@@ -1,6 +1,8 @@
import express from 'express';
import config from './config/index.js';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { initMcpServer } from './services/mcpService.js';
import { initMiddlewares } from './middlewares/index.js';
import { initRoutes } from './routes/index.js';
@@ -13,9 +15,14 @@ import {
import { migrateUserData } from './utils/migration.js';
import { initializeDefaultUser } from './models/User.js';
// Get the directory name in ESM
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export class AppServer {
private app: express.Application;
private port: number | string;
private frontendPath: string | null = null;
constructor() {
this.app = express();
@@ -48,9 +55,8 @@ export class AppServer {
throw error;
})
.finally(() => {
this.app.get('*', (_req, res) => {
res.sendFile(path.join(process.cwd(), 'frontend', 'dist', 'index.html'));
});
// Find and serve frontend
this.findAndServeFrontend();
});
} catch (error) {
console.error('Error initializing server:', error);
@@ -58,15 +64,135 @@ export class AppServer {
}
}
private findAndServeFrontend(): void {
// Find frontend path
this.frontendPath = this.findFrontendDistPath();
if (this.frontendPath) {
console.log(`Serving frontend from: ${this.frontendPath}`);
this.app.use(express.static(this.frontendPath));
// Add the wildcard route for SPA
if (fs.existsSync(path.join(this.frontendPath, 'index.html'))) {
this.app.get('*', (_req, res) => {
res.sendFile(path.join(this.frontendPath!, 'index.html'));
});
}
} else {
console.warn('Frontend dist directory not found. Server will run without frontend.');
this.app.get('/', (_req, res) => {
res
.status(404)
.send('Frontend not found. MCPHub API is running, but the UI is not available.');
});
}
}
start(): void {
this.app.listen(this.port, () => {
console.log(`Server is running on port ${this.port}`);
if (this.frontendPath) {
console.log(`Open http://localhost:${this.port} in your browser to access MCPHub UI`);
} else {
console.log(
`MCPHub API is running on http://localhost:${this.port}, but the UI is not available`,
);
}
});
}
getApp(): express.Application {
return this.app;
}
// Helper method to find frontend dist path in different environments
private findFrontendDistPath(): string | null {
// Debug flag for detailed logging
const debug = process.env.DEBUG === 'true';
if (debug) {
console.log('DEBUG: Current directory:', process.cwd());
console.log('DEBUG: Script directory:', __dirname);
}
// First, find the package root directory
const packageRoot = this.findPackageRoot();
if (debug) {
console.log('DEBUG: Using package root:', packageRoot);
}
if (!packageRoot) {
console.warn('Could not determine package root directory');
return null;
}
// Check for frontend dist in the standard location
const frontendDistPath = path.join(packageRoot, 'frontend', 'dist');
if (debug) {
console.log(`DEBUG: Checking frontend at: ${frontendDistPath}`);
}
if (
fs.existsSync(frontendDistPath) &&
fs.existsSync(path.join(frontendDistPath, 'index.html'))
) {
return frontendDistPath;
}
console.warn('Frontend distribution not found at', frontendDistPath);
return null;
}
// Helper method to find the package root (where package.json is located)
private findPackageRoot(): string | null {
const debug = process.env.DEBUG === 'true';
// Possible locations for package.json
const possibleRoots = [
// Standard npm package location
path.resolve(__dirname, '..', '..'),
// Current working directory
process.cwd(),
// When running from dist directory
path.resolve(__dirname, '..'),
// When installed via npx
path.resolve(__dirname, '..', '..', '..'),
];
// Special handling for npx
if (process.argv[1] && process.argv[1].includes('_npx')) {
const npxDir = path.dirname(process.argv[1]);
possibleRoots.unshift(path.resolve(npxDir, '..'));
}
if (debug) {
console.log('DEBUG: Checking for package.json in:', possibleRoots);
}
for (const root of possibleRoots) {
const packageJsonPath = path.join(root, 'package.json');
if (fs.existsSync(packageJsonPath)) {
try {
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (pkg.name === 'mcphub' || pkg.name === '@samanhappy/mcphub') {
if (debug) {
console.log(`DEBUG: Found package.json at ${packageJsonPath}`);
}
return root;
}
} catch (e) {
if (debug) {
console.error(`DEBUG: Failed to parse package.json at ${packageJsonPath}:`, e);
}
// Continue to the next potential root
}
}
}
return null;
}
}
export default AppServer;