@samanhappy/mcphub 0.0.10 → 0.0.12

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 (97) hide show
  1. package/package.json +8 -5
  2. package/.env.example +0 -2
  3. package/.eslintrc.json +0 -25
  4. package/.github/workflows/build.yml +0 -51
  5. package/.github/workflows/release.yml +0 -19
  6. package/.prettierrc +0 -7
  7. package/Dockerfile +0 -51
  8. package/assets/amap-edit.png +0 -0
  9. package/assets/amap-result.png +0 -0
  10. package/assets/cherry-mcp.png +0 -0
  11. package/assets/cursor-mcp.png +0 -0
  12. package/assets/cursor-query.png +0 -0
  13. package/assets/cursor-tools.png +0 -0
  14. package/assets/dashboard.png +0 -0
  15. package/assets/dashboard.zh.png +0 -0
  16. package/assets/group.png +0 -0
  17. package/assets/group.zh.png +0 -0
  18. package/assets/market.zh.png +0 -0
  19. package/assets/wegroup.jpg +0 -0
  20. package/assets/wegroup.png +0 -0
  21. package/assets/wexin.png +0 -0
  22. package/bin/mcphub.js +0 -3
  23. package/doc/intro.md +0 -73
  24. package/doc/intro2.md +0 -232
  25. package/entrypoint.sh +0 -10
  26. package/frontend/favicon.ico +0 -0
  27. package/frontend/index.html +0 -13
  28. package/frontend/postcss.config.js +0 -6
  29. package/frontend/src/App.tsx +0 -44
  30. package/frontend/src/components/AddGroupForm.tsx +0 -132
  31. package/frontend/src/components/AddServerForm.tsx +0 -90
  32. package/frontend/src/components/ChangePasswordForm.tsx +0 -158
  33. package/frontend/src/components/EditGroupForm.tsx +0 -149
  34. package/frontend/src/components/EditServerForm.tsx +0 -76
  35. package/frontend/src/components/GroupCard.tsx +0 -143
  36. package/frontend/src/components/MarketServerCard.tsx +0 -153
  37. package/frontend/src/components/MarketServerDetail.tsx +0 -297
  38. package/frontend/src/components/ProtectedRoute.tsx +0 -27
  39. package/frontend/src/components/ServerCard.tsx +0 -230
  40. package/frontend/src/components/ServerForm.tsx +0 -276
  41. package/frontend/src/components/icons/LucideIcons.tsx +0 -14
  42. package/frontend/src/components/layout/Content.tsx +0 -17
  43. package/frontend/src/components/layout/Header.tsx +0 -61
  44. package/frontend/src/components/layout/Sidebar.tsx +0 -98
  45. package/frontend/src/components/ui/Badge.tsx +0 -33
  46. package/frontend/src/components/ui/Button.tsx +0 -0
  47. package/frontend/src/components/ui/DeleteDialog.tsx +0 -48
  48. package/frontend/src/components/ui/Pagination.tsx +0 -128
  49. package/frontend/src/components/ui/Toast.tsx +0 -96
  50. package/frontend/src/components/ui/ToggleGroup.tsx +0 -134
  51. package/frontend/src/components/ui/ToolCard.tsx +0 -38
  52. package/frontend/src/contexts/AuthContext.tsx +0 -159
  53. package/frontend/src/contexts/ToastContext.tsx +0 -60
  54. package/frontend/src/hooks/useGroupData.ts +0 -232
  55. package/frontend/src/hooks/useMarketData.ts +0 -410
  56. package/frontend/src/hooks/useServerData.ts +0 -306
  57. package/frontend/src/hooks/useSettingsData.ts +0 -131
  58. package/frontend/src/i18n.ts +0 -42
  59. package/frontend/src/index.css +0 -20
  60. package/frontend/src/layouts/MainLayout.tsx +0 -33
  61. package/frontend/src/locales/en.json +0 -214
  62. package/frontend/src/locales/zh.json +0 -214
  63. package/frontend/src/main.tsx +0 -12
  64. package/frontend/src/pages/Dashboard.tsx +0 -206
  65. package/frontend/src/pages/GroupsPage.tsx +0 -116
  66. package/frontend/src/pages/LoginPage.tsx +0 -104
  67. package/frontend/src/pages/MarketPage.tsx +0 -356
  68. package/frontend/src/pages/ServersPage.tsx +0 -144
  69. package/frontend/src/pages/SettingsPage.tsx +0 -149
  70. package/frontend/src/services/authService.ts +0 -141
  71. package/frontend/src/types/index.ts +0 -160
  72. package/frontend/src/utils/cn.ts +0 -10
  73. package/frontend/tsconfig.json +0 -31
  74. package/frontend/tsconfig.node.json +0 -10
  75. package/frontend/vite.config.ts +0 -26
  76. package/googled76ca578b6543fbc.html +0 -1
  77. package/jest.config.js +0 -10
  78. package/mcp_settings.json +0 -45
  79. package/servers.json +0 -74722
  80. package/src/config/index.ts +0 -46
  81. package/src/controllers/authController.ts +0 -179
  82. package/src/controllers/groupController.ts +0 -341
  83. package/src/controllers/marketController.ts +0 -154
  84. package/src/controllers/serverController.ts +0 -303
  85. package/src/index.ts +0 -17
  86. package/src/middlewares/auth.ts +0 -28
  87. package/src/middlewares/index.ts +0 -43
  88. package/src/models/User.ts +0 -103
  89. package/src/routes/index.ts +0 -96
  90. package/src/server.ts +0 -72
  91. package/src/services/groupService.ts +0 -232
  92. package/src/services/marketService.ts +0 -116
  93. package/src/services/mcpService.ts +0 -385
  94. package/src/services/sseService.ts +0 -119
  95. package/src/types/index.ts +0 -129
  96. package/src/utils/migration.ts +0 -52
  97. package/tsconfig.json +0 -17
@@ -1,206 +0,0 @@
1
- import React from 'react';
2
- import { useTranslation } from 'react-i18next';
3
- import { useServerData } from '@/hooks/useServerData';
4
- import { ServerStatus } from '@/types';
5
-
6
- const DashboardPage: React.FC = () => {
7
- const { t } = useTranslation();
8
- const { servers, error, setError, isLoading } = useServerData();
9
-
10
- // Calculate server statistics
11
- const serverStats = {
12
- total: servers.length,
13
- online: servers.filter(server => server.status === 'connected').length,
14
- offline: servers.filter(server => server.status === 'disconnected').length,
15
- connecting: servers.filter(server => server.status === 'connecting').length
16
- };
17
-
18
- // Map status to translation keys
19
- const statusTranslations = {
20
- connected: 'status.online',
21
- disconnected: 'status.offline',
22
- connecting: 'status.connecting'
23
- }
24
-
25
- // Calculate percentage for each status (for dashboard display)
26
- const getStatusPercentage = (status: ServerStatus) => {
27
- if (servers.length === 0) return 0;
28
- return Math.round((servers.filter(server => server.status === status).length / servers.length) * 100);
29
- };
30
-
31
- return (
32
- <div>
33
- <h1 className="text-2xl font-bold text-gray-900 mb-8">{t('pages.dashboard.title')}</h1>
34
-
35
- {error && (
36
- <div className="mb-6 bg-red-50 border-l-4 border-red-500 p-4 rounded shadow-sm">
37
- <div className="flex items-center justify-between">
38
- <div>
39
- <h3 className="text-red-600 text-lg font-medium">{t('app.error')}</h3>
40
- <p className="text-gray-600 mt-1">{error}</p>
41
- </div>
42
- <button
43
- onClick={() => setError(null)}
44
- className="ml-4 text-gray-500 hover:text-gray-700"
45
- aria-label={t('app.closeButton')}
46
- >
47
- <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
48
- <path fillRule="evenodd" d="M4.293 4.293a1 1 011.414 0L10 8.586l4.293-4.293a1 1 111.414 1.414L11.414 10l4.293 4.293a1 1 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 01-1.414-1.414L8.586 10 4.293 5.707a1 1 010-1.414z" clipRule="evenodd" />
49
- </svg>
50
- </button>
51
- </div>
52
- </div>
53
- )}
54
-
55
- {isLoading ? (
56
- <div className="bg-white shadow rounded-lg p-6 flex items-center justify-center">
57
- <div className="flex flex-col items-center">
58
- <svg className="animate-spin h-10 w-10 text-blue-500 mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
59
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
60
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
61
- </svg>
62
- <p className="text-gray-600">{t('app.loading')}</p>
63
- </div>
64
- </div>
65
- ) : (
66
- <div className="grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
67
- {/* Total servers */}
68
- <div className="bg-white rounded-lg shadow p-6">
69
- <div className="flex items-center">
70
- <div className="p-3 rounded-full bg-blue-100 text-blue-800">
71
- <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
72
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01" />
73
- </svg>
74
- </div>
75
- <div className="ml-4">
76
- <h2 className="text-xl font-semibold text-gray-700">{t('pages.dashboard.totalServers')}</h2>
77
- <p className="text-3xl font-bold text-gray-900">{serverStats.total}</p>
78
- </div>
79
- </div>
80
- </div>
81
-
82
- {/* Online servers */}
83
- <div className="bg-white rounded-lg shadow p-6">
84
- <div className="flex items-center">
85
- <div className="p-3 rounded-full bg-green-100 text-green-800">
86
- <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
87
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
88
- </svg>
89
- </div>
90
- <div className="ml-4">
91
- <h2 className="text-xl font-semibold text-gray-700">{t('pages.dashboard.onlineServers')}</h2>
92
- <p className="text-3xl font-bold text-gray-900">{serverStats.online}</p>
93
- </div>
94
- </div>
95
- <div className="mt-4 h-2 bg-gray-200 rounded-full">
96
- <div
97
- className="h-full bg-green-500 rounded-full"
98
- style={{ width: `${getStatusPercentage('connected')}%` }}
99
- ></div>
100
- </div>
101
- </div>
102
-
103
- {/* Offline servers */}
104
- <div className="bg-white rounded-lg shadow p-6">
105
- <div className="flex items-center">
106
- <div className="p-3 rounded-full bg-red-100 text-red-800">
107
- <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
108
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
109
- </svg>
110
- </div>
111
- <div className="ml-4">
112
- <h2 className="text-xl font-semibold text-gray-700">{t('pages.dashboard.offlineServers')}</h2>
113
- <p className="text-3xl font-bold text-gray-900">{serverStats.offline}</p>
114
- </div>
115
- </div>
116
- <div className="mt-4 h-2 bg-gray-200 rounded-full">
117
- <div
118
- className="h-full bg-red-500 rounded-full"
119
- style={{ width: `${getStatusPercentage('disconnected')}%` }}
120
- ></div>
121
- </div>
122
- </div>
123
-
124
- {/* Connecting servers */}
125
- <div className="bg-white rounded-lg shadow p-6">
126
- <div className="flex items-center">
127
- <div className="p-3 rounded-full bg-yellow-100 text-yellow-800">
128
- <svg xmlns="http://www.w3.org/2000/svg" className="h-8 w-8" fill="none" viewBox="0 0 24 24" stroke="currentColor">
129
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
130
- </svg>
131
- </div>
132
- <div className="ml-4">
133
- <h2 className="text-xl font-semibold text-gray-700">{t('pages.dashboard.connectingServers')}</h2>
134
- <p className="text-3xl font-bold text-gray-900">{serverStats.connecting}</p>
135
- </div>
136
- </div>
137
- <div className="mt-4 h-2 bg-gray-200 rounded-full">
138
- <div
139
- className="h-full bg-yellow-500 rounded-full"
140
- style={{ width: `${getStatusPercentage('connecting')}%` }}
141
- ></div>
142
- </div>
143
- </div>
144
- </div>
145
- )}
146
-
147
- {/* Recent activity list */}
148
- {servers.length > 0 && !isLoading && (
149
- <div className="mt-8">
150
- <h2 className="text-xl font-semibold text-gray-900 mb-4">{t('pages.dashboard.recentServers')}</h2>
151
- <div className="bg-white shadow rounded-lg overflow-hidden">
152
- <table className="min-w-full divide-y divide-gray-200">
153
- <thead className="bg-gray-50">
154
- <tr>
155
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
156
- {t('server.name')}
157
- </th>
158
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
159
- {t('server.status')}
160
- </th>
161
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
162
- {t('server.tools')}
163
- </th>
164
- <th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
165
- {t('server.enabled')}
166
- </th>
167
- </tr>
168
- </thead>
169
- <tbody className="bg-white divide-y divide-gray-200">
170
- {servers.slice(0, 5).map((server, index) => (
171
- <tr key={index}>
172
- <td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
173
- {server.name}
174
- </td>
175
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
176
- <span className={`px-2 inline-flex text-xs leading-5 font-semibold rounded-full ${server.status === 'connected'
177
- ? 'bg-green-100 text-green-800'
178
- : server.status === 'disconnected'
179
- ? 'bg-red-100 text-red-800'
180
- : 'bg-yellow-100 text-yellow-800'
181
- }`}>
182
- {t(statusTranslations[server.status] || server.status)}
183
- </span>
184
- </td>
185
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
186
- {server.tools?.length || 0}
187
- </td>
188
- <td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
189
- {server.enabled !== false ? (
190
- <span className="text-green-600">✓</span>
191
- ) : (
192
- <span className="text-red-600">✗</span>
193
- )}
194
- </td>
195
- </tr>
196
- ))}
197
- </tbody>
198
- </table>
199
- </div>
200
- </div>
201
- )}
202
- </div>
203
- );
204
- };
205
-
206
- export default DashboardPage;
@@ -1,116 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { useTranslation } from 'react-i18next';
3
- import { Group } from '@/types';
4
- import { useGroupData } from '@/hooks/useGroupData';
5
- import { useServerData } from '@/hooks/useServerData';
6
- import AddGroupForm from '@/components/AddGroupForm';
7
- import EditGroupForm from '@/components/EditGroupForm';
8
- import GroupCard from '@/components/GroupCard';
9
-
10
- const GroupsPage: React.FC = () => {
11
- const { t } = useTranslation();
12
- const {
13
- groups,
14
- loading: groupsLoading,
15
- error: groupError,
16
- setError: setGroupError,
17
- deleteGroup,
18
- triggerRefresh
19
- } = useGroupData();
20
- const { servers } = useServerData();
21
-
22
- const [editingGroup, setEditingGroup] = useState<Group | null>(null);
23
- const [showAddForm, setShowAddForm] = useState(false);
24
-
25
- const handleEditClick = (group: Group) => {
26
- setEditingGroup(group);
27
- };
28
-
29
- const handleEditComplete = () => {
30
- setEditingGroup(null);
31
- triggerRefresh(); // Refresh the groups list after editing
32
- };
33
-
34
- const handleDeleteGroup = async (groupId: string) => {
35
- const success = await deleteGroup(groupId);
36
- if (!success) {
37
- setGroupError(t('groups.deleteError'));
38
- }
39
- };
40
-
41
- const handleAddGroup = () => {
42
- setShowAddForm(true);
43
- };
44
-
45
- const handleAddComplete = () => {
46
- setShowAddForm(false);
47
- triggerRefresh(); // Refresh the groups list after adding
48
- };
49
-
50
- return (
51
- <div>
52
- <div className="flex justify-between items-center mb-8">
53
- <h1 className="text-2xl font-bold text-gray-900">{t('pages.groups.title')}</h1>
54
- <div className="flex space-x-4">
55
- <button
56
- onClick={handleAddGroup}
57
- className="px-4 py-2 bg-blue-100 text-blue-800 rounded hover:bg-blue-200 flex items-center"
58
- >
59
- <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 mr-2" viewBox="0 0 20 20" fill="currentColor">
60
- <path fillRule="evenodd" d="M10 3a1 1 0 00-1 1v5H4a1 1 0 100 2h5v5a1 1 0 102 0v-5h5a1 1 0 100-2h-5V4a1 1 0 00-1-1z" clipRule="evenodd" />
61
- </svg>
62
- {t('groups.add')}
63
- </button>
64
- </div>
65
- </div>
66
-
67
- {groupError && (
68
- <div className="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6">
69
- <p>{groupError}</p>
70
- </div>
71
- )}
72
-
73
- {groupsLoading ? (
74
- <div className="bg-white shadow rounded-lg p-6">
75
- <div className="flex flex-col items-center justify-center">
76
- <svg className="animate-spin h-10 w-10 text-blue-500 mb-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
77
- <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
78
- <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
79
- </svg>
80
- <p className="text-gray-600">{t('app.loading')}</p>
81
- </div>
82
- </div>
83
- ) : groups.length === 0 ? (
84
- <div className="bg-white shadow rounded-lg p-6">
85
- <p className="text-gray-600">{t('groups.noGroups')}</p>
86
- </div>
87
- ) : (
88
- <div className="space-y-6">
89
- {groups.map((group) => (
90
- <GroupCard
91
- key={group.id}
92
- group={group}
93
- servers={servers}
94
- onEdit={handleEditClick}
95
- onDelete={handleDeleteGroup}
96
- />
97
- ))}
98
- </div>
99
- )}
100
-
101
- {showAddForm && (
102
- <AddGroupForm onAdd={handleAddComplete} onCancel={handleAddComplete} />
103
- )}
104
-
105
- {editingGroup && (
106
- <EditGroupForm
107
- group={editingGroup}
108
- onEdit={handleEditComplete}
109
- onCancel={() => setEditingGroup(null)}
110
- />
111
- )}
112
- </div>
113
- );
114
- };
115
-
116
- export default GroupsPage;
@@ -1,104 +0,0 @@
1
- import React, { useState } from 'react';
2
- import { useNavigate } from 'react-router-dom';
3
- import { useTranslation } from 'react-i18next';
4
- import { useAuth } from '../contexts/AuthContext';
5
-
6
- const LoginPage: React.FC = () => {
7
- const { t } = useTranslation();
8
- const [username, setUsername] = useState('');
9
- const [password, setPassword] = useState('');
10
- const [error, setError] = useState<string | null>(null);
11
- const [loading, setLoading] = useState(false);
12
- const { login } = useAuth();
13
- const navigate = useNavigate();
14
-
15
- const handleSubmit = async (e: React.FormEvent) => {
16
- e.preventDefault();
17
- setError(null);
18
- setLoading(true);
19
-
20
- try {
21
- if (!username || !password) {
22
- setError(t('auth.emptyFields'));
23
- setLoading(false);
24
- return;
25
- }
26
-
27
- const success = await login(username, password);
28
-
29
- if (success) {
30
- navigate('/');
31
- } else {
32
- setError(t('auth.loginFailed'));
33
- }
34
- } catch (err) {
35
- setError(t('auth.loginError'));
36
- } finally {
37
- setLoading(false);
38
- }
39
- };
40
-
41
- return (
42
- <div className="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8">
43
- <div className="max-w-md w-full space-y-8">
44
- <div>
45
- <h2 className="mt-6 text-center text-3xl font-extrabold text-gray-900">
46
- {t('auth.loginTitle')}
47
- </h2>
48
- </div>
49
- <form className="mt-8 space-y-6" onSubmit={handleSubmit}>
50
- <div className="rounded-md shadow-sm -space-y-px">
51
- <div>
52
- <label htmlFor="username" className="sr-only">
53
- {t('auth.username')}
54
- </label>
55
- <input
56
- id="username"
57
- name="username"
58
- type="text"
59
- autoComplete="username"
60
- required
61
- className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
62
- placeholder={t('auth.username')}
63
- value={username}
64
- onChange={(e) => setUsername(e.target.value)}
65
- />
66
- </div>
67
- <div>
68
- <label htmlFor="password" className="sr-only">
69
- {t('auth.password')}
70
- </label>
71
- <input
72
- id="password"
73
- name="password"
74
- type="password"
75
- autoComplete="current-password"
76
- required
77
- className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
78
- placeholder={t('auth.password')}
79
- value={password}
80
- onChange={(e) => setPassword(e.target.value)}
81
- />
82
- </div>
83
- </div>
84
-
85
- {error && (
86
- <div className="text-red-500 text-sm text-center">{error}</div>
87
- )}
88
-
89
- <div>
90
- <button
91
- type="submit"
92
- disabled={loading}
93
- className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
94
- >
95
- {loading ? t('auth.loggingIn') : t('auth.login')}
96
- </button>
97
- </div>
98
- </form>
99
- </div>
100
- </div>
101
- );
102
- };
103
-
104
- export default LoginPage;