@samanhappy/mcphub 0.12.1 → 0.12.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.fr.md +4 -0
  2. package/README.md +4 -0
  3. package/README.zh.md +4 -0
  4. package/dist/betterAuth.js +63 -4
  5. package/dist/betterAuth.js.map +1 -1
  6. package/dist/controllers/configController.js +1 -1
  7. package/dist/controllers/dxtController.js +1 -137
  8. package/dist/controllers/dxtController.js.map +1 -1
  9. package/dist/controllers/mcpbController.js +138 -0
  10. package/dist/controllers/mcpbController.js.map +1 -0
  11. package/dist/controllers/serverController.js +76 -12
  12. package/dist/controllers/serverController.js.map +1 -1
  13. package/dist/dao/DaoFactory.js +3 -4
  14. package/dist/dao/DaoFactory.js.map +1 -1
  15. package/dist/dao/examples.js +1 -1
  16. package/dist/dao/examples.js.map +1 -1
  17. package/dist/db/connection.js +146 -1
  18. package/dist/db/connection.js.map +1 -1
  19. package/dist/db/repositories/BaseRepository.js +33 -9
  20. package/dist/db/repositories/BaseRepository.js.map +1 -1
  21. package/dist/db/repositories/BearerKeyRepository.js +41 -16
  22. package/dist/db/repositories/BearerKeyRepository.js.map +1 -1
  23. package/dist/db/repositories/OAuthTokenRepository.js +107 -68
  24. package/dist/db/repositories/OAuthTokenRepository.js.map +1 -1
  25. package/dist/db/repositories/UserRepository.js +45 -18
  26. package/dist/db/repositories/UserRepository.js.map +1 -1
  27. package/dist/index.js +76 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/routes/index.js +3 -3
  30. package/dist/routes/index.js.map +1 -1
  31. package/dist/server.js +39 -2
  32. package/dist/server.js.map +1 -1
  33. package/dist/services/mcpService.js +19 -1
  34. package/dist/services/mcpService.js.map +1 -1
  35. package/dist/services/proxy.js +0 -2
  36. package/dist/services/proxy.js.map +1 -1
  37. package/dist/services/sseService.js +25 -9
  38. package/dist/services/sseService.js.map +1 -1
  39. package/dist/services/vectorSearchService.js +74 -8
  40. package/dist/services/vectorSearchService.js.map +1 -1
  41. package/dist/utils/dbRetry.js +199 -0
  42. package/dist/utils/dbRetry.js.map +1 -0
  43. package/dist/utils/smartRouting.js +13 -0
  44. package/dist/utils/smartRouting.js.map +1 -1
  45. package/frontend/dist/assets/index-AkdLybCq.js +294 -0
  46. package/frontend/dist/assets/index-AkdLybCq.js.map +1 -0
  47. package/frontend/dist/assets/index-DkQ2UJF_.css +1 -0
  48. package/frontend/dist/index.html +2 -2
  49. package/package.json +13 -9
  50. package/frontend/dist/assets/index-7Q7UOsR3.js +0 -289
  51. package/frontend/dist/assets/index-7Q7UOsR3.js.map +0 -1
  52. package/frontend/dist/assets/index-GOMK9Sm1.css +0 -1
package/README.fr.md CHANGED
@@ -18,6 +18,7 @@ MCPHub facilite la gestion et la mise à l'échelle de plusieurs serveurs MCP (M
18
18
  - **Routage intelligent** - Découverte d'outils propulsée par IA utilisant la recherche sémantique vectorielle ([En savoir plus](https://docs.mcphubx.com/features/smart-routing))
19
19
  - **Configuration à chaud** - Ajoutez, supprimez ou mettez à jour les serveurs sans temps d'arrêt
20
20
  - **Support OAuth 2.0** - Modes client et serveur pour une authentification sécurisée ([En savoir plus](https://docs.mcphubx.com/features/oauth))
21
+ - **Connexion Sociale** - Support de connexion GitHub et Google via Better Auth (nécessite le mode Base de données)
21
22
  - **Mode Base de données** - Stockez la configuration dans PostgreSQL pour les environnements de production ([En savoir plus](https://docs.mcphubx.com/configuration/database-configuration))
22
23
  - **Prêt pour Docker** - Déployez instantanément avec la configuration conteneurisée
23
24
 
@@ -67,8 +68,11 @@ http://localhost:3000/mcp # Tous les serveurs
67
68
  http://localhost:3000/mcp/{group} # Groupe spécifique
68
69
  http://localhost:3000/mcp/{server} # Serveur spécifique
69
70
  http://localhost:3000/mcp/$smart # Routage intelligent
71
+ http://localhost:3000/mcp/$smart/{group} # Routage intelligent dans un groupe
70
72
  ```
71
73
 
74
+ > **Note de sécurité** : Les points de terminaison MCP nécessitent une authentification par défaut pour éviter toute exposition accidentelle. Pour autoriser l'accès MCP sans authentification, désactivez **Activer l'authentification Bearer** dans la section Clés. **Ignorer l'authentification** n'affecte que la connexion au tableau de bord. À utiliser uniquement dans des environnements de confiance.
75
+
72
76
  📖 Consultez la [Référence API](https://docs.mcphubx.com/api-reference) pour la documentation détaillée des points de terminaison.
73
77
 
74
78
  ## 📚 Documentation
package/README.md CHANGED
@@ -18,6 +18,7 @@ MCPHub makes it easy to manage and scale multiple MCP (Model Context Protocol) s
18
18
  - **Smart Routing** - AI-powered tool discovery using vector semantic search ([Learn more](https://docs.mcphubx.com/features/smart-routing))
19
19
  - **Hot-Swappable Config** - Add, remove, or update servers without downtime
20
20
  - **OAuth 2.0 Support** - Both client and server modes for secure authentication ([Learn more](https://docs.mcphubx.com/features/oauth))
21
+ - **Social Login** - Seamless GitHub and Google login support with Better Auth integration (requires Database Mode)
21
22
  - **Database Mode** - Store configuration in PostgreSQL for production environments ([Learn more](https://docs.mcphubx.com/configuration/database-configuration))
22
23
  - **Docker-Ready** - Deploy instantly with containerized setup
23
24
 
@@ -67,8 +68,11 @@ http://localhost:3000/mcp # All servers
67
68
  http://localhost:3000/mcp/{group} # Specific group
68
69
  http://localhost:3000/mcp/{server} # Specific server
69
70
  http://localhost:3000/mcp/$smart # Smart routing
71
+ http://localhost:3000/mcp/$smart/{group} # Smart routing within group
70
72
  ```
71
73
 
74
+ > **Security note**: MCP endpoints require authentication by default to prevent accidental exposure. To allow unauthenticated MCP access, disable **Enable Bearer Authentication** in the Keys section. **Skip Authentication** only affects dashboard login. Use these only in trusted environments.
75
+
72
76
  📖 See [API Reference](https://docs.mcphubx.com/api-reference) for detailed endpoint documentation.
73
77
 
74
78
  ## 📚 Documentation
package/README.zh.md CHANGED
@@ -18,6 +18,7 @@ MCPHub 通过将多个 MCP(Model Context Protocol)服务器组织为灵活
18
18
  - **智能路由** - 基于向量语义搜索的 AI 工具发现 ([了解更多](https://docs.mcphubx.com/zh/features/smart-routing))
19
19
  - **热插拔配置** - 无需停机即可添加、移除或更新服务器
20
20
  - **OAuth 2.0 支持** - 客户端和服务端模式,实现安全认证 ([了解更多](https://docs.mcphubx.com/zh/features/oauth))
21
+ - **社交一键登录** - 通过 Better Auth 集成支持 GitHub 和 Google 快捷登录(需启用数据库模式)
21
22
  - **数据库模式** - 将配置存储在 PostgreSQL 中,适用于生产环境 ([了解更多](https://docs.mcphubx.com/zh/configuration/database-configuration))
22
23
  - **Docker 就绪** - 容器化部署,开箱即用
23
24
 
@@ -67,8 +68,11 @@ http://localhost:3000/mcp # 所有服务器
67
68
  http://localhost:3000/mcp/{group} # 特定分组
68
69
  http://localhost:3000/mcp/{server} # 特定服务器
69
70
  http://localhost:3000/mcp/$smart # 智能路由
71
+ http://localhost:3000/mcp/$smart/{group} # 智能路由(特定分组)
70
72
  ```
71
73
 
74
+ > **安全提示**:MCP 端点默认需要身份验证,以避免意外暴露。若需对 MCP 端点开放匿名访问,请在密钥设置中关闭 **启用 Bearer 认证**。**免登录开关**仅影响仪表盘登录。仅建议在受信任环境中使用。
75
+
72
76
  📖 查看 [API 参考](https://docs.mcphubx.com/zh/api-reference)了解详细的端点文档。
73
77
 
74
78
  ## 📚 文档
@@ -45,10 +45,11 @@ const databaseUrl = process.env.DB_URL;
45
45
  if (!databaseUrl) {
46
46
  throw new Error('DB_URL is required for Better Auth PostgreSQL storage.');
47
47
  }
48
+ const pool = new Pool({
49
+ connectionString: databaseUrl,
50
+ });
48
51
  const database = new PostgresDialect({
49
- pool: new Pool({
50
- connectionString: databaseUrl,
51
- }),
52
+ pool,
52
53
  });
53
54
  const authOptions = {
54
55
  baseURL,
@@ -67,15 +68,73 @@ const authOptions = {
67
68
  },
68
69
  };
69
70
  export const auth = betterAuth(authOptions);
71
+ const extractMigrationTableName = (entry) => {
72
+ if (typeof entry === 'string') {
73
+ return entry;
74
+ }
75
+ if (!entry || typeof entry !== 'object') {
76
+ return null;
77
+ }
78
+ const record = entry;
79
+ const candidate = record.tableName ?? record.table ?? record.name;
80
+ return typeof candidate === 'string' ? candidate : null;
81
+ };
82
+ const resolveCurrentSchema = async () => {
83
+ try {
84
+ const result = await pool.query('select current_schema() as schema');
85
+ return result.rows[0]?.schema || 'public';
86
+ }
87
+ catch {
88
+ return 'public';
89
+ }
90
+ };
91
+ const listExistingTables = async (schema, tableNames) => {
92
+ if (!tableNames.length) {
93
+ return new Set();
94
+ }
95
+ const result = await pool.query('select table_name from information_schema.tables where table_schema = $1 and table_name = any($2)', [schema, tableNames]);
96
+ return new Set(result.rows.map((row) => row.table_name));
97
+ };
98
+ const isRelationAlreadyExistsError = (error) => {
99
+ if (!error || typeof error !== 'object') {
100
+ return false;
101
+ }
102
+ const message = String(error.message ?? '');
103
+ return /relation\s+".*"\s+already\s+exists/i.test(message);
104
+ };
70
105
  export const ensureBetterAuthSchema = async () => {
71
106
  if (!getBetterAuthRuntimeConfig().enabled) {
72
107
  return;
73
108
  }
74
109
  const { getMigrations } = await import('better-auth/db');
75
110
  const { toBeCreated, toBeAdded, runMigrations } = await getMigrations(authOptions);
76
- if (toBeCreated.length || toBeAdded.length) {
111
+ if (!toBeCreated.length && !toBeAdded.length) {
112
+ return;
113
+ }
114
+ if (toBeCreated.length) {
115
+ const tableNames = toBeCreated
116
+ .map((entry) => extractMigrationTableName(entry))
117
+ .filter((tableName) => Boolean(tableName));
118
+ if (tableNames.length === toBeCreated.length) {
119
+ const schema = await resolveCurrentSchema();
120
+ const existingTables = await listExistingTables(schema, tableNames);
121
+ const allTablesExist = tableNames.every((tableName) => existingTables.has(tableName));
122
+ if (allTablesExist && !toBeAdded.length) {
123
+ console.warn(`[better-auth] Detected existing tables in schema "${schema}"; skipping migrations.`);
124
+ return;
125
+ }
126
+ }
127
+ }
128
+ try {
77
129
  await runMigrations();
78
130
  }
131
+ catch (error) {
132
+ if (isRelationAlreadyExistsError(error)) {
133
+ console.warn('[better-auth] Migration skipped due to existing relations.', error);
134
+ return;
135
+ }
136
+ throw error;
137
+ }
79
138
  };
80
139
  export { betterAuthRuntimeConfig };
81
140
  //# sourceMappingURL=betterAuth.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"betterAuth.js","sourceRoot":"","sources":["../src/betterAuth.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"betterAuth.js","sourceRoot":"","sources":["../src/betterAuth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAqB,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC1B,OAAO,aAAa,MAAM,mBAAmB,CAAC;AAC9C,OAAO,EACL,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,gCAAgC,CAAC;AAExC,MAAM,aAAa,GAAG,0BAA0B,EAAE,CAAC;AACnD,MAAM,eAAe,GAA+D,EAAE,CAAC;AACvF,IACE,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO;IACtC,OAAO,CAAC,GAAG,CAAC,gBAAgB;IAC5B,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAChC,CAAC;IACD,eAAe,CAAC,MAAM,GAAG;QACvB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;QACtC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;KAC/C,CAAC;AACJ,CAAC;AACD,IACE,aAAa,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO;IACtC,OAAO,CAAC,GAAG,CAAC,gBAAgB;IAC5B,OAAO,CAAC,GAAG,CAAC,oBAAoB,EAChC,CAAC;IACD,eAAe,CAAC,MAAM,GAAG;QACvB,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;QACtC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB;KAC/C,CAAC;AACJ,CAAC;AAED,MAAM,cAAc,GAAG,CAAC,OAAe,EAAE,QAAgB,EAAU,EAAE;IACnE,MAAM,cAAc,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC;IAC5E,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QAC7B,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;YAChD,GAAG,CAAC,QAAQ,GAAG,cAAc,CAAC;YAC9B,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC;QACD,IAAI,GAAG,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;YAC1C,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;QACxB,CAAC;QACD,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC;QACtE,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChD,OAAO,GAAG,WAAW,GAAG,cAAc,EAAE,CAAC;IAC3C,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,cAAc,CAC5B,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,oBAAoB,aAAa,CAAC,IAAI,GAAG,aAAa,CAAC,QAAQ,EAAE,EAChG,aAAa,CAAC,QAAQ,CACvB,CAAC;AAEF,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;AACvC,IAAI,CAAC,WAAW,EAAE,CAAC;IACjB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC5E,CAAC;AAED,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC;IACpB,gBAAgB,EAAE,WAAW;CAC9B,CAAC,CAAC;AAEH,MAAM,QAAQ,GAAG,IAAI,eAAe,CAAC;IACnC,IAAI;CACL,CAAC,CAAC;AAEH,MAAM,WAAW,GAAsB;IACrC,OAAO;IACP,QAAQ;IACR,gBAAgB,EAAE;QAChB,OAAO,EAAE,KAAK;KACf;IACD,eAAe;IACf,MAAM,EAAE;QACN,QAAQ,EAAE,KAAK;QACf,aAAa,EAAE,KAAK;QACpB,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE;YAC/B,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,KAAK,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9D,CAAC;KACF;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;AAE5C,MAAM,yBAAyB,GAAG,CAAC,KAAc,EAAiB,EAAE;IAClE,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC;IAClE,OAAO,OAAO,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC,CAAC;AAEF,MAAM,oBAAoB,GAAG,KAAK,IAAqB,EAAE;IACvD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAqB,mCAAmC,CAAC,CAAC;QACzF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,QAAQ,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,kBAAkB,GAAG,KAAK,EAAE,MAAc,EAAE,UAAoB,EAAwB,EAAE;IAC9F,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,IAAI,GAAG,EAAE,CAAC;IACnB,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAC7B,mGAAmG,EACnG,CAAC,MAAM,EAAE,UAAU,CAAC,CACrB,CAAC;IACF,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;AAC3D,CAAC,CAAC;AAEF,MAAM,4BAA4B,GAAG,CAAC,KAAc,EAAW,EAAE;IAC/D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAE,KAA8B,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACtE,OAAO,qCAAqC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7D,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,IAAmB,EAAE;IAC9D,IAAI,CAAC,0BAA0B,EAAE,CAAC,OAAO,EAAE,CAAC;QAC1C,OAAO;IACT,CAAC;IAED,MAAM,EAAE,aAAa,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;IACzD,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC;IAEnF,IAAI,CAAC,WAAW,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QAC7C,OAAO;IACT,CAAC;IAED,IAAI,WAAW,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,WAAW;aAC3B,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;aAChD,MAAM,CAAC,CAAC,SAAS,EAAuB,EAAE,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;QAClE,IAAI,UAAU,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM,EAAE,CAAC;YAC7C,MAAM,MAAM,GAAG,MAAM,oBAAoB,EAAE,CAAC;YAC5C,MAAM,cAAc,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;YACpE,MAAM,cAAc,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;YACtF,IAAI,cAAc,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;gBACxC,OAAO,CAAC,IAAI,CACV,qDAAqD,MAAM,yBAAyB,CACrF,CAAC;gBACF,OAAO;YACT,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,aAAa,EAAE,CAAC;IACxB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,4BAA4B,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,4DAA4D,EAAE,KAAK,CAAC,CAAC;YAClF,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AACF,OAAO,EAAE,uBAAuB,EAAE,CAAC"}
@@ -32,7 +32,7 @@ export const getRuntimeConfig = (req, res) => {
32
32
  };
33
33
  /**
34
34
  * Get public system configuration (only skipAuth setting)
35
- * This endpoint doesn't require authentication to allow checking if auth should be skipped
35
+ * This endpoint doesn't require authentication to allow checking if dashboard login should be skipped
36
36
  */
37
37
  export const getPublicConfig = (req, res) => {
38
38
  try {
@@ -1,138 +1,2 @@
1
- import multer from 'multer';
2
- import path from 'path';
3
- import fs from 'fs';
4
- import AdmZip from 'adm-zip';
5
- // Configure multer for file uploads
6
- const storage = multer.diskStorage({
7
- destination: (req, file, cb) => {
8
- const uploadDir = path.join(process.cwd(), 'data/uploads/dxt');
9
- if (!fs.existsSync(uploadDir)) {
10
- fs.mkdirSync(uploadDir, { recursive: true });
11
- }
12
- cb(null, uploadDir);
13
- },
14
- filename: (req, file, cb) => {
15
- const timestamp = Date.now();
16
- const originalName = path.parse(file.originalname).name;
17
- cb(null, `${originalName}-${timestamp}.dxt`);
18
- },
19
- });
20
- const upload = multer({
21
- storage,
22
- fileFilter: (req, file, cb) => {
23
- if (file.originalname.endsWith('.dxt')) {
24
- cb(null, true);
25
- }
26
- else {
27
- cb(new Error('Only .dxt files are allowed'));
28
- }
29
- },
30
- limits: {
31
- fileSize: 500 * 1024 * 1024, // 500MB limit
32
- },
33
- });
34
- export const uploadMiddleware = upload.single('dxtFile');
35
- // Clean up old DXT server files when installing a new version
36
- const cleanupOldDxtServer = (serverName) => {
37
- try {
38
- const uploadDir = path.join(process.cwd(), 'data/uploads/dxt');
39
- const serverPattern = `server-${serverName}`;
40
- if (fs.existsSync(uploadDir)) {
41
- const files = fs.readdirSync(uploadDir);
42
- files.forEach((file) => {
43
- if (file.startsWith(serverPattern)) {
44
- const filePath = path.join(uploadDir, file);
45
- if (fs.statSync(filePath).isDirectory()) {
46
- fs.rmSync(filePath, { recursive: true, force: true });
47
- console.log(`Cleaned up old DXT server directory: ${filePath}`);
48
- }
49
- }
50
- });
51
- }
52
- }
53
- catch (error) {
54
- console.warn('Failed to cleanup old DXT server files:', error);
55
- // Don't fail the installation if cleanup fails
56
- }
57
- };
58
- export const uploadDxtFile = async (req, res) => {
59
- try {
60
- if (!req.file) {
61
- res.status(400).json({
62
- success: false,
63
- message: 'No DXT file uploaded',
64
- });
65
- return;
66
- }
67
- const dxtFilePath = req.file.path;
68
- const timestamp = Date.now();
69
- const tempExtractDir = path.join(path.dirname(dxtFilePath), `temp-extracted-${timestamp}`);
70
- try {
71
- // Extract the DXT file (which is a ZIP archive) to a temporary directory first
72
- const zip = new AdmZip(dxtFilePath);
73
- zip.extractAllTo(tempExtractDir, true);
74
- // Read and validate the manifest.json
75
- const manifestPath = path.join(tempExtractDir, 'manifest.json');
76
- if (!fs.existsSync(manifestPath)) {
77
- throw new Error('manifest.json not found in DXT file');
78
- }
79
- const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
80
- const manifest = JSON.parse(manifestContent);
81
- // Validate required fields in manifest
82
- if (!manifest.dxt_version) {
83
- throw new Error('Invalid manifest: missing dxt_version');
84
- }
85
- if (!manifest.name) {
86
- throw new Error('Invalid manifest: missing name');
87
- }
88
- if (!manifest.version) {
89
- throw new Error('Invalid manifest: missing version');
90
- }
91
- if (!manifest.server) {
92
- throw new Error('Invalid manifest: missing server configuration');
93
- }
94
- // Use server name as the final extract directory for automatic version management
95
- const finalExtractDir = path.join(path.dirname(dxtFilePath), `server-${manifest.name}`);
96
- // Clean up any existing version of this server
97
- cleanupOldDxtServer(manifest.name);
98
- if (!fs.existsSync(finalExtractDir)) {
99
- fs.mkdirSync(finalExtractDir, { recursive: true });
100
- }
101
- // Move the temporary directory to the final location
102
- fs.renameSync(tempExtractDir, finalExtractDir);
103
- console.log(`DXT server extracted to: ${finalExtractDir}`);
104
- // Clean up the uploaded DXT file
105
- fs.unlinkSync(dxtFilePath);
106
- const response = {
107
- success: true,
108
- data: {
109
- manifest,
110
- extractDir: finalExtractDir,
111
- },
112
- };
113
- res.json(response);
114
- }
115
- catch (extractError) {
116
- // Clean up files on error
117
- if (fs.existsSync(dxtFilePath)) {
118
- fs.unlinkSync(dxtFilePath);
119
- }
120
- if (fs.existsSync(tempExtractDir)) {
121
- fs.rmSync(tempExtractDir, { recursive: true, force: true });
122
- }
123
- throw extractError;
124
- }
125
- }
126
- catch (error) {
127
- console.error('DXT upload error:', error);
128
- let message = 'Failed to process DXT file';
129
- if (error instanceof Error) {
130
- message = error.message;
131
- }
132
- res.status(500).json({
133
- success: false,
134
- message,
135
- });
136
- }
137
- };
1
+ export { uploadMiddleware, uploadMcpbFile } from './mcpbController.js';
138
2
  //# sourceMappingURL=dxtController.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dxtController.js","sourceRoot":"","sources":["../../src/controllers/dxtController.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dxtController.js","sourceRoot":"","sources":["../../src/controllers/dxtController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC"}
@@ -0,0 +1,138 @@
1
+ import multer from 'multer';
2
+ import path from 'path';
3
+ import fs from 'fs';
4
+ import AdmZip from 'adm-zip';
5
+ // Configure multer for file uploads
6
+ const storage = multer.diskStorage({
7
+ destination: (_req, _file, cb) => {
8
+ const uploadDir = path.join(process.cwd(), 'data/uploads/mcpb');
9
+ if (!fs.existsSync(uploadDir)) {
10
+ fs.mkdirSync(uploadDir, { recursive: true });
11
+ }
12
+ cb(null, uploadDir);
13
+ },
14
+ filename: (_req, file, cb) => {
15
+ const timestamp = Date.now();
16
+ const originalName = path.parse(file.originalname).name;
17
+ cb(null, `${originalName}-${timestamp}.mcpb`);
18
+ },
19
+ });
20
+ const upload = multer({
21
+ storage,
22
+ fileFilter: (_req, file, cb) => {
23
+ if (file.originalname.endsWith('.mcpb')) {
24
+ cb(null, true);
25
+ }
26
+ else {
27
+ cb(new Error('Only .mcpb files are allowed'));
28
+ }
29
+ },
30
+ limits: {
31
+ fileSize: 500 * 1024 * 1024, // 500MB limit
32
+ },
33
+ });
34
+ export const uploadMiddleware = upload.single('mcpbFile');
35
+ // Clean up old MCPB server files when installing a new version
36
+ const cleanupOldMcpbServer = (serverName) => {
37
+ try {
38
+ const uploadDir = path.join(process.cwd(), 'data/uploads/mcpb');
39
+ const serverPattern = `server-${serverName}`;
40
+ if (fs.existsSync(uploadDir)) {
41
+ const files = fs.readdirSync(uploadDir);
42
+ files.forEach((file) => {
43
+ if (file.startsWith(serverPattern)) {
44
+ const filePath = path.join(uploadDir, file);
45
+ if (fs.statSync(filePath).isDirectory()) {
46
+ fs.rmSync(filePath, { recursive: true, force: true });
47
+ console.log(`Cleaned up old MCPB server directory: ${filePath}`);
48
+ }
49
+ }
50
+ });
51
+ }
52
+ }
53
+ catch (error) {
54
+ console.warn('Failed to cleanup old MCPB server files:', error);
55
+ // Don't fail the installation if cleanup fails
56
+ }
57
+ };
58
+ export const uploadMcpbFile = async (req, res) => {
59
+ try {
60
+ if (!req.file) {
61
+ res.status(400).json({
62
+ success: false,
63
+ message: 'No MCPB file uploaded',
64
+ });
65
+ return;
66
+ }
67
+ const mcpbFilePath = req.file.path;
68
+ const timestamp = Date.now();
69
+ const tempExtractDir = path.join(path.dirname(mcpbFilePath), `temp-extracted-${timestamp}`);
70
+ try {
71
+ // Extract the MCPB file (which is a ZIP archive) to a temporary directory first
72
+ const zip = new AdmZip(mcpbFilePath);
73
+ zip.extractAllTo(tempExtractDir, true);
74
+ // Read and validate the manifest.json
75
+ const manifestPath = path.join(tempExtractDir, 'manifest.json');
76
+ if (!fs.existsSync(manifestPath)) {
77
+ throw new Error('manifest.json not found in MCPB file');
78
+ }
79
+ const manifestContent = fs.readFileSync(manifestPath, 'utf-8');
80
+ const manifest = JSON.parse(manifestContent);
81
+ // Validate required fields in manifest
82
+ if (!manifest.manifest_version) {
83
+ throw new Error('Invalid manifest: missing manifest_version');
84
+ }
85
+ if (!manifest.name) {
86
+ throw new Error('Invalid manifest: missing name');
87
+ }
88
+ if (!manifest.version) {
89
+ throw new Error('Invalid manifest: missing version');
90
+ }
91
+ if (!manifest.server) {
92
+ throw new Error('Invalid manifest: missing server configuration');
93
+ }
94
+ // Use server name as the final extract directory for automatic version management
95
+ const finalExtractDir = path.join(path.dirname(mcpbFilePath), `server-${manifest.name}`);
96
+ // Clean up any existing version of this server
97
+ cleanupOldMcpbServer(manifest.name);
98
+ if (!fs.existsSync(finalExtractDir)) {
99
+ fs.mkdirSync(finalExtractDir, { recursive: true });
100
+ }
101
+ // Move the temporary directory to the final location
102
+ fs.renameSync(tempExtractDir, finalExtractDir);
103
+ console.log(`MCPB server extracted to: ${finalExtractDir}`);
104
+ // Clean up the uploaded MCPB file
105
+ fs.unlinkSync(mcpbFilePath);
106
+ const response = {
107
+ success: true,
108
+ data: {
109
+ manifest,
110
+ extractDir: finalExtractDir,
111
+ },
112
+ };
113
+ res.json(response);
114
+ }
115
+ catch (extractError) {
116
+ // Clean up files on error
117
+ if (fs.existsSync(mcpbFilePath)) {
118
+ fs.unlinkSync(mcpbFilePath);
119
+ }
120
+ if (fs.existsSync(tempExtractDir)) {
121
+ fs.rmSync(tempExtractDir, { recursive: true, force: true });
122
+ }
123
+ throw extractError;
124
+ }
125
+ }
126
+ catch (error) {
127
+ console.error('MCPB upload error:', error);
128
+ let message = 'Failed to process MCPB file';
129
+ if (error instanceof Error) {
130
+ message = error.message;
131
+ }
132
+ res.status(500).json({
133
+ success: false,
134
+ message,
135
+ });
136
+ }
137
+ };
138
+ //# sourceMappingURL=mcpbController.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcpbController.js","sourceRoot":"","sources":["../../src/controllers/mcpbController.ts"],"names":[],"mappings":"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"}
@@ -769,9 +769,14 @@ export const updateSystemConfig = async (req, res) => {
769
769
  const hasSmartRoutingUpdate = smartRouting &&
770
770
  (typeof smartRouting.enabled === 'boolean' ||
771
771
  typeof smartRouting.dbUrl === 'string' ||
772
+ typeof smartRouting.embeddingProvider === 'string' ||
772
773
  typeof smartRouting.openaiApiBaseUrl === 'string' ||
773
774
  typeof smartRouting.openaiApiKey === 'string' ||
774
775
  typeof smartRouting.openaiApiEmbeddingModel === 'string' ||
776
+ typeof smartRouting.azureOpenaiEndpoint === 'string' ||
777
+ typeof smartRouting.azureOpenaiApiKey === 'string' ||
778
+ typeof smartRouting.azureOpenaiApiVersion === 'string' ||
779
+ typeof smartRouting.azureOpenaiEmbeddingDeployment === 'string' ||
775
780
  typeof smartRouting.progressiveDisclosure === 'boolean');
776
781
  const hasMcpRouterUpdate = mcpRouter &&
777
782
  (typeof mcpRouter.apiKey === 'string' ||
@@ -813,7 +818,7 @@ export const updateSystemConfig = async (req, res) => {
813
818
  routing: {
814
819
  enableGlobalRoute: true,
815
820
  enableGroupNameRoute: true,
816
- enableBearerAuth: false,
821
+ enableBearerAuth: true,
817
822
  bearerAuthKey: '',
818
823
  skipAuth: false,
819
824
  },
@@ -825,9 +830,14 @@ export const updateSystemConfig = async (req, res) => {
825
830
  smartRouting: {
826
831
  enabled: false,
827
832
  dbUrl: '',
833
+ embeddingProvider: 'openai',
828
834
  openaiApiBaseUrl: '',
829
835
  openaiApiKey: '',
830
836
  openaiApiEmbeddingModel: '',
837
+ azureOpenaiEndpoint: '',
838
+ azureOpenaiApiKey: '',
839
+ azureOpenaiApiVersion: '',
840
+ azureOpenaiEmbeddingDeployment: '',
831
841
  },
832
842
  mcpRouter: {
833
843
  apiKey: '',
@@ -842,7 +852,7 @@ export const updateSystemConfig = async (req, res) => {
842
852
  systemConfig.routing = {
843
853
  enableGlobalRoute: true,
844
854
  enableGroupNameRoute: true,
845
- enableBearerAuth: false,
855
+ enableBearerAuth: true,
846
856
  bearerAuthKey: '',
847
857
  skipAuth: false,
848
858
  };
@@ -858,9 +868,14 @@ export const updateSystemConfig = async (req, res) => {
858
868
  systemConfig.smartRouting = {
859
869
  enabled: false,
860
870
  dbUrl: '',
871
+ embeddingProvider: 'openai',
861
872
  openaiApiBaseUrl: '',
862
873
  openaiApiKey: '',
863
874
  openaiApiEmbeddingModel: '',
875
+ azureOpenaiEndpoint: '',
876
+ azureOpenaiApiKey: '',
877
+ azureOpenaiApiVersion: '',
878
+ azureOpenaiEmbeddingDeployment: '',
864
879
  };
865
880
  }
866
881
  if (!systemConfig.mcpRouter) {
@@ -924,23 +939,49 @@ export const updateSystemConfig = async (req, res) => {
924
939
  const previousSmartRoutingConfig = { ...systemConfig.smartRouting };
925
940
  let needsSync = false;
926
941
  if (smartRouting) {
942
+ if (typeof smartRouting.embeddingProvider === 'string') {
943
+ const normalized = smartRouting.embeddingProvider.trim().toLowerCase();
944
+ systemConfig.smartRouting.embeddingProvider =
945
+ normalized === 'azure' || normalized === 'azure_openai' ? 'azure_openai' : 'openai';
946
+ }
927
947
  if (typeof smartRouting.enabled === 'boolean') {
928
948
  // If enabling Smart Routing, validate required fields
929
949
  if (smartRouting.enabled) {
930
950
  const currentDbUrl = process.env.DB_URL || smartRouting.dbUrl || systemConfig.smartRouting.dbUrl;
931
- const currentOpenaiApiKey = smartRouting.openaiApiKey || systemConfig.smartRouting.openaiApiKey;
932
- if (!currentDbUrl || !currentOpenaiApiKey) {
933
- const missingFields = [];
934
- if (!currentDbUrl)
935
- missingFields.push('Database URL');
936
- if (!currentOpenaiApiKey)
937
- missingFields.push('OpenAI API Key');
951
+ if (!currentDbUrl) {
938
952
  res.status(400).json({
939
- success: false,
940
- message: `Smart Routing requires the following fields: ${missingFields.join(', ')}`,
953
+ message: 'Smart routing cannot be enabled without Database URL. Please provide DB URL.',
941
954
  });
942
955
  return;
943
956
  }
957
+ const effectiveProvider = (typeof smartRouting.embeddingProvider === 'string'
958
+ ? smartRouting.embeddingProvider
959
+ : systemConfig.smartRouting.embeddingProvider) || 'openai';
960
+ if (effectiveProvider === 'azure_openai') {
961
+ const currentAzureEndpoint = smartRouting.azureOpenaiEndpoint || systemConfig.smartRouting.azureOpenaiEndpoint;
962
+ const currentAzureKey = smartRouting.azureOpenaiApiKey || systemConfig.smartRouting.azureOpenaiApiKey;
963
+ const currentAzureDeployment = smartRouting.azureOpenaiEmbeddingDeployment ||
964
+ systemConfig.smartRouting.azureOpenaiEmbeddingDeployment;
965
+ const currentAzureApiVersion = smartRouting.azureOpenaiApiVersion || systemConfig.smartRouting.azureOpenaiApiVersion;
966
+ if (!currentAzureEndpoint ||
967
+ !currentAzureKey ||
968
+ !currentAzureApiVersion ||
969
+ !currentAzureDeployment) {
970
+ res.status(400).json({
971
+ message: 'Smart routing cannot be enabled without Azure OpenAI configuration. Please provide endpoint, API key, embedding deployment, and API version.',
972
+ });
973
+ return;
974
+ }
975
+ }
976
+ else {
977
+ const currentOpenAiKey = smartRouting.openaiApiKey || systemConfig.smartRouting.openaiApiKey;
978
+ if (!currentOpenAiKey) {
979
+ res.status(400).json({
980
+ message: 'Smart routing cannot be enabled without OpenAI API key. Please provide an OpenAI API key.',
981
+ });
982
+ return;
983
+ }
984
+ }
944
985
  }
945
986
  systemConfig.smartRouting.enabled = smartRouting.enabled;
946
987
  }
@@ -956,17 +997,40 @@ export const updateSystemConfig = async (req, res) => {
956
997
  if (typeof smartRouting.openaiApiEmbeddingModel === 'string') {
957
998
  systemConfig.smartRouting.openaiApiEmbeddingModel = smartRouting.openaiApiEmbeddingModel;
958
999
  }
1000
+ if (typeof smartRouting.azureOpenaiEndpoint === 'string') {
1001
+ systemConfig.smartRouting.azureOpenaiEndpoint = smartRouting.azureOpenaiEndpoint;
1002
+ }
1003
+ if (typeof smartRouting.azureOpenaiApiKey === 'string') {
1004
+ systemConfig.smartRouting.azureOpenaiApiKey = smartRouting.azureOpenaiApiKey;
1005
+ }
1006
+ if (typeof smartRouting.azureOpenaiApiVersion === 'string') {
1007
+ systemConfig.smartRouting.azureOpenaiApiVersion = smartRouting.azureOpenaiApiVersion;
1008
+ }
1009
+ if (typeof smartRouting.azureOpenaiEmbeddingDeployment === 'string') {
1010
+ systemConfig.smartRouting.azureOpenaiEmbeddingDeployment =
1011
+ smartRouting.azureOpenaiEmbeddingDeployment;
1012
+ }
959
1013
  if (typeof smartRouting.progressiveDisclosure === 'boolean') {
960
1014
  systemConfig.smartRouting.progressiveDisclosure = smartRouting.progressiveDisclosure;
961
1015
  }
962
1016
  // Check if we need to sync embeddings
963
1017
  const isNowEnabled = systemConfig.smartRouting.enabled || false;
964
1018
  const hasConfigChanged = previousSmartRoutingConfig.dbUrl !== systemConfig.smartRouting.dbUrl ||
1019
+ previousSmartRoutingConfig.embeddingProvider !==
1020
+ systemConfig.smartRouting.embeddingProvider ||
965
1021
  previousSmartRoutingConfig.openaiApiBaseUrl !==
966
1022
  systemConfig.smartRouting.openaiApiBaseUrl ||
967
1023
  previousSmartRoutingConfig.openaiApiKey !== systemConfig.smartRouting.openaiApiKey ||
968
1024
  previousSmartRoutingConfig.openaiApiEmbeddingModel !==
969
- systemConfig.smartRouting.openaiApiEmbeddingModel;
1025
+ systemConfig.smartRouting.openaiApiEmbeddingModel ||
1026
+ previousSmartRoutingConfig.azureOpenaiEndpoint !==
1027
+ systemConfig.smartRouting.azureOpenaiEndpoint ||
1028
+ previousSmartRoutingConfig.azureOpenaiApiKey !==
1029
+ systemConfig.smartRouting.azureOpenaiApiKey ||
1030
+ previousSmartRoutingConfig.azureOpenaiApiVersion !==
1031
+ systemConfig.smartRouting.azureOpenaiApiVersion ||
1032
+ previousSmartRoutingConfig.azureOpenaiEmbeddingDeployment !==
1033
+ systemConfig.smartRouting.azureOpenaiEmbeddingDeployment;
970
1034
  // Sync if: first time enabling OR smart routing is enabled and any config changed
971
1035
  needsSync = (!wasSmartRoutingEnabled && isNowEnabled) || (isNowEnabled && hasConfigChanged);
972
1036
  }