Add BaseURL configuration (fixes #103)

This commit is contained in:
Deluan 2020-04-03 17:50:42 -04:00
parent b8eb22d162
commit 75cd21da1f
17 changed files with 61 additions and 25 deletions

View File

@ -100,6 +100,7 @@ services:
ND_PORT: 4533 ND_PORT: 4533
ND_TRANSCODINGCACHESIZE: 100MB ND_TRANSCODINGCACHESIZE: 100MB
ND_SESSIONTIMEOUT: 30m ND_SESSIONTIMEOUT: 30m
ND_BASEURL: ""
volumes: volumes:
- "./data:/data" - "./data:/data"
- "/path/to/your/music/folder:/music:ro" - "/path/to/your/music/folder:/music:ro"

View File

@ -20,6 +20,7 @@ type nd struct {
DbPath string `` DbPath string ``
LogLevel string `default:"info"` LogLevel string `default:"info"`
SessionTimeout string `default:"30m"` SessionTimeout string `default:"30m"`
BaseURL string `default:""`
IgnoredArticles string `default:"The El La Los Las Le Les Os As O A"` IgnoredArticles string `default:"The El La Los Las Le Les Os As O A"`
IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"` IndexGroups string `default:"A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ) [Unknown]([)"`

View File

@ -27,6 +27,9 @@ const (
DevInitialUserName = "admin" DevInitialUserName = "admin"
DevInitialName = "Dev Admin" DevInitialName = "Dev Admin"
URLPathUI = "/app"
URLPathSubsonicAPI = "/rest"
) )
var ( var (

View File

@ -15,6 +15,7 @@ services:
ND_PORT: 4533 ND_PORT: 4533
ND_TRANSCODINGCACHESIZE: 100MB ND_TRANSCODINGCACHESIZE: 100MB
ND_SESSIONTIMEOUT: 30m ND_SESSIONTIMEOUT: 30m
ND_BASEURL: ""
volumes: volumes:
- "./data:/data" - "./data:/data"
- "./music:/music" - "./music:/music"

View File

@ -19,7 +19,7 @@ func main() {
panic(fmt.Sprintf("Could not create the Subsonic API router. Aborting! err=%v", err)) panic(fmt.Sprintf("Could not create the Subsonic API router. Aborting! err=%v", err))
} }
a := CreateServer(conf.Server.MusicFolder) a := CreateServer(conf.Server.MusicFolder)
a.MountRouter("/rest", subsonic) a.MountRouter(consts.URLPathSubsonicAPI, subsonic)
a.MountRouter("/app", CreateAppRouter("/app")) a.MountRouter(consts.URLPathUI, CreateAppRouter())
a.Run(":" + conf.Server.Port) a.Run(":" + conf.Server.Port)
} }

View File

@ -17,20 +17,21 @@ import (
type Router struct { type Router struct {
ds model.DataStore ds model.DataStore
mux http.Handler mux http.Handler
path string
} }
func New(ds model.DataStore, path string) *Router { func New(ds model.DataStore) *Router {
r := &Router{ds: ds, path: path} return &Router{ds: ds}
r.mux = r.routes() }
return r
func (app *Router) Setup(path string) {
app.mux = app.routes(path)
} }
func (app *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (app *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
app.mux.ServeHTTP(w, r) app.mux.ServeHTTP(w, r)
} }
func (app *Router) routes() http.Handler { func (app *Router) routes(path string) http.Handler {
r := chi.NewRouter() r := chi.NewRouter()
r.Post("/login", Login(app.ds)) r.Post("/login", Login(app.ds))
@ -52,7 +53,7 @@ func (app *Router) routes() http.Handler {
// Serve UI app assets // Serve UI app assets
r.Handle("/", ServeIndex(app.ds)) r.Handle("/", ServeIndex(app.ds))
r.Handle("/*", http.StripPrefix(app.path, http.FileServer(assets.AssetFile()))) r.Handle("/*", http.StripPrefix(path, http.FileServer(assets.AssetFile())))
return r return r
} }

View File

@ -5,8 +5,10 @@ import (
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"strings"
"github.com/deluan/navidrome/assets" "github.com/deluan/navidrome/assets"
"github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/consts" "github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
@ -31,6 +33,7 @@ func ServeIndex(ds model.DataStore) http.HandlerFunc {
t, _ = t.Parse(string(indexStr)) t, _ = t.Parse(string(indexStr))
appConfig := map[string]interface{}{ appConfig := map[string]interface{}{
"firstTime": firstTime, "firstTime": firstTime,
"baseURL": strings.TrimSuffix(conf.Server.BaseURL, "/"),
} }
j, _ := json.Marshal(appConfig) j, _ := json.Marshal(appConfig)
data := map[string]interface{}{ data := map[string]interface{}{

View File

@ -3,10 +3,12 @@ package server
import ( import (
"net/http" "net/http"
"os" "os"
"path"
"path/filepath" "path/filepath"
"time" "time"
"github.com/deluan/navidrome/conf" "github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/consts"
"github.com/deluan/navidrome/log" "github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model" "github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/scanner" "github.com/deluan/navidrome/scanner"
@ -15,6 +17,11 @@ import (
"github.com/go-chi/cors" "github.com/go-chi/cors"
) )
type Handler interface {
http.Handler
Setup(path string)
}
type Server struct { type Server struct {
Scanner *scanner.Scanner Scanner *scanner.Scanner
router *chi.Mux router *chi.Mux
@ -29,11 +36,13 @@ func New(scanner *scanner.Scanner, ds model.DataStore) *Server {
return a return a
} }
func (a *Server) MountRouter(path string, subRouter http.Handler) { func (a *Server) MountRouter(urlPath string, subRouter Handler) {
log.Info("Mounting routes", "path", path) urlPath = path.Join(conf.Server.BaseURL, urlPath)
log.Info("Mounting routes", "path", urlPath)
subRouter.Setup(urlPath)
a.router.Group(func(r chi.Router) { a.router.Group(func(r chi.Router) {
r.Use(RequestLogger) r.Use(RequestLogger)
r.Mount(path, subRouter) r.Mount(urlPath, subRouter)
}) })
} }
@ -53,8 +62,9 @@ func (a *Server) initRoutes() {
r.Use(middleware.Heartbeat("/ping")) r.Use(middleware.Heartbeat("/ping"))
r.Use(InjectLogger) r.Use(InjectLogger)
indexHtml := path.Join(conf.Server.BaseURL, consts.URLPathUI, "index.html")
r.Get("/", func(w http.ResponseWriter, r *http.Request) { r.Get("/", func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/app", 302) http.Redirect(w, r, indexHtml, 302)
}) })
workDir, _ := os.Getwd() workDir, _ := os.Getwd()

View File

@ -42,6 +42,8 @@ func New(browser engine.Browser, cover engine.Cover, listGenerator engine.ListGe
return r return r
} }
func (api *Router) Setup(path string) {}
func (api *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (api *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
api.mux.ServeHTTP(w, r) api.mux.ServeHTTP(w, r)
} }

View File

@ -25,7 +25,7 @@
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"homepage": "https://localhost/app/", "homepage": ".",
"proxy": "http://localhost:4633/", "proxy": "http://localhost:4633/",
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"

View File

@ -34,6 +34,7 @@ const App = () => {
if (appConfig.firstTime) { if (appConfig.firstTime) {
localStorage.setItem('initialAccountCreation', 'true') localStorage.setItem('initialAccountCreation', 'true')
} }
localStorage.setItem('baseURL', appConfig.baseURL)
} catch (e) {} } catch (e) {}
return ( return (

View File

@ -1,11 +1,12 @@
import jwtDecode from 'jwt-decode' import jwtDecode from 'jwt-decode'
import md5 from 'md5-hex' import md5 from 'md5-hex'
import baseUrl from './utils/baseUrl'
const authProvider = { const authProvider = {
login: ({ username, password }) => { login: ({ username, password }) => {
let url = '/app/login' let url = baseUrl('/app/login')
if (localStorage.getItem('initialAccountCreation')) { if (localStorage.getItem('initialAccountCreation')) {
url = '/app/createAdmin' url = baseUrl('/app/createAdmin')
} }
const request = new Request(url, { const request = new Request(url, {
method: 'POST', method: 'POST',

View File

@ -1,10 +1,12 @@
import { fetchUtils } from 'react-admin' import { fetchUtils } from 'react-admin'
import jsonServerProvider from 'ra-data-json-server' import jsonServerProvider from 'ra-data-json-server'
import baseUrl from './utils/baseUrl'
const baseUrl = '/app/api' const restUrl = '/app/api'
const httpClient = (url, options = {}) => { const httpClient = (url, options = {}) => {
url = url.replace(baseUrl + '/albumSong', baseUrl + '/song') url = baseUrl(url)
url = url.replace(restUrl + '/albumSong', restUrl + '/song')
if (!options.headers) { if (!options.headers) {
options.headers = new Headers({ Accept: 'application/json' }) options.headers = new Headers({ Accept: 'application/json' })
} }
@ -22,6 +24,6 @@ const httpClient = (url, options = {}) => {
}) })
} }
const dataProvider = jsonServerProvider(baseUrl, httpClient) const dataProvider = jsonServerProvider(restUrl, httpClient)
export default dataProvider export default dataProvider

View File

@ -1,4 +1,5 @@
import { fetchUtils } from 'react-admin' import { fetchUtils } from 'react-admin'
import baseUrl from "../utils/baseUrl"
const url = (command, id, options) => { const url = (command, id, options) => {
const params = new URLSearchParams() const params = new URLSearchParams()
@ -18,7 +19,8 @@ const url = (command, id, options) => {
params.append(k, options[k]) params.append(k, options[k])
}) })
} }
return `rest/${command}?${params.toString()}` const url = `/rest/${command}?${params.toString()}`
return baseUrl(url)
} }
const scrobble = (id, submit) => { const scrobble = (id, submit) => {

8
ui/src/utils/baseUrl.js Normal file
View File

@ -0,0 +1,8 @@
const baseUrl = (path) => {
const base = localStorage.getItem('baseURL') || ''
const parts = [base]
parts.push(path.replace(/^\//, ''))
return parts.join('/')
}
export default baseUrl

View File

@ -25,9 +25,9 @@ func CreateServer(musicFolder string) *server.Server {
return serverServer return serverServer
} }
func CreateAppRouter(path string) *app.Router { func CreateAppRouter() *app.Router {
dataStore := persistence.New() dataStore := persistence.New()
router := app.New(dataStore, path) router := app.New(dataStore)
return router return router
} }

View File

@ -27,7 +27,7 @@ func CreateServer(musicFolder string) *server.Server {
)) ))
} }
func CreateAppRouter(path string) *app.Router { func CreateAppRouter() *app.Router {
panic(wire.Build(allProviders)) panic(wire.Build(allProviders))
} }