mirror of
https://github.com/labring/sealos.git
synced 2026-09-24 15:46:19 +08:00
feat:add openapi to mcp sdk. (#5626)
* feat:add openapi to mcp sdk. # Conflicts: # frontend/pnpm-lock.yaml * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * feat:add openapi to mcp sdk. * fix devbox.json * fix dockerfile * fix dockerfile * fix bug. * fix bug. * fix bug. * fix bug. * fix bug. * fix openapi.json * fix bug. * fix bug. * fix openapi bug. * demo * fix openapi bug. * fix openapi bug. * fix openapi bug. * fix ci * update * fix build-packages * update path * update * fix bug. * fix bug. * fix ci bug. * fix ci bug. * fix ci bug. * fix ci bug. * fix ci bug. --------- Co-authored-by: zjy <3161362058@qq.com>
This commit is contained in:
@@ -94,6 +94,7 @@ RUN if ["$name" = "objectstorage" ]; then \
|
||||
COPY --from=builder /app/$path/public ./$path/public
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/$path/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/$path/.next/static ./$path/.next/static
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT=3000
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"gen:theme-typings": "pnpm chakra-cli tokens packages/ui/src/theme/theme.ts --out node_modules/.pnpm/node_modules/@chakra-ui/styled-system/dist/theming.types.d.ts",
|
||||
"postinstall": "pnpm run gen:theme-typings && pnpm run build-packages",
|
||||
"prepare": "cd .. && husky frontend/.husky",
|
||||
"build-packages": "pnpm -r --filter ./packages/client-sdk run build"
|
||||
"build-packages": "pnpm -r --filter './packages/*' run build"
|
||||
},
|
||||
"workspaces": [
|
||||
"./packages/*",
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "next dev",
|
||||
"build": "echo \"build driver\""
|
||||
"build": "exit 0"
|
||||
},
|
||||
"keywords": [
|
||||
"driver",
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dist
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "sealos-mcp-sdk",
|
||||
"version": "0.1.1",
|
||||
"description": "sealos mcp sdk for OpenAPI integration with MCP",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/index.esm.js",
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"*": [
|
||||
"./dist/index.d.ts"
|
||||
]
|
||||
}
|
||||
},
|
||||
"keywords": [
|
||||
"sealos",
|
||||
"mcp",
|
||||
"sdk",
|
||||
"openapi",
|
||||
"vercel"
|
||||
],
|
||||
"author": "lizhenq2009@gmail.com",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-typescript": "^11.1.4",
|
||||
"@types/node": "^20.7.1",
|
||||
"rollup": "2.79.1",
|
||||
"rollup-plugin-copy": "^3.5.0",
|
||||
"rollup-plugin-dts": "^4.2.3",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@vercel/mcp-adapter": "^0.9.1",
|
||||
"axios": "^1.5.1",
|
||||
"https": "^1.0.0",
|
||||
"openapi-types": "^12.1.3",
|
||||
"next": "^13.5.3",
|
||||
"zod": "^3.22.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@kubernetes/client-node": "^0.18.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import typescript from '@rollup/plugin-typescript';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
import copy from 'rollup-plugin-copy';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
|
||||
const external = [
|
||||
'axios',
|
||||
'https',
|
||||
'zod',
|
||||
'path',
|
||||
'fs',
|
||||
'@vercel/mcp-adapter',
|
||||
'async_hooks',
|
||||
'openapi-types'
|
||||
];
|
||||
|
||||
const createConfig = (input, output, plugins = []) => ({
|
||||
input,
|
||||
output,
|
||||
external,
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
commonjs(),
|
||||
typescript({
|
||||
tsconfig: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
declaration: false,
|
||||
declarationMap: false,
|
||||
noImplicitAny: false
|
||||
},
|
||||
noEmitOnError: false
|
||||
}),
|
||||
...plugins
|
||||
]
|
||||
});
|
||||
|
||||
const createDtsConfig = (input, output, plugins = []) => ({
|
||||
input,
|
||||
output,
|
||||
external,
|
||||
plugins: [dts(), ...plugins]
|
||||
});
|
||||
|
||||
export default [
|
||||
createConfig('src/index.ts', [
|
||||
{
|
||||
file: 'dist/index.js',
|
||||
format: 'cjs',
|
||||
sourcemap: true
|
||||
},
|
||||
{
|
||||
file: 'dist/index.esm.js',
|
||||
format: 'es',
|
||||
sourcemap: true
|
||||
}
|
||||
]),
|
||||
|
||||
createDtsConfig('src/index.ts', {
|
||||
file: 'dist/index.d.ts',
|
||||
format: 'es'
|
||||
}, [
|
||||
copy({
|
||||
targets: [
|
||||
{ src: 'package.json', dest: 'dist' },
|
||||
{ src: 'README.md', dest: 'dist', failOnError: false }
|
||||
]
|
||||
})
|
||||
])
|
||||
];
|
||||
@@ -0,0 +1,111 @@
|
||||
import axios from 'axios';
|
||||
import https from 'https';
|
||||
import { z } from 'zod';
|
||||
|
||||
export async function executeHttpRequest(
|
||||
toolId: string,
|
||||
toolName: string,
|
||||
params: any,
|
||||
apiBaseUrl: string,
|
||||
headers: Record<string, string> = {}
|
||||
): Promise<{ content: Array<{ type: string; text: string }> }> {
|
||||
console.error(`Executing tool: ${toolId} (${toolName})`);
|
||||
|
||||
// Only keep authentication-related headers
|
||||
const cleanedHeaders: Record<string, string> = {};
|
||||
|
||||
// Preserve authorization-related headers
|
||||
if (headers.authorization) {
|
||||
cleanedHeaders.Authorization = headers.authorization;
|
||||
}
|
||||
if (headers.Authorization) {
|
||||
cleanedHeaders.Authorization = headers.Authorization;
|
||||
}
|
||||
// Output cleaned headers
|
||||
console.log(`Transformed headers: ${JSON.stringify(cleanedHeaders, null, 2)}`);
|
||||
try {
|
||||
const [method, ...pathParts] = toolId.split('-');
|
||||
const path = '/' + pathParts.join('/').replace(/-/g, '/');
|
||||
const baseUrl = apiBaseUrl.endsWith('/') ? apiBaseUrl : `${apiBaseUrl}/`;
|
||||
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
|
||||
const url = new URL(cleanPath, baseUrl).toString();
|
||||
const config: any = {
|
||||
method: method.toLowerCase(),
|
||||
url: url,
|
||||
headers: cleanedHeaders
|
||||
};
|
||||
if (method.toLowerCase() === 'get') {
|
||||
if (params && typeof params === 'object') {
|
||||
const queryParams: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (Array.isArray(value)) {
|
||||
queryParams[key] = value.join(',');
|
||||
} else if (value !== undefined && value !== null) {
|
||||
queryParams[key] = String(value);
|
||||
}
|
||||
}
|
||||
config.params = queryParams;
|
||||
}
|
||||
} else {
|
||||
config.data = params;
|
||||
}
|
||||
console.error(`Processed parameters:`, config.params || config.data);
|
||||
console.error('Final request config:', config);
|
||||
const response = await axios(config);
|
||||
console.error('Response status:', response.status);
|
||||
console.error('Response headers:', response.headers);
|
||||
console.error('Response data:', response.data);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(response.data, null, 2)
|
||||
}
|
||||
]
|
||||
};
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
console.error('Request failed:', {
|
||||
status: error.response?.status,
|
||||
statusText: error.response?.statusText,
|
||||
data: error.response?.data,
|
||||
headers: error.response?.headers
|
||||
});
|
||||
throw new Error(
|
||||
`API request failed: ${error.message} - ${JSON.stringify(error.response?.data)}`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export function convertToZodProperties(schema: {
|
||||
type: string;
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
}): z.ZodRawShape {
|
||||
const zodProperties: z.ZodRawShape = {};
|
||||
for (const [key, prop] of Object.entries(schema.properties)) {
|
||||
switch (prop.type) {
|
||||
case 'string':
|
||||
zodProperties[key] = z.string();
|
||||
break;
|
||||
case 'number':
|
||||
zodProperties[key] = z.number();
|
||||
break;
|
||||
case 'boolean':
|
||||
zodProperties[key] = z.boolean();
|
||||
break;
|
||||
case 'integer':
|
||||
zodProperties[key] = z.number().int();
|
||||
default:
|
||||
zodProperties[key] = z.any();
|
||||
}
|
||||
|
||||
if (!schema.required?.includes(key)) {
|
||||
zodProperties[key] = zodProperties[key].optional();
|
||||
}
|
||||
}
|
||||
|
||||
return zodProperties;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { McpHandler, createMcpApiHandler } from './sdk';
|
||||
@@ -0,0 +1,111 @@
|
||||
// openapi-tools.ts
|
||||
|
||||
import { OpenAPIV3 } from 'openapi-types';
|
||||
import path from 'path';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export interface Tool {
|
||||
name: string;
|
||||
description: string;
|
||||
inputSchema: {
|
||||
type: string;
|
||||
properties: Record<string, any>;
|
||||
required?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
function getOpenApiSpecPath(specPath: any) {
|
||||
if (typeof specPath !== 'string') {
|
||||
throw new Error('Invalid OpenAPI spec path');
|
||||
}
|
||||
const normalizedPath = path.normalize(specPath).replace(/^(\.\.(\/|\\|$))+/, '');
|
||||
const fullPath = path.resolve(process.cwd(), normalizedPath);
|
||||
const baseDir = process.cwd();
|
||||
if (!fullPath.startsWith(baseDir)) {
|
||||
throw new Error('Access to paths outside the base directory is not allowed');
|
||||
}
|
||||
if (!fs.existsSync(fullPath)) {
|
||||
throw new Error(`OpenAPI spec file not found: ${normalizedPath}`);
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
export class OpenAPIToolsParser {
|
||||
public static loadAndParseOpenAPISpec(config: {
|
||||
openApiSpec: string | OpenAPIV3.Document;
|
||||
}): Map<string, Tool> {
|
||||
const tools = new Map<string, Tool>();
|
||||
let spec: OpenAPIV3.Document;
|
||||
try {
|
||||
const resolvedPath = getOpenApiSpecPath(config.openApiSpec);
|
||||
const content = fs.readFileSync(resolvedPath, 'utf-8');
|
||||
spec = JSON.parse(content);
|
||||
} catch (error) {
|
||||
// @ts-ignore
|
||||
throw new Error(`Failed to parse OpenAPI spec: ${error.message}`);
|
||||
}
|
||||
// @ts-ignore
|
||||
for (const [path, pathItem] of Object.entries(spec.paths)) {
|
||||
if (!pathItem) continue;
|
||||
for (const [method, operation] of Object.entries(pathItem)) {
|
||||
if (method === 'parameters' || !operation) continue;
|
||||
|
||||
const op = operation as OpenAPIV3.OperationObject;
|
||||
const cleanPath = path.replace(/^\//, '');
|
||||
const toolId = `${method.toUpperCase()}-${cleanPath}`.replace(/[^a-zA-Z0-9-]/g, '-');
|
||||
|
||||
const tool: Tool = {
|
||||
// @ts-ignore
|
||||
name: op.summary,
|
||||
description: op.description || 'No description available',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
required: undefined
|
||||
}
|
||||
};
|
||||
if (op.parameters) {
|
||||
for (const param of op.parameters) {
|
||||
if ('name' in param && 'in' in param) {
|
||||
const paramSchema = param.schema as OpenAPIV3.SchemaObject;
|
||||
tool.inputSchema.properties[param.name] = {
|
||||
type: paramSchema?.type || 'string',
|
||||
description: param.description || `${param.name} parameter`
|
||||
};
|
||||
|
||||
if (param.required) {
|
||||
tool.inputSchema.required = tool.inputSchema.required || [];
|
||||
tool.inputSchema.required.push(param.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (op.requestBody) {
|
||||
const requestBody = op.requestBody as OpenAPIV3.RequestBodyObject;
|
||||
if (requestBody.content) {
|
||||
const jsonContent = requestBody.content['application/json'];
|
||||
if (jsonContent && jsonContent.schema) {
|
||||
const schema = jsonContent.schema as OpenAPIV3.SchemaObject;
|
||||
if (schema.properties) {
|
||||
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
||||
const propSchemaObj = propSchema as OpenAPIV3.SchemaObject;
|
||||
tool.inputSchema.properties[propName] = {
|
||||
type: propSchemaObj.type || 'string',
|
||||
description: propSchemaObj.description || `${propName} property`
|
||||
};
|
||||
}
|
||||
}
|
||||
if (schema.required) {
|
||||
tool.inputSchema.required = tool.inputSchema.required || [];
|
||||
tool.inputSchema.required = [...tool.inputSchema.required, ...schema.required];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tools.set(toolId, tool);
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
}
|
||||
export type { OpenAPIV3 };
|
||||
@@ -0,0 +1,111 @@
|
||||
import { createMcpHandler } from '@vercel/mcp-adapter';
|
||||
import { OpenAPIToolsParser } from './openapi';
|
||||
import { executeHttpRequest, convertToZodProperties } from './httprequest';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
|
||||
export function McpHandler(path: string, url: string) {
|
||||
const requestContextStorage = new AsyncLocalStorage();
|
||||
const tools = OpenAPIToolsParser.loadAndParseOpenAPISpec({
|
||||
openApiSpec: path
|
||||
});
|
||||
const baseHandler = createMcpHandler(
|
||||
(server) => {
|
||||
tools.forEach((tool, id) => {
|
||||
const zodProperties = convertToZodProperties(tool.inputSchema);
|
||||
server.tool(tool.name, tool.description, zodProperties, async (params: any) => {
|
||||
const context = requestContextStorage.getStore();
|
||||
// @ts-ignore
|
||||
const headers = context ? context.headers : {};
|
||||
const createUserResult = await executeHttpRequest(id, tool.name, params, url, headers);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: JSON.stringify(createUserResult)
|
||||
}
|
||||
]
|
||||
};
|
||||
});
|
||||
});
|
||||
},
|
||||
{
|
||||
capabilities: {
|
||||
tools: {
|
||||
echo: {
|
||||
description: 'Echo a message'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
redisUrl: process.env.REDIS_URL,
|
||||
basePath: '/api',
|
||||
verboseLogs: true,
|
||||
maxDuration: 60
|
||||
}
|
||||
);
|
||||
|
||||
return async function (request: Request): Promise<Response> {
|
||||
// Extract request headers
|
||||
const headersObj: Record<string, string> = {};
|
||||
request.headers.forEach((value, key) => {
|
||||
headersObj[key] = value;
|
||||
});
|
||||
|
||||
// Create request context
|
||||
const requestContext = {
|
||||
headers: headersObj
|
||||
};
|
||||
|
||||
// Run request handler with async local storage
|
||||
return await requestContextStorage.run(requestContext, async () => {
|
||||
try {
|
||||
return await baseHandler(request);
|
||||
} catch (error) {
|
||||
console.error('Error processing request:', error);
|
||||
return new Response(JSON.stringify({ error: 'Internal Server Error' }), {
|
||||
status: 500,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createMcpApiHandler(path: string, baseUrl: string) {
|
||||
return async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const protocol = req.headers['x-forwarded-proto'] || 'http';
|
||||
const host = req.headers.host;
|
||||
const url = new URL(req.url || '', `${protocol}://${host}`);
|
||||
const request = new Request(url.toString(), {
|
||||
method: req.method || 'GET',
|
||||
headers: new Headers(req.headers as Record<string, string>),
|
||||
body:
|
||||
req.method === 'POST' || req.method === 'PUT' || req.method === 'PATCH'
|
||||
? JSON.stringify(req.body)
|
||||
: undefined
|
||||
});
|
||||
const handler = McpHandler(path, baseUrl);
|
||||
const response = await handler(request);
|
||||
response.headers.forEach((value, key) => {
|
||||
res.setHeader(key, value);
|
||||
});
|
||||
res.status(response.status);
|
||||
if (response.body) {
|
||||
const responseData = await response.text();
|
||||
// nosemgrep: javascript.express.security.audit.xss.direct-response-write.direct-response-write
|
||||
return res.send(responseData);
|
||||
} else {
|
||||
return res.end();
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('MCP Handler Error:', error);
|
||||
return res.status(500).json({
|
||||
message: 'Internal server error',
|
||||
error: process.env.NODE_ENV === 'development' ? error?.message : undefined
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "../../tsconfig.deps.json",
|
||||
"compilerOptions": {
|
||||
"lib": ["es2017", "DOM"],
|
||||
"noImplicitAny": true,
|
||||
"declaration": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "dist/**/*.d.ts", "dist/**/*.ts"]
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"dev": "next dev",
|
||||
"build": "echo \"build @sealos/ui\""
|
||||
"build": "exit 0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
|
||||
Generated
+771
-155
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
import { McpHandler } from 'sealos-mcp-sdk';
|
||||
import path from 'path';
|
||||
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
const handler = McpHandler(
|
||||
path.join(process.cwd(), 'public', 'devbox.json'),
|
||||
'http://devbox-frontend.devbox-frontend.svc.cluster.local:3000'
|
||||
);
|
||||
|
||||
export { handler as GET, handler as POST, handler as DELETE };
|
||||
@@ -31,13 +31,13 @@
|
||||
"fast-json-patch": "^3.1.1",
|
||||
"framer-motion": "^10.16.4",
|
||||
"immer": "^10.1.1",
|
||||
"lucide-react": "^0.476.0",
|
||||
"ini": "^4.1.3",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"jszip": "^3.10.1",
|
||||
"lodash": "^4.17.21",
|
||||
"lucide-react": "^0.476.0",
|
||||
"nanoid": "^4.0.2",
|
||||
"next": "14.2.5",
|
||||
"next-intl": "^3.17.2",
|
||||
@@ -51,6 +51,7 @@
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"sass": "^1.68.0",
|
||||
"sealos-desktop-sdk": "workspace:*",
|
||||
"sealos-mcp-sdk": "workspace:^",
|
||||
"zod": "^3.23.8",
|
||||
"zod-openapi": "^4.2.4",
|
||||
"zustand": "^4.5.4"
|
||||
|
||||
@@ -0,0 +1,418 @@
|
||||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"title": "Devbox API",
|
||||
"version": "1.0.0",
|
||||
"description": ""
|
||||
},
|
||||
"paths": {
|
||||
"/api/v1/DevBox/create": {
|
||||
"post": {
|
||||
"summary": "createDevbox",
|
||||
"description": "使用模版,创建一个新的DevBox",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "DevBox名称"
|
||||
},
|
||||
"runtimeName": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Debian",
|
||||
"C++",
|
||||
"Rust",
|
||||
"Java",
|
||||
"Go",
|
||||
"Python",
|
||||
"Node.js",
|
||||
".Net",
|
||||
"C",
|
||||
"PHP"
|
||||
],
|
||||
"description": "DevBox 创建所使用的模版的名称,有以下可选项:Debian,C++,Rust,Java,Go,Python,Node.js,.Net,C,PHP"
|
||||
},
|
||||
"cpu": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"default": 2000,
|
||||
"description": "CPU核心数, 使用1000,2000的格式,代表1Core, 2Core"
|
||||
},
|
||||
"memory": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"default": 4096,
|
||||
"description": "内存数, 使用2048, 4096,代表2G, 4G"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"runtimeName"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Devbox created successfully",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Devbox name"
|
||||
},
|
||||
"sshPort": {
|
||||
"type": "number",
|
||||
"description": "SSH port"
|
||||
},
|
||||
"base64PrivateKey": {
|
||||
"type": "string",
|
||||
"description": "Base64 encoded private key"
|
||||
},
|
||||
"userName": {
|
||||
"type": "string",
|
||||
"description": "User name"
|
||||
},
|
||||
"workingDir": {
|
||||
"type": "string",
|
||||
"description": "Working directory"
|
||||
},
|
||||
"domain": {
|
||||
"type": "string",
|
||||
"description": "Domain"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"sshPort",
|
||||
"base64PrivateKey",
|
||||
"userName",
|
||||
"workingDir",
|
||||
"domain"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"data"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Invalid request body",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "number"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"description": "Template not found",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "number"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"409": {
|
||||
"description": "Devbox already exists",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "number"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal server error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "number"
|
||||
},
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"data": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"code",
|
||||
"message"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/DevBox/release": {
|
||||
"post": {
|
||||
"summary": "releaseDevbox",
|
||||
"description": "DevBox发版,release,为现有的DevBox发布一个新的版本",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"devboxName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "DevBox名称"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "DevBox新的Tag"
|
||||
},
|
||||
"releaseDes": {
|
||||
"type": "string",
|
||||
"default": "",
|
||||
"description": "新版本的介绍"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"devboxName",
|
||||
"tag"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/DevBox/releases": {
|
||||
"get": {
|
||||
"summary": "getDevboxreleases",
|
||||
"description": "获取单个Devbox的所有历史版本",
|
||||
"parameters": [
|
||||
{
|
||||
"in": "query",
|
||||
"name": "devboxName",
|
||||
"description": "Devbox名称",
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Devbox名称"
|
||||
},
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/v1/DevBox/lifecycle": {
|
||||
"post": {
|
||||
"summary": "manageDevbox",
|
||||
"description": "操作Devbox,Devbox生命周期管理,主要包含开机,关机,重启,冷关机,这些操作都通过这个工具完成",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"devboxName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Devbox名称"
|
||||
},
|
||||
"action": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"start",
|
||||
"stop",
|
||||
"restart",
|
||||
"shutdown"
|
||||
],
|
||||
"description": "操作方式:start为开机,stop为关机,restart为重启,shutdown为冷关机"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"devboxName",
|
||||
"action"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/getDevboxListEasyResponse": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Query"
|
||||
],
|
||||
"summary": "获取所有Devbox",
|
||||
"description": "获取所有Devbox,得到Devbox的列表,可以知道当前有哪些Devbox"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/DevBox/ports/create": {
|
||||
"post": {
|
||||
"summary": "createPort",
|
||||
"description": "创建Devbox端口,为现有的Devbox创建一个新的端口",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"devboxName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Devbox名称"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"maximum": 65535,
|
||||
"description": "新创建端口的端口号"
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"HTTP",
|
||||
"GRPC",
|
||||
"WS"
|
||||
],
|
||||
"default": "HTTP",
|
||||
"description": "这个端口的通信方式,有三种通信方式可选:HTTP,GRPC,WS"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"devboxName",
|
||||
"port"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/deployDevbox": {
|
||||
"post": {
|
||||
"summary": "deployDevbox",
|
||||
"description": "部署 Devbox",
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"devboxName": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Devbox名称"
|
||||
},
|
||||
"port": {
|
||||
"type": "number",
|
||||
"minimum": 1,
|
||||
"default": 65535,
|
||||
"description": "端口号"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Devbox版本号,即Devbox发布版本时所使用的版本号"
|
||||
},
|
||||
"cpu": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"default": 2000,
|
||||
"description": "CPU核心数,使用1000、2000,分别代表1核、2核"
|
||||
},
|
||||
"memory": {
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"default": 4096,
|
||||
"description": "内存大小,使用2048、4096,分别代表2G、4G"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"devboxName",
|
||||
"tag"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/v1/DevBox/list": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Query"
|
||||
],
|
||||
"summary": "getDevboxList",
|
||||
"description": "获取DevBox列表,获得每个DevBox的详细信息"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,21 @@
|
||||
{
|
||||
"extends": "../../tsconfig.web.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"target": "ES2020",
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"paths": {
|
||||
"@/*": ["./*"],
|
||||
"prisma/*": ["prisma/*"]
|
||||
"@/*": [
|
||||
"./*"
|
||||
],
|
||||
"prisma/*": [
|
||||
"prisma/*"
|
||||
],
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
@@ -14,6 +23,10 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"extends": "../../tsconfig.web.json"
|
||||
}
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user