mirror of
https://github.com/containous/traefik.git
synced 2024-12-21 09:34:05 +03:00
Configurable API & Dashboard base path
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
parent
090db6d4b0
commit
0ec12c7aa7
@ -87,8 +87,44 @@ rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashb
|
||||
??? example "Dashboard Dynamic Configuration Examples"
|
||||
--8<-- "content/operations/include-dashboard-examples.md"
|
||||
|
||||
### Custom API Base Path
|
||||
|
||||
As shown above, by default Traefik exposes its API and Dashboard under the `/` base path,
|
||||
which means that respectively the API is served under the `/api` path,
|
||||
and the dashboard under the `/dashboard` path.
|
||||
|
||||
However, it is possible to configure this base path:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
# Customizes the base path:
|
||||
# - Serving API under `/traefik/api`
|
||||
# - Serving Dashboard under `/traefik/dashboard`
|
||||
basePath: /traefik
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
# Customizes the base path:
|
||||
# - Serving API under `/traefik/api`
|
||||
# - Serving Dashboard under `/traefik/dashboard`
|
||||
basePath = "/traefik"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# Customizes the base path:
|
||||
# - Serving API under `/traefik/api`
|
||||
# - Serving Dashboard under `/traefik/dashboard`
|
||||
--api.basePath=/traefik
|
||||
```
|
||||
|
||||
??? example "Dashboard Under Custom Path Dynamic Configuration Examples"
|
||||
--8<-- "content/operations/include-dashboard-custom-path-examples.md"
|
||||
|
||||
## Insecure Mode
|
||||
|
||||
!!! warning "Please note that this mode is incompatible with the [custom API base path option](#custom-api-base-path)."
|
||||
|
||||
When _insecure_ mode is enabled, one can access the dashboard on the `traefik` port (default: `8080`) of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
|
||||
|
@ -0,0 +1,83 @@
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Dynamic Configuration
|
||||
labels:
|
||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && PathPrefix(`/traefik`)"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
- "traefik.http.routers.dashboard.middlewares=auth"
|
||||
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="Docker (Swarm)"
|
||||
# Dynamic Configuration
|
||||
deploy:
|
||||
labels:
|
||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && PathPrefix(`/traefik`)"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
- "traefik.http.routers.dashboard.middlewares=auth"
|
||||
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
# Dummy service for Swarm port detection. The port can be any valid integer value.
|
||||
- "traefik.http.services.dummy-svc.loadbalancer.server.port=9999"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes CRD"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: traefik-dashboard
|
||||
spec:
|
||||
routes:
|
||||
- match: Host(`traefik.example.com`) && PathPrefix(`/traefik`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: api@internal
|
||||
kind: TraefikService
|
||||
middlewares:
|
||||
- name: auth
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: auth
|
||||
spec:
|
||||
basicAuth:
|
||||
secret: secretName # Kubernetes secret named "secretName"
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Dynamic Configuration
|
||||
- "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && PathPrefix(`/traefik`)"
|
||||
- "traefik.http.routers.dashboard.service=api@internal"
|
||||
- "traefik.http.routers.dashboard.middlewares=auth"
|
||||
- "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Dynamic Configuration
|
||||
http:
|
||||
routers:
|
||||
dashboard:
|
||||
rule: Host(`traefik.example.com`) && PathPrefix(`/traefik`)
|
||||
service: api@internal
|
||||
middlewares:
|
||||
- auth
|
||||
middlewares:
|
||||
auth:
|
||||
basicAuth:
|
||||
users:
|
||||
- "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"
|
||||
- "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Dynamic Configuration
|
||||
[http.routers.my-api]
|
||||
rule = "Host(`traefik.example.com`) && PathPrefix(`/traefik`)"
|
||||
service = "api@internal"
|
||||
middlewares = ["auth"]
|
||||
|
||||
[http.middlewares.auth.basicAuth]
|
||||
users = [
|
||||
"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/",
|
||||
"test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0",
|
||||
]
|
||||
```
|
@ -42,6 +42,9 @@ Access log format: json | common (Default: ```common```)
|
||||
`--api`:
|
||||
Enable api/dashboard. (Default: ```false```)
|
||||
|
||||
`--api.basepath`:
|
||||
Defines the base path where the API and Dashboard will be exposed. (Default: ```/```)
|
||||
|
||||
`--api.dashboard`:
|
||||
Activate dashboard. (Default: ```true```)
|
||||
|
||||
|
@ -42,6 +42,9 @@ Access log format: json | common (Default: ```common```)
|
||||
`TRAEFIK_API`:
|
||||
Enable api/dashboard. (Default: ```false```)
|
||||
|
||||
`TRAEFIK_API_BASEPATH`:
|
||||
Defines the base path where the API and Dashboard will be exposed. (Default: ```/```)
|
||||
|
||||
`TRAEFIK_API_DASHBOARD`:
|
||||
Activate dashboard. (Default: ```true```)
|
||||
|
||||
|
@ -294,6 +294,7 @@
|
||||
name1 = "foobar"
|
||||
|
||||
[api]
|
||||
basePath = "foobar"
|
||||
insecure = true
|
||||
dashboard = true
|
||||
debug = true
|
||||
|
@ -330,6 +330,7 @@ providers:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
api:
|
||||
basePath: foobar
|
||||
insecure: true
|
||||
dashboard: true
|
||||
debug: true
|
||||
|
@ -1,36 +1,88 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/webui"
|
||||
)
|
||||
|
||||
type indexTemplateData struct {
|
||||
APIUrl string
|
||||
}
|
||||
|
||||
// Handler expose dashboard routes.
|
||||
type Handler struct {
|
||||
BasePath string
|
||||
|
||||
assets fs.FS // optional assets, to override the webui.FS default
|
||||
}
|
||||
|
||||
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
assets := h.assets
|
||||
if assets == nil {
|
||||
assets = webui.FS
|
||||
}
|
||||
|
||||
// allow iframes from traefik domains only
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src
|
||||
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||
|
||||
// The content type must be guessed by the file server.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
if r.RequestURI == "/" {
|
||||
indexTemplate, err := template.ParseFS(assets, "index.html")
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Unable to parse index template")
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
apiPath := strings.TrimSuffix(h.BasePath, "/") + "/api/"
|
||||
if err = indexTemplate.Execute(w, indexTemplateData{APIUrl: apiPath}); err != nil {
|
||||
log.Error().Err(err).Msg("Unable to render index template")
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
http.FileServerFS(assets).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// Append adds dashboard routes on the given router, optionally using the given
|
||||
// assets (or webui.FS otherwise).
|
||||
func Append(router *mux.Router, customAssets fs.FS) {
|
||||
func Append(router *mux.Router, basePath string, customAssets fs.FS) error {
|
||||
assets := customAssets
|
||||
if assets == nil {
|
||||
assets = webui.FS
|
||||
}
|
||||
|
||||
indexTemplate, err := template.ParseFS(assets, "index.html")
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing index template: %w", err)
|
||||
}
|
||||
|
||||
dashboardPath := strings.TrimSuffix(basePath, "/") + "/dashboard/"
|
||||
|
||||
// Expose dashboard
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
Path(basePath).
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/")
|
||||
http.Redirect(resp, req, prefix+"/dashboard/", http.StatusFound)
|
||||
http.Redirect(resp, req, prefix+dashboardPath, http.StatusFound)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix("/dashboard/").
|
||||
Path(dashboardPath).
|
||||
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// allow iframes from our domains only
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src
|
||||
@ -40,22 +92,26 @@ func Append(router *mux.Router, customAssets fs.FS) {
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
http.StripPrefix("/dashboard/", http.FileServerFS(assets)).ServeHTTP(w, r)
|
||||
apiPath := strings.TrimSuffix(basePath, "/") + "/api/"
|
||||
if err = indexTemplate.Execute(w, indexTemplateData{APIUrl: apiPath}); err != nil {
|
||||
log.Error().Err(err).Msg("Unable to render index template")
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (g Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
assets := g.assets
|
||||
if assets == nil {
|
||||
assets = webui.FS
|
||||
}
|
||||
// allow iframes from our domains only
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src
|
||||
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||
|
||||
// The content type must be guessed by the file server.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
http.FileServerFS(assets).ServeHTTP(w, r)
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
PathPrefix(dashboardPath).
|
||||
HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// allow iframes from traefik domains only
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src
|
||||
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||
|
||||
// The content type must be guessed by the file server.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
|
||||
w.Header().Del("Content-Type")
|
||||
|
||||
http.StripPrefix(dashboardPath, http.FileServerFS(assets)).ServeHTTP(w, r)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
@ -78,38 +78,40 @@ func New(staticConfig static.Configuration, runtimeConfig *runtime.Configuration
|
||||
func (h Handler) createRouter() *mux.Router {
|
||||
router := mux.NewRouter().UseEncodedPath()
|
||||
|
||||
apiRouter := router.PathPrefix(h.staticConfig.API.BasePath).Subrouter().UseEncodedPath()
|
||||
|
||||
if h.staticConfig.API.Debug {
|
||||
DebugHandler{}.Append(router)
|
||||
DebugHandler{}.Append(apiRouter)
|
||||
}
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRuntimeConfiguration)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/rawdata").HandlerFunc(h.getRuntimeConfiguration)
|
||||
|
||||
// Experimental endpoint
|
||||
router.Methods(http.MethodGet).Path("/api/overview").HandlerFunc(h.getOverview)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/overview").HandlerFunc(h.getOverview)
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/entrypoints").HandlerFunc(h.getEntryPoints)
|
||||
router.Methods(http.MethodGet).Path("/api/entrypoints/{entryPointID}").HandlerFunc(h.getEntryPoint)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/entrypoints").HandlerFunc(h.getEntryPoints)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/entrypoints/{entryPointID}").HandlerFunc(h.getEntryPoint)
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/http/routers").HandlerFunc(h.getRouters)
|
||||
router.Methods(http.MethodGet).Path("/api/http/routers/{routerID}").HandlerFunc(h.getRouter)
|
||||
router.Methods(http.MethodGet).Path("/api/http/services").HandlerFunc(h.getServices)
|
||||
router.Methods(http.MethodGet).Path("/api/http/services/{serviceID}").HandlerFunc(h.getService)
|
||||
router.Methods(http.MethodGet).Path("/api/http/middlewares").HandlerFunc(h.getMiddlewares)
|
||||
router.Methods(http.MethodGet).Path("/api/http/middlewares/{middlewareID}").HandlerFunc(h.getMiddleware)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/routers").HandlerFunc(h.getRouters)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/routers/{routerID}").HandlerFunc(h.getRouter)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/services").HandlerFunc(h.getServices)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/services/{serviceID}").HandlerFunc(h.getService)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/middlewares").HandlerFunc(h.getMiddlewares)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/http/middlewares/{middlewareID}").HandlerFunc(h.getMiddleware)
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/routers").HandlerFunc(h.getTCPRouters)
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/routers/{routerID}").HandlerFunc(h.getTCPRouter)
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/services").HandlerFunc(h.getTCPServices)
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/services/{serviceID}").HandlerFunc(h.getTCPService)
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/middlewares").HandlerFunc(h.getTCPMiddlewares)
|
||||
router.Methods(http.MethodGet).Path("/api/tcp/middlewares/{middlewareID}").HandlerFunc(h.getTCPMiddleware)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/routers").HandlerFunc(h.getTCPRouters)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/routers/{routerID}").HandlerFunc(h.getTCPRouter)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/services").HandlerFunc(h.getTCPServices)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/services/{serviceID}").HandlerFunc(h.getTCPService)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/middlewares").HandlerFunc(h.getTCPMiddlewares)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/tcp/middlewares/{middlewareID}").HandlerFunc(h.getTCPMiddleware)
|
||||
|
||||
router.Methods(http.MethodGet).Path("/api/udp/routers").HandlerFunc(h.getUDPRouters)
|
||||
router.Methods(http.MethodGet).Path("/api/udp/routers/{routerID}").HandlerFunc(h.getUDPRouter)
|
||||
router.Methods(http.MethodGet).Path("/api/udp/services").HandlerFunc(h.getUDPServices)
|
||||
router.Methods(http.MethodGet).Path("/api/udp/services/{serviceID}").HandlerFunc(h.getUDPService)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/udp/routers").HandlerFunc(h.getUDPRouters)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/udp/routers/{routerID}").HandlerFunc(h.getUDPRouter)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/udp/services").HandlerFunc(h.getUDPServices)
|
||||
apiRouter.Methods(http.MethodGet).Path("/api/udp/services/{serviceID}").HandlerFunc(h.getUDPService)
|
||||
|
||||
version.Handler{}.Append(router)
|
||||
version.Handler{}.Append(apiRouter)
|
||||
|
||||
return router
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package static
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -145,16 +146,18 @@ type TLSClientConfig struct {
|
||||
|
||||
// API holds the API configuration.
|
||||
type API struct {
|
||||
Insecure bool `description:"Activate API directly on the entryPoint named traefik." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
|
||||
Dashboard bool `description:"Activate dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty" export:"true"`
|
||||
Debug bool `description:"Enable additional endpoints for debugging and profiling." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"`
|
||||
DisableDashboardAd bool `description:"Disable ad in the dashboard." json:"disableDashboardAd,omitempty" toml:"disableDashboardAd,omitempty" yaml:"disableDashboardAd,omitempty" export:"true"`
|
||||
BasePath string `description:"Defines the base path where the API and Dashboard will be exposed." json:"basePath,omitempty" toml:"basePath,omitempty" yaml:"basePath,omitempty" export:"true"`
|
||||
Insecure bool `description:"Activate API directly on the entryPoint named traefik." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"`
|
||||
Dashboard bool `description:"Activate dashboard." json:"dashboard,omitempty" toml:"dashboard,omitempty" yaml:"dashboard,omitempty" export:"true"`
|
||||
Debug bool `description:"Enable additional endpoints for debugging and profiling." json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty" export:"true"`
|
||||
DisableDashboardAd bool `description:"Disable ad in the dashboard." json:"disableDashboardAd,omitempty" toml:"disableDashboardAd,omitempty" yaml:"disableDashboardAd,omitempty" export:"true"`
|
||||
// TODO: Re-enable statistics
|
||||
// Statistics *types.Statistics `description:"Enable more detailed statistics." json:"statistics,omitempty" toml:"statistics,omitempty" yaml:"statistics,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"`
|
||||
}
|
||||
|
||||
// SetDefaults sets the default values.
|
||||
func (a *API) SetDefaults() {
|
||||
a.BasePath = "/"
|
||||
a.Dashboard = true
|
||||
}
|
||||
|
||||
@ -360,6 +363,10 @@ func (c *Configuration) ValidateConfiguration() error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.API != nil && !path.IsAbs(c.API.BasePath) {
|
||||
return errors.New("API basePath must be a valid absolute path")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/traefik/traefik/v3/pkg/api"
|
||||
"github.com/traefik/traefik/v3/pkg/api/dashboard"
|
||||
"github.com/traefik/traefik/v3/pkg/config/runtime"
|
||||
@ -44,10 +45,13 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
||||
apiRouterBuilder := api.NewBuilder(staticConfiguration)
|
||||
|
||||
if staticConfiguration.API.Dashboard {
|
||||
factory.dashboardHandler = dashboard.Handler{}
|
||||
factory.dashboardHandler = dashboard.Handler{BasePath: staticConfiguration.API.BasePath}
|
||||
factory.api = func(configuration *runtime.Configuration) http.Handler {
|
||||
router := apiRouterBuilder(configuration).(*mux.Router)
|
||||
dashboard.Append(router, nil)
|
||||
if err := dashboard.Append(router, staticConfiguration.API.BasePath, nil); err != nil {
|
||||
log.Error().Err(err).Msg("Error appending dashboard to API router")
|
||||
}
|
||||
|
||||
return router
|
||||
}
|
||||
} else {
|
||||
|
@ -1,6 +1,13 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
{{if .APIUrl}}
|
||||
<script>
|
||||
window.APIURL = "{{.APIUrl}}"
|
||||
</script>
|
||||
{{end}}
|
||||
|
||||
<title><%= productName %></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
|
@ -4,7 +4,7 @@ import { APP } from '../_helpers/APP'
|
||||
|
||||
// Set config defaults when creating the instance
|
||||
const api = axios.create({
|
||||
baseURL: APP.config.apiUrl
|
||||
baseURL: window.APIURL || APP.config.apiUrl
|
||||
})
|
||||
|
||||
export default boot(({ app }) => {
|
||||
|
Loading…
Reference in New Issue
Block a user