Skip to content

Commit 194c285

Browse files
author
Lasim
committed
feat: add installation details and environment variables components
- Implemented EnvironmentVariables.vue to display user environment variables. - Created InstallationInfo.vue for detailed installation information. - Added DangerZone.vue for managing sensitive actions related to installations. - Integrated new components into the installation view with tab navigation using DsTabs and DsTabsItem. - Enhanced the UI with responsive design and accessibility features. - Updated README.md for DsTabs component with usage examples and API reference.
1 parent 7d133d6 commit 194c285

File tree

27 files changed

+2418
-471
lines changed

27 files changed

+2418
-471
lines changed

services/backend/src/db/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,12 @@ export function registerPluginTables(plugins: Plugin[], logger?: FastifyBaseLogg
543543
}
544544

545545
export async function createPluginTables(plugins: Plugin[], logger: FastifyBaseLogger) {
546+
// Plugin tables are now handled by migrations
547+
logger.info({
548+
operation: 'create_plugin_tables'
549+
}, 'Plugin tables are handled by migrations.');
550+
return;
551+
546552
if (!dbInstance || !isDbInitialized) {
547553
logger.warn({
548554
operation: 'create_plugin_tables'
@@ -570,7 +576,7 @@ export async function createPluginTables(plugins: Plugin[], logger: FastifyBaseL
570576
const ext = plugin.databaseExtension as DatabaseExtensionWithTables;
571577
if (!ext.tableDefinitions) continue;
572578

573-
for (const [tableName, columnDefs] of Object.entries(ext.tableDefinitions)) {
579+
for (const [tableName, columnDefs] of Object.entries(ext.tableDefinitions || {})) {
574580
const fullTableName = `${plugin.meta.id}_${tableName}`;
575581

576582
try {
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { type FastifyInstance } from 'fastify';
2+
import { z } from 'zod';
3+
import { createSchema } from 'zod-openapi';
4+
import { requireTeamPermission } from '../../../middleware/roleMiddleware';
5+
import { McpInstallationService } from '../../../services/mcpInstallationService';
6+
import { getDb } from '../../../db';
7+
8+
// Response schemas
9+
const installationSchema = z.object({
10+
id: z.string(),
11+
team_id: z.string(),
12+
server_id: z.string(),
13+
user_id: z.string(),
14+
installation_name: z.string(),
15+
installation_type: z.enum(['local', 'cloud']),
16+
user_environment_variables: z.record(z.string(), z.string()).optional(),
17+
created_at: z.string(),
18+
updated_at: z.string(),
19+
last_used_at: z.string().nullable(),
20+
server: z.object({
21+
id: z.string(),
22+
name: z.string(),
23+
description: z.string(),
24+
github_url: z.string().nullable(),
25+
homepage_url: z.string().nullable(),
26+
author_name: z.string().nullable(),
27+
language: z.string(),
28+
runtime: z.string(),
29+
status: z.enum(['active', 'deprecated', 'maintenance']),
30+
tags: z.array(z.string()).nullable(),
31+
environment_variables: z.array(z.any()).nullable(),
32+
category_id: z.string().nullable()
33+
}).optional()
34+
});
35+
36+
const successResponseSchema = z.object({
37+
success: z.boolean().default(true),
38+
data: installationSchema
39+
});
40+
41+
const errorResponseSchema = z.object({
42+
success: z.boolean().default(false),
43+
error: z.string()
44+
});
45+
46+
export default async function getInstallationRoute(fastify: FastifyInstance) {
47+
fastify.get<{
48+
Params: { teamId: string; installationId: string };
49+
}>('/teams/:teamId/mcp/installations/:installationId', {
50+
schema: {
51+
tags: ['MCP Installations'],
52+
summary: 'Get MCP installation by ID',
53+
description: 'Retrieves a specific MCP server installation by ID for the specified team.',
54+
security: [{ cookieAuth: [] }],
55+
params: createSchema(z.object({
56+
teamId: z.string().min(1, 'Team ID is required'),
57+
installationId: z.string().min(1, 'Installation ID is required')
58+
}), {
59+
}),
60+
response: {
61+
200: createSchema(successResponseSchema.describe('Installation details')),
62+
401: createSchema(errorResponseSchema.describe('Unauthorized - Authentication required')),
63+
403: createSchema(errorResponseSchema.describe('Forbidden - Insufficient permissions')),
64+
404: createSchema(errorResponseSchema.describe('Not Found - Installation not found'))
65+
}
66+
},
67+
preValidation: requireTeamPermission('mcp.installations.view')
68+
}, async (request, reply) => {
69+
const { teamId, installationId } = request.params;
70+
const userId = request.user!.id;
71+
72+
request.log.info({
73+
operation: 'get_mcp_installation',
74+
teamId,
75+
installationId,
76+
userId
77+
}, 'Getting MCP installation by ID');
78+
79+
try {
80+
const db = getDb();
81+
const installationService = new McpInstallationService(db, request.log);
82+
83+
const installation = await installationService.getInstallationById(installationId, teamId);
84+
85+
if (!installation) {
86+
request.log.warn({
87+
operation: 'get_mcp_installation',
88+
teamId,
89+
installationId,
90+
userId
91+
}, 'MCP installation not found');
92+
93+
return reply.status(404).send({
94+
success: false,
95+
error: 'Installation not found'
96+
});
97+
}
98+
99+
request.log.info({
100+
operation: 'get_mcp_installation',
101+
teamId,
102+
installationId,
103+
userId
104+
}, 'Retrieved MCP installation');
105+
106+
return reply.status(200).send({
107+
success: true,
108+
data: {
109+
...installation,
110+
created_at: installation.created_at.toISOString(),
111+
updated_at: installation.updated_at.toISOString(),
112+
last_used_at: installation.last_used_at?.toISOString() || null
113+
}
114+
});
115+
116+
} catch (error) {
117+
request.log.error({
118+
operation: 'get_mcp_installation',
119+
error,
120+
teamId,
121+
installationId,
122+
userId
123+
}, 'Failed to get MCP installation');
124+
125+
const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred';
126+
127+
return reply.status(500).send({
128+
success: false,
129+
error: errorMessage
130+
});
131+
}
132+
});
133+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import { type FastifyInstance } from 'fastify';
22
import createInstallationRoute from './create';
33
import listInstallationsRoute from './list';
4+
import getInstallationRoute from './get';
45
import getClientConfigRoute from './config';
56
import deleteInstallationRoute from './delete';
67

78
export default async function installationsRoutes(fastify: FastifyInstance) {
89
// Register all installation routes
910
await fastify.register(createInstallationRoute);
1011
await fastify.register(listInstallationsRoute);
12+
await fastify.register(getInstallationRoute);
1113
await fastify.register(getClientConfigRoute);
1214
await fastify.register(deleteInstallationRoute);
1315
}

services/backend/src/routes/mcp/installations/list.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ const installationSchema = z.object({
2121
id: z.string(),
2222
name: z.string(),
2323
description: z.string(),
24+
github_url: z.string().nullable(),
25+
runtime: z.string(),
2426
installation_methods: z.array(z.any()),
2527
environment_variables: z.array(z.any()),
2628
default_config: z.any().nullable()

services/backend/src/services/mcpInstallationService.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export interface McpInstallation {
2323
id: string;
2424
name: string;
2525
description: string;
26+
github_url: string | null;
27+
runtime: string;
2628
installation_methods: any[];
2729
environment_variables: any[];
2830
default_config: any;
@@ -85,9 +87,15 @@ export class McpInstallationService {
8587
id: row.server.id,
8688
name: row.server.name,
8789
description: row.server.description,
88-
installation_methods: this.parseJsonField(row.server.installation_methods, []),
90+
github_url: row.server.github_url,
91+
homepage_url: row.server.homepage_url,
92+
author_name: row.server.author_name,
93+
language: row.server.language,
94+
runtime: row.server.runtime,
95+
status: row.server.status,
96+
tags: this.parseJsonField(row.server.tags, []),
8997
environment_variables: this.parseJsonField(row.server.environment_variables, []),
90-
default_config: this.parseJsonField(row.server.default_config, null)
98+
category_id: row.server.category_id
9199
} : undefined
92100
}));
93101
}
@@ -134,9 +142,15 @@ export class McpInstallationService {
134142
id: server.id,
135143
name: server.name,
136144
description: server.description,
137-
installation_methods: this.parseJsonField(server.installation_methods, []),
145+
github_url: server.github_url,
146+
homepage_url: server.homepage_url,
147+
author_name: server.author_name,
148+
language: server.language,
149+
runtime: server.runtime,
150+
status: server.status,
151+
tags: this.parseJsonField(server.tags, []),
138152
environment_variables: this.parseJsonField(server.environment_variables, []),
139-
default_config: this.parseJsonField(server.default_config, null)
153+
category_id: server.category_id
140154
} : undefined
141155
};
142156
}

0 commit comments

Comments
 (0)