mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-04 10:13:53 +08:00
Compare commits
24 Commits
v1.9.1-beta
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6d611de382 | |||
| e484dbd55b | |||
| 9192b3995f | |||
| 7b54e12950 | |||
| 8857fc4f33 | |||
| abb4963bac | |||
| 6bed4f7672 | |||
| ea32fb1cd6 | |||
| a4642ec6e6 | |||
| 6ee1996e6f | |||
| 9ba746ba7f | |||
| fdac169bda | |||
| 3026e24bab | |||
| e010f00a19 | |||
| 1a9b916bab | |||
| 3e4f4e1cff | |||
| 3217ad2bba | |||
| 1485b31226 | |||
| 8968e1f961 | |||
| 4b8fa43c36 | |||
| c21d783c52 | |||
| e928cd84d7 | |||
| 412cb75bf0 | |||
| a13b2a8afe |
@@ -156,6 +156,10 @@ curl -sSO https://download.apipark.com/install/quick-start.sh ; bash quick-sta
|
||||
|
||||
<br>
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
# 🚀 Use Cases
|
||||
## Simplify AI Integration Costs
|
||||
- Connect to 100+ major models from all mainstream AI vendors, with standardized API calls requiring no additional adaptation work.
|
||||
@@ -199,11 +203,20 @@ To achieve this goal, we plan to add new features to APIPark, including:
|
||||
# 📕 Documentation
|
||||
Visit [APIPark Documentation](https://docs.apipark.com/docs/deploy) for detailed installation guides, API references, and usage instructions.
|
||||
|
||||
|
||||
# 🧑🤝🧑Friendly Links
|
||||
<a href="https://xroute.ai/">
|
||||
<img width="1248" height="158" alt="新建 PPTX 演示文稿 (2)_03" src="https://github.com/user-attachments/assets/0ebd694c-410a-4e3f-a793-90f1140d15df" />
|
||||
|
||||
</a>
|
||||
<br>
|
||||
|
||||
<br>
|
||||
|
||||
# 🧾 License
|
||||
APIPark uses the Apache 2.0 License. For more details, please refer to the LICENSE file.
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
# 💌 Contact Us
|
||||
@@ -212,6 +225,46 @@ For enterprise-level features and professional technical support, contact our pr
|
||||
- Website: https://apipark.com
|
||||
- Email: contact@apipark.com
|
||||
|
||||
🙏 A big thanks to everyone who helped shape APIPark. We are thrilled to hear the community’s thoughts! Let’s make the world of APIs and AI stronger and more fun together. 🎉
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
🙏 A big thanks to everyone who helped shape APIPark. We are thrilled to hear the community’s thoughts! Let’s make the world of APIs and AI stronger and more fun together. 🎉
|
||||
# 🤝 Partner
|
||||
- [Cursor](https://www.cursor.com/): Cursor is an AI-powered code editor that integrates artificial intelligence directly into the coding workflow, offering features like intelligent next edit suggestions, deep codebase understanding for relevant answers, and natural language editing to streamline development tasks and boost developer productivity.
|
||||
|
||||
- [Dify](https://dify.ai/): Dify is a leading Agentic AI Development Platform that provides a comprehensive suite of tools for building and extending AI applications, offering everything needed for agentic workflows, RAG pipelines, integrations, and observability, while allowing users to amplify their applications with various global Large Language Models (LLMs) and versatile plugins.
|
||||
|
||||
- [Trae](https://www.trae.ai/): Trae is an AI-native Integrated Development Environment (IDE) product that aims to embody the concept of “The Real AI Engineer” through intelligent productivity, seamlessly integrating into the development process to enhance quality and efficiency, featuring a chat-based interaction interface and supporting code generation and assistance.
|
||||
|
||||
- [Windsurf](https://windsurf.com/): Windsurf is an AI code editor designed to provide a seamless and limitless flow for developers, introducing a new purpose-built IDE that leverages AI to enhance coding with features like "Cascade" for deep codebase understanding, "Windsurf Tab" for intelligent autocompletion, and "Memories" for remembering important aspects of the codebase.
|
||||
|
||||
- [Coze](https://www.coze.com/): Coze is a next-generation AI application and chatbot development platform by ByteDance, empowering users to easily create and deploy powerful AI chatbots across various platforms with a no-code bot builder, integrated workflow logic, access to proprietary data, and simplified creation through pre-built plugins, knowledge bases, and workflows.
|
||||
|
||||
- [Claude Code](https://www.anthropic.com/claude-code): Claude Code is a command-line AI tool by Anthropic that embeds the Claude Opus 4 model directly into the user’s terminal, providing deep codebase awareness, the ability to edit files and execute commands, and making coordinated changes across multiple files, all while integrating seamlessly with popular IDEs and leveraging existing test suites.
|
||||
|
||||
- [Flowith](https://flowith.io/): Flowith is an AI creation workspace designed to revolutionize productivity and deep work by transforming knowledge and streamlining tasks through a multi-thread interface powered by advanced AI agents, offering an intuitive canvas-based user experience unlike traditional chat-based AI tools, and including a 24/7 operational version for complex tasks.
|
||||
|
||||
- [OpenManus](https://github.com/FoundationAgents/OpenManus): OpenManus is an open-source framework dedicated to building general AI agents, aiming to provide a platform where users can create and deploy their own agents without an invite code, supporting multi-agent capabilities, and requiring configuration for Large Language Model (LLM) APIs while integrating with browser automation tools.
|
||||
|
||||
- [Fellou](https://fellou.ai/): Fellou is an innovative Agentic Browser designed to transcend traditional web browsing by actively performing actions on behalf of the user, automating the entire process of information gathering and insight delivery, and excelling in in-depth research with seamless integrations with popular tools like Notion and LinkedIn.
|
||||
|
||||
- [Genspark](https://www.genspark.ai/): Genspark is an ultimate all-in-one AI companion offering a comprehensive suite of tools like AI Slides, AI Sheets, and AI Chat, designed to enhance various aspects of productivity and content creation, with personalized tools and AI Pods for generating content from diverse sources.
|
||||
|
||||
- [TEN](https://github.com/TEN-framework/ten-framework): TEN (The Embodied Narrator) is an open-source framework for building real-time, multimodal conversational voice AI agents, including components like TEN Framework, TEN Turn Detection, TEN Agent, TMAN Designer, and TEN Portal, offering features like Real-time Avatar, seamless MCP integration, real-time hardware communication, and vision/screenshare detection.
|
||||
|
||||
- [ChatGPT](https://chatgpt.com/): ChatGPT is an AI chatbot developed by OpenAI, built upon large language models like GPT-3.5 and GPT-4, designed to generate human-like conversational dialogue, understand context, answer follow-up questions, and integrate with various platforms for enhanced productivity through advanced language understanding, generation, and multilingual capabilities.
|
||||
|
||||
- [LangChain](https://www.langchain.com/): LangChain is a robust platform engineered for the development of reliable agents and Large Language Model (LLM) applications, offering a comprehensive product suite that seamlessly integrates various tools across the entire application development lifecycle, including LangGraph, LangSmith, and the LangGraph Platform, with functionalities for code generation, automation, and AI Search.
|
||||
|
||||
- [LEMON AI](https://lemonai.cc/): Lemon AI is the first Full-stack, Open-source, Agentic AI framework, offering a fully local alternative to platforms like Manus & Genspark AI. It features an integrated Code Interpreter VM sandbox for safe execution.
|
||||
|
||||
- [LobeChat](https://lobehub.com/): LobeHub offers LobeChat, a personal LLM productivity tool designed to elevate the user experience beyond traditional chatbots by empowering individuals to build personal AI agents and professional teams, supporting a wide array of LLMs, offering a simple chat interface, visual recognition, voice interaction, a rich plugin ecosystem, and knowledge base functionalities.
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/): Visual Studio Code (VS Code) is a widely popular, free, and open-source code editor by Microsoft, renowned for its extensibility and customization, supporting vast programming languages, and integrating AI capabilities like intelligent next edit suggestions and an advanced “agent mode” for complex tasks, with broad compatibility with various AI models.
|
||||
|
||||
- [XRoute](https://xroute.ai): The Unified Interface For LLMs, provides better prices, better throughput, and no subscription.
|
||||
|
||||
- [XPack MCP Marketplace](https://github.com/xpack-ai/XPack-MCP-Marketplace): The world's first open-source MCP monetization platform, transform any OpenAPI into a monetizable MCP server and build your own API marketplace in just 10 minutes. Everything is open-source and ready for commercial use.
|
||||
|
||||
- [MemU](https://github.com/NevaMind-AI/memU): MemU is an open-source memory framework for AI companions
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="356px" height="48px" viewBox="0 0 356 48" enable-background="new 0 0 356 48" xml:space="preserve"> <image id="image0" width="356" height="48" x="0" y="0"
|
||||
xlink:href="data:image/png;base64,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
|
||||
SUVORK5CYII=" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,8 @@
|
||||
<svg width="21" height="22" viewBox="0 0 21 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Microsoft">
|
||||
<rect id="Rectangle 1010" y="0.5" width="10" height="10" fill="#EF4F21"/>
|
||||
<rect id="Rectangle 1012" y="11.5" width="10" height="10" fill="#03A4EE"/>
|
||||
<rect id="Rectangle 1011" x="11" y="0.5" width="10" height="10" fill="#7EB903"/>
|
||||
<rect id="Rectangle 1013" x="11" y="11.5" width="10" height="10" fill="#FBB604"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 439 B |
@@ -0,0 +1,40 @@
|
||||
provider: azure_openai
|
||||
label:
|
||||
en_US: Azure OpenAI
|
||||
icon_small:
|
||||
en_US: icon_s_en.svg
|
||||
icon_large:
|
||||
en_US: icon_l_en.svg
|
||||
background: "#E3F0FF"
|
||||
help:
|
||||
title:
|
||||
en_US: Get your API key from Azure
|
||||
zh_Hans: 从 Azure 获取 API Key
|
||||
url:
|
||||
en_US: https://azure.microsoft.com/en-us/products/ai-services/openai-service
|
||||
supported_model_types:
|
||||
- llm
|
||||
configurate_methods:
|
||||
- customizable-model
|
||||
provider_credential_schema:
|
||||
credential_form_schemas:
|
||||
- variable: api_key
|
||||
label:
|
||||
en_US: API Key
|
||||
type: secret-input
|
||||
required: true
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 API Key
|
||||
en_US: Enter your API Key
|
||||
- variable: base_url
|
||||
type: text-input
|
||||
required: true
|
||||
placeholder:
|
||||
zh_Hans: 在此输入您的 Base URL
|
||||
en_US: Enter your Base URL
|
||||
- variable: api_version
|
||||
label:
|
||||
en_US: '2024-02-01'
|
||||
type: text-input
|
||||
required: true
|
||||
address: https://docs-test-001.openai.azure.com/
|
||||
@@ -113,9 +113,13 @@ func (i *imlAPIController) Edit(ctx *gin.Context, serviceId string, apiId string
|
||||
if input.AiModel.Type != "local" {
|
||||
provider = input.AiModel.Provider
|
||||
}
|
||||
modelName := input.AiModel.Name
|
||||
if modelName == "" {
|
||||
modelName = input.AiModel.Id
|
||||
}
|
||||
proxy.Plugins["ai_formatter"] = api.PluginSetting{
|
||||
Config: plugin_model.ConfigType{
|
||||
"model": input.AiModel.Name,
|
||||
"model": modelName,
|
||||
"provider": provider,
|
||||
"config": input.AiModel.Config,
|
||||
},
|
||||
|
||||
+71
-31
@@ -21,13 +21,14 @@ import (
|
||||
var _ IMcpController = (*imlMcpController)(nil)
|
||||
|
||||
type imlMcpController struct {
|
||||
settingModule system.ISettingModule `autowired:""`
|
||||
authorizationModule application_authorization.IAuthorizationModule `autowired:""`
|
||||
appModule service.IAppModule `autowired:""`
|
||||
mcpModule mcp.IMcpModule `autowired:""`
|
||||
sessionKeys sync.Map
|
||||
server map[string]http.Handler
|
||||
openServer http.Handler
|
||||
settingModule system.ISettingModule `autowired:""`
|
||||
authorizationModule application_authorization.IAuthorizationModule `autowired:""`
|
||||
appModule service.IAppModule `autowired:""`
|
||||
mcpModule mcp.IMcpModule `autowired:""`
|
||||
sessionKeys sync.Map
|
||||
sseServers map[string]http.Handler
|
||||
openSseServer http.Handler
|
||||
openStreamableServer http.Handler
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppMCPHandle(ctx *gin.Context) {
|
||||
@@ -42,12 +43,12 @@ func (i *imlMcpController) AppMCPHandle(ctx *gin.Context) {
|
||||
paths := strings.Split(req.URL.Path, "/")
|
||||
req.URL.Path = fmt.Sprintf("/api/v1/%s/%s", mcp_server.GlobalBasePath, paths[len(paths)-1])
|
||||
locale := utils.I18n(ctx)
|
||||
if v, ok := i.server[locale]; ok {
|
||||
if v, ok := i.sseServers[locale]; ok {
|
||||
v.ServeHTTP(ctx.Writer, req)
|
||||
return
|
||||
}
|
||||
|
||||
i.server[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
i.sseServers[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppHandleSSE(ctx *gin.Context) {
|
||||
@@ -68,7 +69,7 @@ func (i *imlMcpController) AppHandleSSE(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
ctx.Request.URL.Path = fmt.Sprintf("/openapi/v1/%s/sse", mcp_server.GlobalBasePath)
|
||||
i.handleSSE(ctx, i.openServer, SessionInfo{
|
||||
i.handleSSE(ctx, i.openSseServer, SessionInfo{
|
||||
Apikey: apikey,
|
||||
App: appId,
|
||||
})
|
||||
@@ -81,8 +82,29 @@ func (i *imlMcpController) AppHandleMessage(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
ctx.Request.URL.Path = fmt.Sprintf("/openapi/v1/%s/message", mcp_server.GlobalBasePath)
|
||||
ctx.Request = ctx.Request.WithContext(utils.SetLabel(ctx.Request.Context(), "app", appId))
|
||||
i.handleMessage(ctx, i.openServer)
|
||||
//ctx.Request = ctx.Request.WithContext(utils.SetLabel(ctx.Request.Context(), "app", appId))
|
||||
i.handleMessage(ctx, i.openSseServer)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
if apikey == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid apikey", "success": "fail"})
|
||||
return
|
||||
}
|
||||
appId := ctx.Request.Header.Get("X-Application-Id")
|
||||
if appId == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid app id", "success": "fail"})
|
||||
return
|
||||
}
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "apikey", apikey))
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "app", appId))
|
||||
req.URL.Path = mcp_server.OpenGlobalMCPPath
|
||||
i.openStreamableServer.ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) AppMCPConfig(ctx *gin.Context, appId string) (string, error) {
|
||||
@@ -94,36 +116,44 @@ func (i *imlMcpController) AppMCPConfig(ctx *gin.Context, appId string) (string,
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get app info error: %v", err)
|
||||
}
|
||||
return fmt.Sprintf(mcpDefaultConfig, appInfo.Name, fmt.Sprintf("%s/openapi/v1/mcp/app/%s/sse?apikey={your_api_key}", strings.TrimSuffix(cfg.SitePrefix, "/"), appId)), nil
|
||||
}
|
||||
|
||||
var mcpDefaultConfig = `{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"url": "%s"
|
||||
}
|
||||
}
|
||||
return mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s%s", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.OpenAppMCPPath),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
"X-Application-Id": appId,
|
||||
},
|
||||
nil,
|
||||
).ToString(appInfo.Name), nil
|
||||
}
|
||||
`
|
||||
|
||||
func (i *imlMcpController) GlobalMCPConfig(ctx *gin.Context) (string, error) {
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
if cfg.SitePrefix == "" {
|
||||
return "", fmt.Errorf("site prefix is empty")
|
||||
}
|
||||
return fmt.Sprintf(mcpDefaultConfig, "APIPark-MCP-Server", fmt.Sprintf("%s/openapi/v1/%s/sse?apikey={your_api_key}", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.GlobalBasePath)), nil
|
||||
return mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s%s", strings.TrimSuffix(cfg.SitePrefix, "/"), mcp_server.OpenGlobalMCPPath),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
},
|
||||
nil,
|
||||
).ToString("APIPark-MCP-Server"), nil
|
||||
}
|
||||
|
||||
func (i *imlMcpController) OnComplete() {
|
||||
i.server = make(map[string]http.Handler)
|
||||
i.sseServers = make(map[string]http.Handler)
|
||||
for language, tools := range mcpToolsByLanguage {
|
||||
s := server.NewMCPServer("APIPark MCP Server", "1.0.0", server.WithLogging())
|
||||
s.AddTool(tools[ToolServiceList], i.mcpModule.Services)
|
||||
s.AddTool(tools[ToolOpenAPIDocument], i.mcpModule.APIs)
|
||||
s.AddTool(tools[ToolInvokeAPI], i.mcpModule.Invoke)
|
||||
i.server[language] = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s", mcp_server.GlobalBasePath)))
|
||||
i.sseServers[language] = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s", mcp_server.GlobalBasePath)))
|
||||
if language == languageEnUs {
|
||||
i.openServer = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s", strings.Trim(mcp_server.GlobalBasePath, "/"))))
|
||||
i.openSseServer = server.NewSSEServer(s, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s", strings.Trim(mcp_server.GlobalBasePath, "/"))))
|
||||
i.openStreamableServer = server.NewStreamableHTTPServer(s, server.WithEndpointPath(mcp_server.OpenGlobalMCPPath))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,16 +162,16 @@ func (i *imlMcpController) GlobalMCPHandle(ctx *gin.Context) {
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
locale := utils.I18n(ctx)
|
||||
if v, ok := i.server[locale]; ok {
|
||||
if v, ok := i.sseServers[locale]; ok {
|
||||
v.ServeHTTP(ctx.Writer, req)
|
||||
return
|
||||
}
|
||||
i.server[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
i.sseServers[languageEnUs].ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleSSE(ctx *gin.Context) {
|
||||
apikey := ctx.Request.URL.Query().Get("apikey")
|
||||
i.handleSSE(ctx, i.openServer, SessionInfo{
|
||||
i.handleSSE(ctx, i.openSseServer, SessionInfo{
|
||||
Apikey: apikey,
|
||||
})
|
||||
}
|
||||
@@ -167,7 +197,16 @@ func (i *imlMcpController) handleSSE(ctx *gin.Context, server http.Handler, sIn
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleMessage(ctx *gin.Context) {
|
||||
i.handleMessage(ctx, i.openServer)
|
||||
i.handleMessage(ctx, i.openSseServer)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) GlobalHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
cfg := i.settingModule.Get(ctx)
|
||||
req := ctx.Request.WithContext(utils.SetGatewayInvoke(ctx.Request.Context(), cfg.InvokeAddress))
|
||||
req = req.WithContext(utils.SetLabel(req.Context(), "apikey", apikey))
|
||||
i.openStreamableServer.ServeHTTP(ctx.Writer, req)
|
||||
}
|
||||
|
||||
func (i *imlMcpController) MCPHandle(ctx *gin.Context) {
|
||||
@@ -204,12 +243,13 @@ func (i *imlMcpController) ServiceHandleMessage(ctx *gin.Context) {
|
||||
}
|
||||
|
||||
func (i *imlMcpController) ServiceHandleStreamHTTP(ctx *gin.Context) {
|
||||
apikey := ctx.Request.URL.Query().Get("apikey")
|
||||
serviceId := ctx.Param("serviceId")
|
||||
apikey := ctx.Request.Header.Get("Authorization")
|
||||
serviceId := ctx.Request.Header.Get("X-Service-Id")
|
||||
if serviceId == "" {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": "invalid service id", "success": "fail"})
|
||||
return
|
||||
}
|
||||
apikey = strings.TrimPrefix(apikey, "Bearer ")
|
||||
ok, err := i.authorizationModule.CheckAPIKeyAuthorizationByService(ctx, serviceId, apikey)
|
||||
if err != nil {
|
||||
ctx.AbortWithStatusJSON(403, gin.H{"code": -1, "msg": err.Error(), "success": "fail"})
|
||||
|
||||
@@ -13,11 +13,13 @@ type IMcpController interface {
|
||||
GlobalMCPHandle(ctx *gin.Context)
|
||||
GlobalHandleSSE(ctx *gin.Context)
|
||||
GlobalHandleMessage(ctx *gin.Context)
|
||||
GlobalHandleStreamHTTP(ctx *gin.Context)
|
||||
GlobalMCPConfig(ctx *gin.Context) (string, error)
|
||||
|
||||
AppMCPHandle(ctx *gin.Context)
|
||||
AppHandleSSE(ctx *gin.Context)
|
||||
AppHandleMessage(ctx *gin.Context)
|
||||
AppHandleStreamHTTP(ctx *gin.Context)
|
||||
AppMCPConfig(ctx *gin.Context, appId string) (string, error)
|
||||
|
||||
ServiceHandleSSE(ctx *gin.Context)
|
||||
|
||||
+16
-10
@@ -520,16 +520,21 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string,
|
||||
modelId := ""
|
||||
modelCfg := ""
|
||||
modelType := "online"
|
||||
if input.Model != nil {
|
||||
modelId = *input.Model
|
||||
}
|
||||
if *input.Provider == ai_provider_local.ProviderLocal {
|
||||
modelType = "local"
|
||||
list, err := i.aiLocalModel.SimpleList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if modelId == "" {
|
||||
list, err := i.aiLocalModel.SimpleList(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, fmt.Errorf("no local model")
|
||||
}
|
||||
modelId = list[0].Id
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, fmt.Errorf("no local model")
|
||||
}
|
||||
modelId = list[0].Id
|
||||
modelCfg = ai_provider_local.LocalConfig
|
||||
} else {
|
||||
pv, err := i.providerModule.Provider(ctx, *input.Provider)
|
||||
@@ -540,14 +545,15 @@ func (i *imlServiceController) createAIService(ctx *gin.Context, teamID string,
|
||||
if !has {
|
||||
return nil, fmt.Errorf("provider not found")
|
||||
}
|
||||
m, has := p.GetModel(pv.DefaultLLM)
|
||||
if modelId == "" {
|
||||
modelId = pv.DefaultLLM
|
||||
}
|
||||
m, has := p.GetModel(modelId)
|
||||
if !has {
|
||||
return nil, fmt.Errorf("model %s not found", pv.DefaultLLM)
|
||||
}
|
||||
//modelId = m.ID()
|
||||
modelId = m.Name()
|
||||
modelCfg = m.DefaultConfig()
|
||||
|
||||
}
|
||||
|
||||
var info *service_dto.Service
|
||||
|
||||
@@ -132,7 +132,7 @@ const EditableTableWithModal = <T extends { _id?: string }>({
|
||||
}
|
||||
])
|
||||
],
|
||||
[state.language, disabled, configFields]
|
||||
[state.language, disabled, configFields, configurations]
|
||||
)
|
||||
|
||||
const formItems = useMemo(() => {
|
||||
|
||||
@@ -19,7 +19,7 @@ export type MemberTableListItem = {
|
||||
enable:boolean
|
||||
departmentId:string
|
||||
roles:EntityItem[]
|
||||
form: string
|
||||
from: string
|
||||
};
|
||||
|
||||
export type AddToDepartmentProps = {
|
||||
@@ -41,7 +41,7 @@ export type MemberDropdownModalFieldType = {
|
||||
|
||||
export type MemberDropdownModalProps = {
|
||||
type:'addDep'|'addChild'|'addMember'|'editMember'|'rename'
|
||||
entity?:(MemberTableListItem & {departmentIds:string[]}) | ({id?:string, departmentIds?:string[],name?:string,form?:string})
|
||||
entity?:(MemberTableListItem & {departmentIds:string[]}) | ({id?:string, departmentIds?:string[],name?:string,from?:string})
|
||||
selectedMemberGroupId?:string
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,7 @@ export const MemberDropdownModal = forwardRef<MemberDropdownModalHandle,MemberDr
|
||||
const {fetchData} = useFetch()
|
||||
const [departmentList, setDepartmentList] = useState<DepartmentListItem[]>([])
|
||||
const { state } = useGlobalContext()
|
||||
const [disableEditMemberData] = useState<boolean>(entity?.form !== 'self-build')
|
||||
|
||||
const [disableEditMemberData] = useState<boolean>(entity?.from === 'feishu')
|
||||
const save:()=>Promise<boolean | string> = ()=>{
|
||||
let url:string
|
||||
let method:string
|
||||
|
||||
@@ -95,14 +95,15 @@ const AddToDepartment = forwardRef<AddToDepartmentHandle, AddToDepartmentProps>(
|
||||
treeData?.map((x: DataNode) => ({
|
||||
...x,
|
||||
name: $t((x as unknown as { name: string }).name),
|
||||
checkable: false,
|
||||
children: x.children?.map(y => ({ ...y, checkable: false }))
|
||||
checkable: false, // 根节点不可选中
|
||||
children: x.children?.map(y => ({ ...y, checkable: true })) // 子节点可以选中
|
||||
})),
|
||||
[state.language, treeData]
|
||||
)
|
||||
|
||||
const onCheck: TreeProps['onCheck'] = (checkedKeys: string[]) => {
|
||||
setSelectedKeys(checkedKeys.checked)
|
||||
const onCheck: TreeProps['onCheck'] = (checkedKeys, info) => {
|
||||
const selectedIds = Array.isArray(checkedKeys) ? checkedKeys : checkedKeys.checked || []
|
||||
setSelectedKeys(selectedIds)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -153,11 +154,12 @@ const MemberList = () => {
|
||||
const [tableHttpReload, setTableHttpReload] = useState(true)
|
||||
const [tableListDataSource, setTableListDataSource] = useState<MemberTableListItem[]>([])
|
||||
const pageListRef = useRef<ActionType>(null)
|
||||
const { topGroupId, selectedDepartmentIds, refreshGroup } = useOutletContext<{
|
||||
const { topGroupId, selectedDepartmentIds, refreshGroup, refreshTableCount } = useOutletContext<{
|
||||
topGroupId: string
|
||||
departmentList: DepartmentListItem[]
|
||||
selectedDepartmentIds: string[]
|
||||
refreshGroup: () => void
|
||||
refreshTableCount: number
|
||||
}>()
|
||||
const AddMemberRef = useRef<MemberDropdownModalHandle>(null)
|
||||
const EditMemberRef = useRef<MemberDropdownModalHandle>(null)
|
||||
@@ -396,7 +398,7 @@ const MemberList = () => {
|
||||
width: 600,
|
||||
okText: $t('确认'),
|
||||
okButtonProps: {
|
||||
disabled: isActionAllowed(type) || (type === 'editMember' && entity?.form !== 'self-build')
|
||||
disabled: isActionAllowed(type) || (type === 'editMember' && entity?.from === 'feishu')
|
||||
},
|
||||
cancelText: $t('取消'),
|
||||
closable: true,
|
||||
@@ -415,6 +417,13 @@ const MemberList = () => {
|
||||
getDepartmentList()
|
||||
}, [])
|
||||
|
||||
// 监听外部刷新触发器
|
||||
useEffect(() => {
|
||||
if (refreshTableCount > 0) {
|
||||
manualReloadTable()
|
||||
}
|
||||
}, [refreshTableCount])
|
||||
|
||||
const getDepartmentList = async () => {
|
||||
setDepartmentValueEnum([])
|
||||
const { code, data, msg } = await fetchData<BasicResponse<{ department: DepartmentListItem }>>(
|
||||
|
||||
@@ -36,6 +36,11 @@ const MemberPage = ()=>{
|
||||
const [selectedDepartmentId, setSelectedDepartmentId] = useState<string>('-1')
|
||||
const {accessData,state} = useGlobalContext()
|
||||
const [refreshMemberCount, setRefreshMemberCount] = useState<number>(0)
|
||||
const [refreshTableCount, setRefreshTableCount] = useState<number>(0)
|
||||
|
||||
const refreshMemberTable = () => {
|
||||
setRefreshTableCount(prev => prev + 1)
|
||||
}
|
||||
const onSearchWordChange = (e:string)=>{
|
||||
setSearchWord(e || '')
|
||||
}
|
||||
@@ -90,7 +95,7 @@ const MemberPage = ()=>{
|
||||
case 'addChild':
|
||||
return AddChildRef.current?.save().then((res)=>{if(res === true)getDepartmentList()})
|
||||
case 'addMember':
|
||||
return AddMemberRef.current?.save().then((res)=>{if(res === true){getDepartmentList();setRefreshMemberCount(pre=>pre+1)}})
|
||||
return AddMemberRef.current?.save().then((res)=>{if(res === true){getDepartmentList();setRefreshMemberCount(pre=>pre+1);refreshMemberTable()}})
|
||||
case 'rename':
|
||||
return RenameRef.current?.save().then((res)=>{if(res === true)getDepartmentList()})
|
||||
case 'delete':
|
||||
@@ -262,7 +267,7 @@ const MemberPage = ()=>{
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 p-btnbase pr-PAGE_INSIDE_X overflow-x-hidden">
|
||||
<Outlet context={{refreshMemberCount, selectedDepartmentIds,refreshGroup:()=>getDepartmentList()}}/>
|
||||
<Outlet context={{refreshMemberCount, selectedDepartmentIds,refreshGroup:()=>getDepartmentList(), refreshTableCount}}/>
|
||||
</div>
|
||||
</div>
|
||||
</InsidePage>);
|
||||
|
||||
@@ -74,15 +74,11 @@ const ServiceHubDetail = () => {
|
||||
|
||||
// 添加servers部分
|
||||
if (!apiDoc.includes('servers:')) {
|
||||
const serverConfig = `info:
|
||||
title: API Space API
|
||||
version: 1.0.0
|
||||
openapi: 3.0.1
|
||||
servers:
|
||||
const serverConfig = `servers:
|
||||
- url: ${apiPrefix}
|
||||
description: 默认服务器
|
||||
`
|
||||
result = serverConfig + result.substring(result.indexOf('paths:'))
|
||||
result = apiDoc.substring(0, pathsIndex) + serverConfig + apiDoc.substring(pathsIndex, pathsIndex + 6)
|
||||
}
|
||||
|
||||
// 处理路径
|
||||
@@ -93,11 +89,19 @@ servers:
|
||||
const trimmedLine = line.trim()
|
||||
|
||||
// 检测是否是路径行
|
||||
if (trimmedLine.match(/^\//)) {
|
||||
if (trimmedLine.match(/^['"]?\/.*['"]?:/)) {
|
||||
// 这是一个路径行
|
||||
const indentation = line.substring(0, line.indexOf('/'))
|
||||
const pathWithoutIndent = line.substring(line.indexOf('/'))
|
||||
lines[i] = indentation + apiPrefix + pathWithoutIndent
|
||||
const pathMatch = line.match(/^(\s*)['"]?(\/[^'"]*)['"]?:\s*$/)
|
||||
if (pathMatch) {
|
||||
const indentation = pathMatch[1]
|
||||
const actualPath = pathMatch[2]
|
||||
lines[i] = `${indentation}'${apiPrefix}${actualPath}':`
|
||||
} else {
|
||||
const indentation = line.substring(0, line.indexOf('/'))
|
||||
const pathWithoutIndent = line.substring(line.indexOf('/'))
|
||||
const cleanPath = pathWithoutIndent.replace(/:\s*$/, '').replace(/['"]/g, '')
|
||||
lines[i] = `${indentation}'${apiPrefix}${cleanPath}':`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package auth
|
||||
|
||||
func init() {
|
||||
b := NewAKSK()
|
||||
Register(b.Name(), b)
|
||||
}
|
||||
|
||||
func NewAKSK() *AKSK {
|
||||
return &AKSK{}
|
||||
}
|
||||
|
||||
type AKSK struct {
|
||||
}
|
||||
|
||||
func (a *AKSK) Name() string {
|
||||
return "aksk"
|
||||
}
|
||||
|
||||
func (a *AKSK) ToPattern(cfg map[string]interface{}) interface{} {
|
||||
result := make(map[string]interface{})
|
||||
result["ak"] = cfg["ak"]
|
||||
result["sk"] = cfg["sk"]
|
||||
return result
|
||||
}
|
||||
|
||||
func (a *AKSK) ToConfig(cfg map[string]interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package auth
|
||||
|
||||
func init() {
|
||||
b := NewJWT()
|
||||
Register(b.Name(), b)
|
||||
}
|
||||
|
||||
func NewJWT() *JWT {
|
||||
return &JWT{}
|
||||
}
|
||||
|
||||
type JWT struct {
|
||||
}
|
||||
|
||||
func (J *JWT) Name() string {
|
||||
return "jwt"
|
||||
}
|
||||
|
||||
func (J *JWT) ToPattern(cfg map[string]interface{}) interface{} {
|
||||
result := make(map[string]interface{})
|
||||
result["username"] = cfg["user"]
|
||||
return result
|
||||
}
|
||||
|
||||
func (J *JWT) ToConfig(cfg map[string]interface{}) interface{} {
|
||||
result := make(map[string]interface{})
|
||||
result["iss"] = cfg["iss"]
|
||||
result["algorithm"] = cfg["algorithm"]
|
||||
result["secret"] = cfg["secret"]
|
||||
result["rsa_public_key"] = cfg["publicKey"]
|
||||
result["path"] = cfg["userPath"]
|
||||
result["claims_to_verify"] = cfg["claimsToVerify"]
|
||||
result["signature_is_base_64"] = cfg["signatureIsBase64"]
|
||||
return result
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package auth
|
||||
|
||||
func init() {
|
||||
b := NewOAuth2()
|
||||
Register(b.Name(), b)
|
||||
}
|
||||
|
||||
func NewOAuth2() *OAuth2 {
|
||||
return &OAuth2{}
|
||||
}
|
||||
|
||||
type OAuth2 struct {
|
||||
}
|
||||
|
||||
func (o *OAuth2) Name() string {
|
||||
return "oauth2"
|
||||
}
|
||||
func (o *OAuth2) ToPattern(cfg map[string]interface{}) interface{} {
|
||||
result := make(map[string]interface{})
|
||||
result["client_id"] = cfg["client_id"]
|
||||
result["client_secret"] = cfg["client_secret"]
|
||||
result["client_type"] = cfg["client_type"]
|
||||
result["hash_secret"] = cfg["hash_secret"]
|
||||
result["redirect_urls"] = cfg["redirect_urls"]
|
||||
return result
|
||||
}
|
||||
func (o *OAuth2) ToConfig(cfg map[string]interface{}) interface{} {
|
||||
return nil
|
||||
}
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0
|
||||
github.com/mark3labs/mcp-go v0.33.0
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nsqio/go-nsq v1.1.0
|
||||
github.com/ollama/ollama v0.5.8
|
||||
@@ -27,6 +27,8 @@ require (
|
||||
|
||||
require (
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.11.6 // indirect
|
||||
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
@@ -46,6 +48,7 @@ require (
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jinzhu/now v1.1.5 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
@@ -67,6 +70,7 @@ require (
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.13 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
@@ -87,6 +91,4 @@ require (
|
||||
//)
|
||||
|
||||
//replace github.com/eolinker/ap-account => ../../eolinker/ap-account
|
||||
|
||||
//
|
||||
//replace github.com/eolinker/go-common => ../../eolinker/go-common
|
||||
|
||||
@@ -2,6 +2,8 @@ github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
@@ -9,6 +11,8 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||
@@ -80,6 +84,8 @@ github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjw
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI=
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU=
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||
@@ -101,8 +107,8 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mark3labs/mcp-go v0.33.0 h1:naxhjnTIs/tyPZmWUZFuG0lDmdA6sUyYGGf3gsHvTCc=
|
||||
github.com/mark3labs/mcp-go v0.33.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4=
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3 h1:nmOg1HFgSOgy0bZkAQ+E6qVpPMPmE8hIkM0BO94Ks9k=
|
||||
github.com/mark3labs/mcp-go v0.42.0-beta.3/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
@@ -159,6 +165,8 @@ github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65E
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
|
||||
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.13 h1:RVZSAnWWWiI5IrYAXjQorajncORbS0zI48LQlE2kQWg=
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package mcp_server
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type TransportType string
|
||||
|
||||
const (
|
||||
TransportTypeStreamableHTTP TransportType = "streamable-http"
|
||||
TransportTypeSSE TransportType = "sse"
|
||||
)
|
||||
|
||||
func NewMCPConfig(typ TransportType, url string, headers map[string]string, alwaysAllow []string) *MCPConfig {
|
||||
return &MCPConfig{
|
||||
Type: typ,
|
||||
URL: url,
|
||||
Headers: headers,
|
||||
AlwaysAllow: alwaysAllow,
|
||||
}
|
||||
}
|
||||
|
||||
type MCPConfig struct {
|
||||
Type TransportType `json:"type"`
|
||||
URL string `json:"url"`
|
||||
Headers map[string]string `json:"headers,omitempty"`
|
||||
AlwaysAllow []string `json:"alwaysAllow,omitempty"`
|
||||
}
|
||||
|
||||
func (c *MCPConfig) ToString(name string) string {
|
||||
m := map[string]interface{}{
|
||||
"mcpServers": map[string]interface{}{
|
||||
name: c,
|
||||
},
|
||||
}
|
||||
data, _ := json.MarshalIndent(m, "", "\t")
|
||||
return string(data)
|
||||
}
|
||||
+24
-6
@@ -17,6 +17,10 @@ var (
|
||||
ServiceBasePath = "mcp/service"
|
||||
GlobalBasePath = "mcp/global"
|
||||
AppBasePath = "mcp/app"
|
||||
|
||||
OpenGlobalMCPPath = "/openapi/v1/global/mcp"
|
||||
OpenAppMCPPath = "/openapi/v1/app/mcp"
|
||||
OpenServiceMCPPath = "/openapi/v1/service/mcp"
|
||||
)
|
||||
|
||||
func NewServer() *Server {
|
||||
@@ -61,7 +65,7 @@ func (s *Server) Set(id string, ser *server.MCPServer) {
|
||||
}
|
||||
tmp.handlers["api-sse"] = server.NewSSEServer(ser, server.WithStaticBasePath(fmt.Sprintf("/api/v1/%s/%s", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-sse"] = server.NewSSEServer(ser, server.WithStaticBasePath(fmt.Sprintf("/openapi/v1/%s/%s", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-stream"] = server.NewStreamableHTTPServer(ser, server.WithEndpointPath(fmt.Sprintf("/openapi/v1/%s/%s/mcp", ServiceBasePath, id)))
|
||||
tmp.handlers["openapi-stream"] = server.NewStreamableHTTPServer(ser, server.WithEndpointPath(OpenServiceMCPPath))
|
||||
s.servers[id] = tmp
|
||||
|
||||
}
|
||||
@@ -91,12 +95,23 @@ func (s *Server) Get(id string) (*Handler, bool) {
|
||||
}
|
||||
|
||||
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
sid, err := genPath(r.URL.Path)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
var sid string
|
||||
if r.URL.Path == OpenServiceMCPPath {
|
||||
sid = r.Header.Get("X-Service-Id")
|
||||
if sid == "" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
id, err := genPath(r.URL.Path)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(err.Error()))
|
||||
return
|
||||
}
|
||||
sid = id
|
||||
}
|
||||
|
||||
ser, has := s.Get(sid)
|
||||
if has {
|
||||
ser.ServeHTTP(w, r)
|
||||
@@ -156,6 +171,9 @@ func SetServerByOpenapi(sid, name, version, content string) error {
|
||||
toolOptions = append(toolOptions, mcp.WithDescription(a.Description))
|
||||
params := make(map[string]*Param)
|
||||
for _, v := range a.Params {
|
||||
if v.In == "header" && v.Name == "Authorization" {
|
||||
continue
|
||||
}
|
||||
params[v.Name] = NewParam(Position(v.In), v.Required, v.Description)
|
||||
options := make([]mcp.PropertyOption, 0, 2)
|
||||
if v.Required {
|
||||
|
||||
@@ -6,11 +6,11 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
||||
auth_driver "github.com/APIParkLab/APIPark/module/application-authorization/auth-driver"
|
||||
|
||||
|
||||
"github.com/eolinker/go-common/utils"
|
||||
|
||||
|
||||
application_authorization_dto "github.com/APIParkLab/APIPark/module/application-authorization/dto"
|
||||
)
|
||||
|
||||
@@ -26,12 +26,12 @@ type Config struct {
|
||||
Iss string `json:"iss"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Secret string `json:"secret"`
|
||||
PublicKey string `json:"public_key"`
|
||||
PublicKey string `json:"publicKey"`
|
||||
User string `json:"user"`
|
||||
UserPath string `json:"user_path"`
|
||||
ClaimsToVerify []string `json:"claims_to_verify"`
|
||||
UserPath string `json:"userPath"`
|
||||
ClaimsToVerify []string `json:"claimsToVerify"`
|
||||
Label map[string]string `json:"label"`
|
||||
SignatureIsBase64 bool `json:"signature_is_base64"`
|
||||
SignatureIsBase64 bool `json:"signatureIsBase64"`
|
||||
}
|
||||
|
||||
func (cfg *Config) ID() string {
|
||||
@@ -46,7 +46,7 @@ func (cfg *Config) ID() string {
|
||||
for _, claim := range cfg.ClaimsToVerify {
|
||||
builder.WriteString(strings.TrimSpace(claim))
|
||||
}
|
||||
|
||||
|
||||
case "RS256", "RS384", "RS512", "ES256", "ES384", "ES512":
|
||||
builder.WriteString(strings.TrimSpace(cfg.Iss))
|
||||
builder.WriteString(strings.TrimSpace(cfg.PublicKey))
|
||||
@@ -81,7 +81,7 @@ func (cfg *Config) Valid() ([]byte, error) {
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupport algorithm")
|
||||
}
|
||||
|
||||
|
||||
//校验 校验字段
|
||||
for _, claim := range cfg.ClaimsToVerify {
|
||||
switch claim {
|
||||
@@ -94,7 +94,7 @@ func (cfg *Config) Valid() ([]byte, error) {
|
||||
}
|
||||
|
||||
func (cfg *Config) Detail() []application_authorization_dto.DetailItem {
|
||||
|
||||
|
||||
items := []application_authorization_dto.DetailItem{
|
||||
{Key: "Iss", Value: cfg.Iss},
|
||||
{Key: "签名算法", Value: cfg.Algorithm},
|
||||
@@ -102,7 +102,7 @@ func (cfg *Config) Detail() []application_authorization_dto.DetailItem {
|
||||
{Key: "用户名JsonPath", Value: cfg.UserPath},
|
||||
{Key: "校验字段", Value: strings.Join(cfg.ClaimsToVerify, ",")},
|
||||
}
|
||||
|
||||
|
||||
switch cfg.Algorithm {
|
||||
case "HS256", "HS384", "HS512":
|
||||
items = append(items, application_authorization_dto.DetailItem{Key: "Secret", Value: cfg.Secret})
|
||||
@@ -110,10 +110,10 @@ func (cfg *Config) Detail() []application_authorization_dto.DetailItem {
|
||||
if cfg.SignatureIsBase64 {
|
||||
base64 = "true"
|
||||
}
|
||||
items = append(items, application_authorization_dto.DetailItem{Key: "Secret", Value: base64})
|
||||
items = append(items, application_authorization_dto.DetailItem{Key: "SignatureIsBase64", Value: base64})
|
||||
default:
|
||||
items = append(items, application_authorization_dto.DetailItem{Key: "RSA公钥", Value: cfg.PublicKey})
|
||||
}
|
||||
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
@@ -219,19 +219,19 @@ func (i *imlAuthorizationModule) initGateway(ctx context.Context, partitionId st
|
||||
return clientDriver.Application().Online(ctx, applications...)
|
||||
}
|
||||
|
||||
func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service) error {
|
||||
clusters, err := i.clusterService.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(clusters) < 1 {
|
||||
return nil
|
||||
}
|
||||
func (i *imlAuthorizationModule) getApplicationRelease(ctx context.Context, s *service.Service) (*gateway.ApplicationRelease, error) {
|
||||
authorizations, err := i.authorizationService.ListByApp(ctx, s.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
app := &gateway.ApplicationRelease{
|
||||
if len(authorizations) < 1 {
|
||||
return &gateway.ApplicationRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: s.Id,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
return &gateway.ApplicationRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: s.Id,
|
||||
Description: s.Description,
|
||||
@@ -256,10 +256,40 @@ func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service)
|
||||
},
|
||||
}
|
||||
}),
|
||||
}, nil
|
||||
}
|
||||
func (i *imlAuthorizationModule) doOffline(ctx context.Context, clusterId string, app *gateway.ApplicationRelease) error {
|
||||
client, err := i.clusterService.GatewayClient(ctx, clusterId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = client.Close(ctx)
|
||||
}()
|
||||
return client.Application().Offline(ctx, app)
|
||||
}
|
||||
|
||||
func (i *imlAuthorizationModule) online(ctx context.Context, s *service.Service) error {
|
||||
clusters, err := i.clusterService.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(clusters) < 1 {
|
||||
return nil
|
||||
}
|
||||
release, err := i.getApplicationRelease(ctx, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, c := range clusters {
|
||||
err := i.doOnline(ctx, c.Uuid, app)
|
||||
if len(release.Authorizations) < 1 {
|
||||
err = i.doOffline(ctx, c.Uuid, release)
|
||||
if err != nil {
|
||||
log.Warnf("service authorization offline for cluster[%s] %v", c.Name, err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
err = i.doOnline(ctx, c.Uuid, release)
|
||||
if err != nil {
|
||||
log.Warnf("service authorization online for cluster[%s] %v", c.Name, err)
|
||||
}
|
||||
@@ -375,7 +405,7 @@ func (i *imlAuthorizationModule) EditAuthorization(ctx context.Context, appId st
|
||||
}
|
||||
|
||||
func (i *imlAuthorizationModule) DeleteAuthorization(ctx context.Context, pid string, aid string) error {
|
||||
_, err := i.serviceService.Get(ctx, pid)
|
||||
s, err := i.serviceService.Get(ctx, pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -385,35 +415,11 @@ func (i *imlAuthorizationModule) DeleteAuthorization(ctx context.Context, pid st
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clusters, err := i.clusterService.List(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
app := &gateway.ApplicationRelease{
|
||||
BasicItem: &gateway.BasicItem{
|
||||
ID: pid,
|
||||
},
|
||||
}
|
||||
for _, c := range clusters {
|
||||
err := i.doOffline(ctx, c.Uuid, app)
|
||||
if err != nil {
|
||||
log.Warnf("service authorization offline for cluster[%s] %v", c.Name, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
return i.online(ctx, s)
|
||||
})
|
||||
}
|
||||
func (i *imlAuthorizationModule) doOffline(ctx context.Context, clusterId string, app *gateway.ApplicationRelease) error {
|
||||
client, err := i.clusterService.GatewayClient(ctx, clusterId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = client.Close(ctx)
|
||||
}()
|
||||
return client.Application().Offline(ctx, app)
|
||||
|
||||
}
|
||||
func (i *imlAuthorizationModule) Authorizations(ctx context.Context, pid string) ([]*application_authorization_dto.AuthorizationItem, error) {
|
||||
_, err := i.serviceService.Get(ctx, pid)
|
||||
if err != nil {
|
||||
|
||||
+11
-13
@@ -9,10 +9,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
service_overview "github.com/APIParkLab/APIPark/service/service-overview"
|
||||
|
||||
mcp_server "github.com/APIParkLab/APIPark/mcp-server"
|
||||
|
||||
service_overview "github.com/APIParkLab/APIPark/service/service-overview"
|
||||
|
||||
"github.com/APIParkLab/APIPark/module/monitor/driver"
|
||||
|
||||
"github.com/APIParkLab/APIPark/service/monitor"
|
||||
@@ -326,15 +326,6 @@ func (i *imlCatalogueModule) Subscribe(ctx context.Context, subscribeInfo *catal
|
||||
})
|
||||
}
|
||||
|
||||
var mcpDefaultConfig = `{
|
||||
"mcpServers": {
|
||||
"%s": {
|
||||
"url": "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*catalogue_dto.ServiceDetail, error) {
|
||||
// 获取服务的基本信息
|
||||
s, err := i.serviceService.Get(ctx, sid)
|
||||
@@ -400,8 +391,15 @@ func (i *imlCatalogueModule) ServiceDetail(ctx context.Context, sid string) (*ca
|
||||
mcpAccessConfig := ""
|
||||
if s.EnableMCP {
|
||||
if sitePrefix != "" {
|
||||
mcpAccessAddress = fmt.Sprintf("%s/openapi/v1/%s/%s/sse?apikey={your_api_key}", strings.TrimSuffix(sitePrefix, "/"), mcp_server.ServiceBasePath, s.Id)
|
||||
mcpAccessConfig = fmt.Sprintf(mcpDefaultConfig, fmt.Sprintf("APIPark/%s", s.Name), mcpAccessAddress)
|
||||
mcpAccessConfig = mcp_server.NewMCPConfig(
|
||||
mcp_server.TransportTypeStreamableHTTP,
|
||||
fmt.Sprintf("%s/openapi/v1/service/mcp", strings.TrimSuffix(sitePrefix, "/")),
|
||||
map[string]string{
|
||||
"Authorization": "Bearer {your_api_key}",
|
||||
"X-Service-Id": s.Id,
|
||||
},
|
||||
nil,
|
||||
).ToString(fmt.Sprintf("APIPark/%s", s.Name))
|
||||
}
|
||||
}
|
||||
invokeMap, err := i.ProviderStatistics(ctx, time.Now().Add(-24*30*time.Hour), time.Now(), s.Id)
|
||||
|
||||
@@ -236,6 +236,10 @@ func (i *imlServiceModule) AILogs(ctx context.Context, serviceId string, start i
|
||||
return nil, 0, err
|
||||
}
|
||||
return utils.SliceToSlice(list, func(s *log_service.Item) *service_dto.AILogItem {
|
||||
var tokenPerSecond int64 = 0
|
||||
if s.ResponseTime > 0 {
|
||||
tokenPerSecond = s.TotalToken * 1000 / s.ResponseTime
|
||||
}
|
||||
item := &service_dto.AILogItem{
|
||||
Id: s.ID,
|
||||
API: auto.UUID(s.API),
|
||||
@@ -243,7 +247,7 @@ func (i *imlServiceModule) AILogs(ctx context.Context, serviceId string, start i
|
||||
LogTime: auto.TimeLabel(s.RecordTime),
|
||||
Ip: s.RemoteIP,
|
||||
Token: s.TotalToken,
|
||||
TokenPerSecond: s.TotalToken * 1000 / s.ResponseTime,
|
||||
TokenPerSecond: tokenPerSecond,
|
||||
Consumer: auto.UUID(s.Consumer),
|
||||
Provider: auto.UUID(s.AIProvider),
|
||||
Model: s.AIModel,
|
||||
|
||||
@@ -17,12 +17,16 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
globalMessagePath := fmt.Sprintf("/api/v1/%s/message", mcp_server.GlobalBasePath)
|
||||
appSSEPath := fmt.Sprintf("/api/v1/%s/sse", mcp_server.AppBasePath)
|
||||
appMessagePath := fmt.Sprintf("/api/v1/%s/message", mcp_server.AppBasePath)
|
||||
globalMcpPath := fmt.Sprintf("/api/v1/%s/mcp", mcp_server.GlobalBasePath)
|
||||
appMcpPath := fmt.Sprintf("/api/v1/%s/mcp", mcp_server.AppBasePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, serviceSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, serviceMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, globalSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, globalMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, appSSEPath)
|
||||
ignore.IgnorePath("login", http.MethodPost, appMessagePath)
|
||||
ignore.IgnorePath("login", http.MethodGet, globalMcpPath)
|
||||
ignore.IgnorePath("login", http.MethodGet, appMcpPath)
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.MCPHandle),
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.MCPHandle),
|
||||
|
||||
@@ -60,6 +60,7 @@ func (o *openapiCheck) Handler(ginCtx *gin.Context) {
|
||||
}
|
||||
authorization = apikey
|
||||
}
|
||||
authorization = strings.TrimPrefix(authorization, "Bearer ")
|
||||
if authorization == o.apikey {
|
||||
return
|
||||
}
|
||||
|
||||
+21
-8
@@ -19,17 +19,22 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
|
||||
serviceSSEPath := fmt.Sprintf("/openapi/v1/%s/:serviceId/sse", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
serviceMessagePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/message", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
serviceStreamablePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/mcp", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
//serviceStreamablePath := fmt.Sprintf("/openapi/v1/%s/:serviceId/mcp", strings.Trim(mcp_server.ServiceBasePath, "/"))
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodPost, globalMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, globalSSEPath)
|
||||
//ignore.IgnorePath("openapi", http.MethodGet, globalSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, appMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, appSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, serviceSSEPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, serviceMessagePath)
|
||||
ignore.IgnorePath("openapi", http.MethodGet, serviceStreamablePath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, serviceStreamablePath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, serviceStreamablePath)
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodGet, mcp_server.OpenAppMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, mcp_server.OpenAppMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, mcp_server.OpenAppMCPPath)
|
||||
|
||||
ignore.IgnorePath("openapi", http.MethodGet, mcp_server.OpenServiceMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodPost, mcp_server.OpenServiceMCPPath)
|
||||
ignore.IgnorePath("openapi", http.MethodDelete, mcp_server.OpenServiceMCPPath)
|
||||
|
||||
return []pm3.Api{
|
||||
pm3.CreateApiSimple(http.MethodGet, globalSSEPath, p.mcpController.GlobalHandleSSE),
|
||||
@@ -41,9 +46,17 @@ func (p *plugin) mcpAPIs() []pm3.Api {
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceSSEPath, p.mcpController.ServiceHandleSSE),
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceMessagePath, p.mcpController.ServiceHandleMessage),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodPost, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, serviceStreamablePath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenGlobalMCPPath, p.mcpController.GlobalHandleStreamHTTP),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenAppMCPPath, p.mcpController.AppHandleStreamHTTP),
|
||||
|
||||
pm3.CreateApiSimple(http.MethodPost, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodDelete, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
pm3.CreateApiSimple(http.MethodGet, mcp_server.OpenServiceMCPPath, p.mcpController.ServiceHandleStreamHTTP),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -202,6 +202,16 @@ curl -sSO https://download.apipark.com/install/quick-start.sh; bash quick-start.
|
||||
|
||||
# 📕文档
|
||||
访问 [APIPark文档](https://docs.apipark.com/docs/deploy) 获取详细的安装指南、API 参考和使用说明。
|
||||
<br>
|
||||
|
||||
|
||||
|
||||
友情链接
|
||||
<a href="https://xroute.ai/">
|
||||
|
||||
<img width="1248" height="158" alt="新建 PPTX 演示文稿 (2)_02(1)" src="https://github.com/user-attachments/assets/3e1cd0c1-c4c3-4f0c-8649-810d76f3166b" />
|
||||
|
||||
</a>
|
||||
|
||||
<br>
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ package commit
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/eolinker/go-common/utils"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/eolinker/go-common/utils"
|
||||
|
||||
"github.com/eolinker/go-common/store"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
|
||||
Reference in New Issue
Block a user