mirror of
https://github.com/samanhappy/mcphub.git
synced 2025-12-24 02:39:19 -05:00
introduce market
This commit is contained in:
154
src/controllers/marketController.ts
Normal file
154
src/controllers/marketController.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { ApiResponse } from '../types/index.js';
|
||||
import {
|
||||
getMarketServers,
|
||||
getMarketServerByName,
|
||||
getMarketCategories,
|
||||
getMarketTags,
|
||||
searchMarketServers,
|
||||
filterMarketServersByCategory,
|
||||
filterMarketServersByTag
|
||||
} from '../services/marketService.js';
|
||||
|
||||
// Get all market servers
|
||||
export const getAllMarketServers = (_: Request, res: Response): void => {
|
||||
try {
|
||||
const marketServers = Object.values(getMarketServers());
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: marketServers,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get market servers information',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get a specific market server by name
|
||||
export const getMarketServer = (req: Request, res: Response): void => {
|
||||
try {
|
||||
const { name } = req.params;
|
||||
if (!name) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: 'Server name is required',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const server = getMarketServerByName(name);
|
||||
if (!server) {
|
||||
res.status(404).json({
|
||||
success: false,
|
||||
message: 'Market server not found',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: server,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get market server information',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get all market categories
|
||||
export const getAllMarketCategories = (_: Request, res: Response): void => {
|
||||
try {
|
||||
const categories = getMarketCategories();
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: categories,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get market categories',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Get all market tags
|
||||
export const getAllMarketTags = (_: Request, res: Response): void => {
|
||||
try {
|
||||
const tags = getMarketTags();
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: tags,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to get market tags',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Search market servers
|
||||
export const searchMarketServersByQuery = (req: Request, res: Response): void => {
|
||||
try {
|
||||
const { query } = req.query;
|
||||
const searchQuery = typeof query === 'string' ? query : '';
|
||||
|
||||
const servers = searchMarketServers(searchQuery);
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: servers,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to search market servers',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Filter market servers by category
|
||||
export const getMarketServersByCategory = (req: Request, res: Response): void => {
|
||||
try {
|
||||
const { category } = req.params;
|
||||
|
||||
const servers = filterMarketServersByCategory(category);
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: servers,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to filter market servers by category',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Filter market servers by tag
|
||||
export const getMarketServersByTag = (req: Request, res: Response): void => {
|
||||
try {
|
||||
const { tag } = req.params;
|
||||
|
||||
const servers = filterMarketServersByTag(tag);
|
||||
const response: ApiResponse = {
|
||||
success: true,
|
||||
data: servers,
|
||||
};
|
||||
res.json(response);
|
||||
} catch (error) {
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Failed to filter market servers by tag',
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -19,6 +19,15 @@ import {
|
||||
getGroupServers,
|
||||
updateGroupServersBatch
|
||||
} from '../controllers/groupController.js';
|
||||
import {
|
||||
getAllMarketServers,
|
||||
getMarketServer,
|
||||
getAllMarketCategories,
|
||||
getAllMarketTags,
|
||||
searchMarketServersByQuery,
|
||||
getMarketServersByCategory,
|
||||
getMarketServersByTag
|
||||
} from '../controllers/marketController.js';
|
||||
import {
|
||||
login,
|
||||
register,
|
||||
@@ -50,6 +59,15 @@ export const initRoutes = (app: express.Application): void => {
|
||||
// New route for batch updating servers in a group
|
||||
router.put('/groups/:id/servers/batch', updateGroupServersBatch);
|
||||
|
||||
// Market routes
|
||||
router.get('/market/servers', getAllMarketServers);
|
||||
router.get('/market/servers/search', searchMarketServersByQuery);
|
||||
router.get('/market/servers/:name', getMarketServer);
|
||||
router.get('/market/categories', getAllMarketCategories);
|
||||
router.get('/market/categories/:category', getMarketServersByCategory);
|
||||
router.get('/market/tags', getAllMarketTags);
|
||||
router.get('/market/tags/:tag', getMarketServersByTag);
|
||||
|
||||
// Auth routes (these will NOT be protected by auth middleware)
|
||||
app.post('/auth/login', [
|
||||
check('username', 'Username is required').not().isEmpty(),
|
||||
|
||||
103
src/services/marketService.ts
Normal file
103
src/services/marketService.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { MarketServer } from '../types/index.js';
|
||||
|
||||
// Get path to the servers.json file
|
||||
export const getServersJsonPath = (): string => {
|
||||
return path.resolve(process.cwd(), 'servers.json');
|
||||
};
|
||||
|
||||
// Load all market servers from servers.json
|
||||
export const getMarketServers = (): Record<string, MarketServer> => {
|
||||
try {
|
||||
const serversJsonPath = getServersJsonPath();
|
||||
const data = fs.readFileSync(serversJsonPath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
} catch (error) {
|
||||
console.error('Failed to load servers from servers.json:', error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
// Get a specific market server by name
|
||||
export const getMarketServerByName = (name: string): MarketServer | null => {
|
||||
const servers = getMarketServers();
|
||||
return servers[name] || null;
|
||||
};
|
||||
|
||||
// Get all categories from market servers
|
||||
export const getMarketCategories = (): string[] => {
|
||||
const servers = getMarketServers();
|
||||
const categories = new Set<string>();
|
||||
|
||||
Object.values(servers).forEach((server) => {
|
||||
server.categories?.forEach((category) => {
|
||||
categories.add(category);
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(categories).sort();
|
||||
};
|
||||
|
||||
// Get all tags from market servers
|
||||
export const getMarketTags = (): string[] => {
|
||||
const servers = getMarketServers();
|
||||
const tags = new Set<string>();
|
||||
|
||||
Object.values(servers).forEach((server) => {
|
||||
server.tags?.forEach((tag) => {
|
||||
tags.add(tag);
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(tags).sort();
|
||||
};
|
||||
|
||||
// Search market servers by query
|
||||
export const searchMarketServers = (query: string): MarketServer[] => {
|
||||
const servers = getMarketServers();
|
||||
const searchTerms = query.toLowerCase().split(' ').filter(term => term.length > 0);
|
||||
|
||||
if (searchTerms.length === 0) {
|
||||
return Object.values(servers);
|
||||
}
|
||||
|
||||
return Object.values(servers).filter((server) => {
|
||||
// Search in name, display_name, description, categories, and tags
|
||||
const searchableText = [
|
||||
server.name,
|
||||
server.display_name,
|
||||
server.description,
|
||||
...(server.categories || []),
|
||||
...(server.tags || [])
|
||||
].join(' ').toLowerCase();
|
||||
|
||||
return searchTerms.some(term => searchableText.includes(term));
|
||||
});
|
||||
};
|
||||
|
||||
// Filter market servers by category
|
||||
export const filterMarketServersByCategory = (category: string): MarketServer[] => {
|
||||
const servers = getMarketServers();
|
||||
|
||||
if (!category) {
|
||||
return Object.values(servers);
|
||||
}
|
||||
|
||||
return Object.values(servers).filter((server) => {
|
||||
return server.categories?.includes(category);
|
||||
});
|
||||
};
|
||||
|
||||
// Filter market servers by tag
|
||||
export const filterMarketServersByTag = (tag: string): MarketServer[] => {
|
||||
const servers = getMarketServers();
|
||||
|
||||
if (!tag) {
|
||||
return Object.values(servers);
|
||||
}
|
||||
|
||||
return Object.values(servers).filter((server) => {
|
||||
return server.tags?.includes(tag);
|
||||
});
|
||||
};
|
||||
@@ -17,6 +17,60 @@ export interface IGroup {
|
||||
servers: string[]; // Array of server names that belong to this group
|
||||
}
|
||||
|
||||
// Market server types
|
||||
export interface MarketServerRepository {
|
||||
type: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export interface MarketServerAuthor {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface MarketServerInstallation {
|
||||
type: string;
|
||||
command: string;
|
||||
args: string[];
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface MarketServerArgument {
|
||||
description: string;
|
||||
required: boolean;
|
||||
example: string;
|
||||
}
|
||||
|
||||
export interface MarketServerExample {
|
||||
title: string;
|
||||
description: string;
|
||||
prompt: string;
|
||||
}
|
||||
|
||||
export interface MarketServerTool {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: Record<string, any>;
|
||||
}
|
||||
|
||||
export interface MarketServer {
|
||||
name: string;
|
||||
display_name: string;
|
||||
description: string;
|
||||
repository: MarketServerRepository;
|
||||
homepage: string;
|
||||
author: MarketServerAuthor;
|
||||
license: string;
|
||||
categories: string[];
|
||||
tags: string[];
|
||||
examples: MarketServerExample[];
|
||||
installations: {
|
||||
[key: string]: MarketServerInstallation;
|
||||
};
|
||||
arguments: Record<string, MarketServerArgument>;
|
||||
tools: MarketServerTool[];
|
||||
is_official?: boolean;
|
||||
}
|
||||
|
||||
// Represents the settings for MCP servers
|
||||
export interface McpSettings {
|
||||
users?: IUser[]; // Array of user credentials and permissions
|
||||
|
||||
Reference in New Issue
Block a user