2024-02-21 01:21:19 +00:00
package http
2024-01-05 17:04:46 +00:00
import (
2024-05-02 19:14:10 +00:00
"embed"
2024-01-05 17:04:46 +00:00
"errors"
2024-05-02 19:14:10 +00:00
"net/http"
2024-01-05 17:04:46 +00:00
"strings"
2024-06-23 08:24:36 +00:00
"github.com/mudler/LocalAI/pkg/utils"
2024-03-27 20:10:58 +00:00
2024-06-23 08:24:36 +00:00
"github.com/mudler/LocalAI/core/http/endpoints/localai"
"github.com/mudler/LocalAI/core/http/endpoints/openai"
"github.com/mudler/LocalAI/core/http/routes"
2024-04-17 21:33:49 +00:00
2024-06-23 08:24:36 +00:00
"github.com/mudler/LocalAI/core/config"
"github.com/mudler/LocalAI/core/schema"
"github.com/mudler/LocalAI/core/services"
"github.com/mudler/LocalAI/pkg/model"
2024-01-05 17:04:46 +00:00
2024-04-20 08:43:37 +00:00
"github.com/gofiber/contrib/fiberzerolog"
2024-01-05 17:04:46 +00:00
"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
2024-06-04 19:43:46 +00:00
"github.com/gofiber/fiber/v2/middleware/csrf"
2024-05-07 06:39:23 +00:00
"github.com/gofiber/fiber/v2/middleware/favicon"
2024-05-02 19:14:10 +00:00
"github.com/gofiber/fiber/v2/middleware/filesystem"
2024-01-05 17:04:46 +00:00
"github.com/gofiber/fiber/v2/middleware/recover"
2024-04-20 08:43:37 +00:00
2024-04-20 23:19:57 +00:00
// swagger handler
2024-04-20 08:43:37 +00:00
"github.com/rs/zerolog/log"
2024-01-05 17:04:46 +00:00
)
2024-03-14 22:08:34 +00:00
func readAuthHeader ( c * fiber . Ctx ) string {
authHeader := c . Get ( "Authorization" )
// elevenlabs
xApiKey := c . Get ( "xi-api-key" )
if xApiKey != "" {
authHeader = "Bearer " + xApiKey
}
// anthropic
xApiKey = c . Get ( "x-api-key" )
if xApiKey != "" {
authHeader = "Bearer " + xApiKey
}
return authHeader
}
2024-05-02 19:14:10 +00:00
// Embed a directory
//
//go:embed static/*
var embedDirStatic embed . FS
2024-03-29 21:29:33 +00:00
// @title LocalAI API
// @version 2.0.0
// @description The LocalAI Rest API.
// @termsOfService
// @contact.name LocalAI
// @contact.url https://localai.io
// @license.name MIT
// @license.url https://raw.githubusercontent.com/mudler/LocalAI/master/LICENSE
// @BasePath /
// @securityDefinitions.apikey BearerAuth
// @in header
// @name Authorization
2024-04-17 21:33:49 +00:00
func App ( cl * config . BackendConfigLoader , ml * model . ModelLoader , appConfig * config . ApplicationConfig ) ( * fiber . App , error ) {
2024-06-05 06:45:24 +00:00
fiberCfg := fiber . Config {
2024-04-20 08:43:37 +00:00
Views : renderEngine ( ) ,
BodyLimit : appConfig . UploadLimitMB * 1024 * 1024 , // this is the default limit of 4MB
// We disable the Fiber startup message as it does not conform to structured logging.
// We register a startup log line with connection information in the OnListen hook to keep things user friendly though
DisableStartupMessage : true ,
2024-01-05 17:04:46 +00:00
// Override default error handler
2024-06-05 06:45:24 +00:00
}
if ! appConfig . OpaqueErrors {
// Normally, return errors as JSON responses
fiberCfg . ErrorHandler = func ( ctx * fiber . Ctx , err error ) error {
2024-01-05 17:04:46 +00:00
// Status code defaults to 500
code := fiber . StatusInternalServerError
// Retrieve the custom status code if it's a *fiber.Error
var e * fiber . Error
if errors . As ( err , & e ) {
code = e . Code
}
// Send custom error page
return ctx . Status ( code ) . JSON (
schema . ErrorResponse {
Error : & schema . APIError { Message : err . Error ( ) , Code : code } ,
} ,
)
2024-06-05 06:45:24 +00:00
}
} else {
// If OpaqueErrors are required, replace everything with a blank 500.
fiberCfg . ErrorHandler = func ( ctx * fiber . Ctx , _ error ) error {
return ctx . Status ( 500 ) . SendString ( "" )
}
}
app := fiber . New ( fiberCfg )
2024-01-05 17:04:46 +00:00
2024-04-20 08:43:37 +00:00
app . Hooks ( ) . OnListen ( func ( listenData fiber . ListenData ) error {
scheme := "http"
if listenData . TLS {
scheme = "https"
}
log . Info ( ) . Str ( "endpoint" , scheme + "://" + listenData . Host + ":" + listenData . Port ) . Msg ( "LocalAI API is listening! Please connect to the endpoint for API documentation." )
return nil
} )
// Have Fiber use zerolog like the rest of the application rather than it's built-in logger
logger := log . Logger
app . Use ( fiberzerolog . New ( fiberzerolog . Config {
Logger : & logger ,
} ) )
2024-01-05 17:04:46 +00:00
// Default middleware config
2024-02-17 09:00:34 +00:00
2024-04-17 21:33:49 +00:00
if ! appConfig . Debug {
2024-02-17 09:00:34 +00:00
app . Use ( recover . New ( ) )
}
2024-03-01 15:19:53 +00:00
metricsService , err := services . NewLocalAIMetricsService ( )
if err != nil {
return nil , err
}
if metricsService != nil {
app . Use ( localai . LocalAIMetricsAPIMiddleware ( metricsService ) )
app . Hooks ( ) . OnShutdown ( func ( ) error {
return metricsService . Shutdown ( )
} )
2024-01-05 17:04:46 +00:00
}
// Auth middleware checking if API key is valid. If no API key is set, no auth is required.
auth := func ( c * fiber . Ctx ) error {
2024-04-17 21:33:49 +00:00
if len ( appConfig . ApiKeys ) == 0 {
return c . Next ( )
}
if len ( appConfig . ApiKeys ) == 0 {
2024-01-05 17:04:46 +00:00
return c . Next ( )
}
2024-03-14 22:08:34 +00:00
authHeader := readAuthHeader ( c )
2024-01-05 17:04:46 +00:00
if authHeader == "" {
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Authorization header missing" } )
}
2024-03-14 22:08:34 +00:00
// If it's a bearer token
2024-01-05 17:04:46 +00:00
authHeaderParts := strings . Split ( authHeader , " " )
if len ( authHeaderParts ) != 2 || authHeaderParts [ 0 ] != "Bearer" {
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Invalid Authorization header format" } )
}
apiKey := authHeaderParts [ 1 ]
2024-04-17 21:33:49 +00:00
for _ , key := range appConfig . ApiKeys {
2024-01-05 17:04:46 +00:00
if apiKey == key {
return c . Next ( )
}
}
return c . Status ( fiber . StatusUnauthorized ) . JSON ( fiber . Map { "message" : "Invalid API key" } )
}
2024-04-17 21:33:49 +00:00
if appConfig . CORS {
2024-01-05 17:04:46 +00:00
var c func ( ctx * fiber . Ctx ) error
2024-04-17 21:33:49 +00:00
if appConfig . CORSAllowOrigins == "" {
2024-01-05 17:04:46 +00:00
c = cors . New ( )
} else {
2024-04-17 21:33:49 +00:00
c = cors . New ( cors . Config { AllowOrigins : appConfig . CORSAllowOrigins } )
2024-01-05 17:04:46 +00:00
}
app . Use ( c )
}
2024-06-04 19:43:46 +00:00
if appConfig . CSRF {
log . Debug ( ) . Msg ( "Enabling CSRF middleware. Tokens are now required for state-modifying requests" )
app . Use ( csrf . New ( ) )
}
2024-04-17 21:33:49 +00:00
// Load config jsons
utils . LoadConfig ( appConfig . UploadDir , openai . UploadedFilesFile , & openai . UploadedFiles )
utils . LoadConfig ( appConfig . ConfigsDir , openai . AssistantsConfigFile , & openai . Assistants )
utils . LoadConfig ( appConfig . ConfigsDir , openai . AssistantsFileConfigFile , & openai . AssistantFiles )
2024-06-05 06:45:24 +00:00
galleryService := services . NewGalleryService ( appConfig )
2024-04-23 07:22:58 +00:00
galleryService . Start ( appConfig . Context , cl )
2024-04-20 23:19:57 +00:00
routes . RegisterElevenLabsRoutes ( app , cl , ml , appConfig , auth )
2024-04-23 07:22:58 +00:00
routes . RegisterLocalAIRoutes ( app , cl , ml , appConfig , galleryService , auth )
2024-04-20 23:19:57 +00:00
routes . RegisterOpenAIRoutes ( app , cl , ml , appConfig , auth )
2024-05-02 19:14:10 +00:00
if ! appConfig . DisableWebUI {
routes . RegisterUIRoutes ( app , cl , ml , appConfig , galleryService , auth )
}
2024-04-24 22:19:02 +00:00
routes . RegisterJINARoutes ( app , cl , ml , appConfig , auth )
2024-01-05 17:04:46 +00:00
2024-05-07 06:39:23 +00:00
httpFS := http . FS ( embedDirStatic )
app . Use ( favicon . New ( favicon . Config {
URL : "/favicon.ico" ,
FileSystem : httpFS ,
File : "static/favicon.ico" ,
} ) )
2024-05-02 19:14:10 +00:00
app . Use ( "/static" , filesystem . New ( filesystem . Config {
2024-05-07 06:39:23 +00:00
Root : httpFS ,
2024-05-02 19:14:10 +00:00
PathPrefix : "static" ,
Browse : true ,
} ) )
2024-03-27 20:10:58 +00:00
// Define a custom 404 handler
2024-03-28 20:52:52 +00:00
// Note: keep this at the bottom!
app . Use ( notFoundHandler )
2024-03-27 20:10:58 +00:00
2024-01-05 17:04:46 +00:00
return app , nil
}