/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package server import ( "html/template" "io/fs" "os" "strings" brotli "github.com/anargu/gin-brotli" "github.com/apache/answer/internal/base/middleware" "github.com/apache/answer/internal/router" "github.com/apache/answer/plugin" "github.com/apache/answer/ui" "github.com/gin-gonic/gin" ) // NewHTTPServer new http server. func NewHTTPServer(debug bool, staticRouter *router.StaticRouter, answerRouter *router.AnswerAPIRouter, swaggerRouter *router.SwaggerRouter, viewRouter *router.UIRouter, authUserMiddleware *middleware.AuthUserMiddleware, avatarMiddleware *middleware.AvatarMiddleware, shortIDMiddleware *middleware.ShortIDMiddleware, templateRouter *router.TemplateRouter, pluginAPIRouter *router.PluginAPIRouter, uiConf *UI, ) *gin.Engine { if debug { gin.SetMode(gin.DebugMode) } else { gin.SetMode(gin.ReleaseMode) } r := gin.New() r.Use(middleware.Recovery( uiConf.APIBaseURL+"/answer/api/v1", uiConf.APIBaseURL+"/answer/admin/api", )) r.Use(func(ctx *gin.Context) { if strings.Contains(ctx.Request.URL.Path, "/chat/completions") { return } brotli.Brotli(brotli.DefaultCompression)(ctx) }, middleware.ExtractAndSetAcceptLanguage, shortIDMiddleware.SetShortIDFlag()) r.GET("/healthz", func(ctx *gin.Context) { ctx.String(200, "OK") }) templatePath := os.Getenv("ANSWER_TEMPLATE_PATH") if templatePath != "" { r.LoadHTMLGlob(templatePath) } else { html, _ := fs.Sub(ui.Template, "template") htmlTemplate := template.Must(template.New("").Funcs(funcMap).ParseFS(html, "*")) r.SetHTMLTemplate(htmlTemplate) } r.Use(middleware.HeadersByRequestURI()) viewRouter.Register(r, uiConf.BaseURL) rootGroup := r.Group("") swaggerRouter.Register(rootGroup) static := r.Group(uiConf.APIBaseURL) static.Use(avatarMiddleware.AvatarThumb(), authUserMiddleware.VisitAuth()) staticRouter.RegisterStaticRouter(static) // The route must be available without logging in mustUnAuthV1 := r.Group(uiConf.APIBaseURL + "/answer/api/v1") answerRouter.RegisterMustUnAuthAnswerAPIRouter(authUserMiddleware, mustUnAuthV1) // register api that no need to login unAuthV1 := r.Group(uiConf.APIBaseURL + "/answer/api/v1") unAuthV1.Use(authUserMiddleware.Auth(), authUserMiddleware.EjectUserBySiteInfo()) answerRouter.RegisterUnAuthAnswerAPIRouter(unAuthV1) // register api that must be authenticated but no need to check account status authWithoutStatusV1 := r.Group(uiConf.APIBaseURL + "/answer/api/v1") authWithoutStatusV1.Use(authUserMiddleware.MustAuthWithoutAccountAvailable()) answerRouter.RegisterAuthUserWithAnyStatusAnswerAPIRouter(authWithoutStatusV1) // register api that must be authenticated authV1 := r.Group(uiConf.APIBaseURL + "/answer/api/v1") authV1.Use(authUserMiddleware.MustAuthAndAccountAvailable()) answerRouter.RegisterAnswerAPIRouter(authV1) adminauthV1 := r.Group(uiConf.APIBaseURL + "/answer/admin/api") adminauthV1.Use(authUserMiddleware.AdminAuth()) answerRouter.RegisterAnswerAdminAPIRouter(adminauthV1) templateRouter.RegisterTemplateRouter(rootGroup, uiConf.BaseURL) // plugin routes pluginAPIRouter.RegisterUnAuthConnectorRouter(mustUnAuthV1) pluginAPIRouter.RegisterAuthUserConnectorRouter(authV1) pluginAPIRouter.RegisterAuthAdminConnectorRouter(adminauthV1) _ = plugin.CallAgent(func(agent plugin.Agent) error { agent.RegisterUnAuthRouter(mustUnAuthV1) agent.RegisterAuthUserRouter(authV1) agent.RegisterAuthAdminRouter(adminauthV1) return nil }) // mcp mcpAPIGroup := r.Group(uiConf.APIBaseURL + "/answer/api/v1") mcpAPIGroup.Use(authUserMiddleware.AuthMcpEnable(), authUserMiddleware.AuthAPIKey()) answerRouter.RegisterMCPRouter(mcpAPIGroup) return r }