mirror of
https://github.com/APIParkLab/APIPark.git
synced 2026-06-14 20:41:15 +08:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b54e12950 | |||
| ea32fb1cd6 | |||
| 6ee1996e6f | |||
| fdac169bda | |||
| 3026e24bab | |||
| 1a9b916bab |
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "set NODE_OPTIONS=--max-old-space-size=8192 && lerna run build --scope=core --stream --verbose ",
|
||||
"build": "set NODE_OPTIONS=--max-old-space-size=4096 && lerna run build --scope=core --stream --verbose ",
|
||||
"serve": "lerna run preview --parallel",
|
||||
"serve:remotes": "lerna run serve --scope=remote --parallel",
|
||||
"dev": "lerna run dev --scope=core --stream",
|
||||
|
||||
@@ -93,11 +93,6 @@ const Login: FC = () => {
|
||||
const code = query.get('code')
|
||||
if (code) {
|
||||
feishuLogin(code)
|
||||
setSpinning(false)
|
||||
return
|
||||
}
|
||||
if (isInFeishuClient() && feishu) {
|
||||
openFeishuLogin(feishu.config.client_id)
|
||||
}
|
||||
setSpinning(false)
|
||||
}
|
||||
@@ -166,25 +161,10 @@ const Login: FC = () => {
|
||||
fetchLogin({ username: 'guest', password: '12345678' })
|
||||
}
|
||||
|
||||
const isInFeishuClient = () => {
|
||||
// 方法1:检查User-Agent
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const isLark = ua.includes('lark') || ua.includes('feishu');
|
||||
|
||||
// 方法2:检查全局对象
|
||||
const hasSDK = typeof window.h5sdk !== 'undefined' || typeof window.tt !== 'undefined';
|
||||
|
||||
// 方法3:检查URL参数
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const hasFeishuParams = params.has('from') || params.has('required_launch_ability');
|
||||
|
||||
return isLark || hasSDK || hasFeishuParams;
|
||||
}
|
||||
|
||||
// 打开飞书授权页面
|
||||
const openFeishuLogin = (id?: string) => {
|
||||
const openFeishuLogin = () => {
|
||||
const href = window.location.origin + window.location.pathname
|
||||
const authUrl = `https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=${id || feishuAppId}&redirect_uri=${href}`
|
||||
const authUrl = `https://accounts.feishu.cn/open-apis/authen/v1/authorize?client_id=${feishuAppId}&redirect_uri=${href}`
|
||||
localStorage.setItem('feishuCallbackUrl', href)
|
||||
window.location.href = authUrl
|
||||
}
|
||||
@@ -356,7 +336,7 @@ const Login: FC = () => {
|
||||
loading={loading}
|
||||
className="h-[40px] w-full inline-flex justify-center items-center"
|
||||
type="default"
|
||||
onClick={() => openFeishuLogin(feishuAppId)}
|
||||
onClick={openFeishuLogin}
|
||||
>
|
||||
<img className="h-[30px]" src={FeishuLogo} />
|
||||
{$t('飞书授权登录')}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function ManagementInsidePage() {
|
||||
const { message } = App.useApp()
|
||||
const { fetchData } = useFetch()
|
||||
const { setBreadcrumb } = useBreadcrumb()
|
||||
const [activeMenu, setActiveMenu] = useState<string>('authorization')
|
||||
const [activeMenu, setActiveMenu] = useState<string>('service')
|
||||
const { appId, teamId } = useParams<RouterParams>()
|
||||
const navigateTo = useNavigate()
|
||||
const currentUrl = useLocation().pathname
|
||||
@@ -56,7 +56,7 @@ export default function ManagementInsidePage() {
|
||||
}, [accessData, accessInit, TENANT_MANAGEMENT_APP_MENU])
|
||||
|
||||
useEffect(() => {
|
||||
setActiveMenu(currentUrl.split('/').pop() || 'authorization')
|
||||
setActiveMenu(currentUrl.split('/').pop() || 'service')
|
||||
}, [currentUrl])
|
||||
|
||||
const onMenuClick: MenuProps['onClick'] = (node) => {
|
||||
|
||||
@@ -336,7 +336,7 @@ export default function ServiceHubManagement() {
|
||||
setTableSearchWord={setTableSearchWord}
|
||||
editApp={(row: ServiceHubAppListItem) => {
|
||||
setAppName(row.name)
|
||||
navigateTo(`/consumer/${row.team.id}/inside/${row.id}/authorization`)
|
||||
navigateTo(`/consumer/${row.team.id}/inside/${row.id}/service`)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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