navidrome/server/app/app.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

147 lines
4.1 KiB
Go
Raw Normal View History

2020-01-20 01:34:54 +01:00
package app
import (
2020-01-20 02:40:18 +01:00
"context"
2020-01-20 01:34:54 +01:00
"net/http"
2020-01-20 02:40:18 +01:00
"net/url"
"strings"
2020-01-20 01:34:54 +01:00
2020-01-24 01:44:08 +01:00
"github.com/deluan/navidrome/assets"
"github.com/deluan/navidrome/conf"
2020-02-06 22:48:35 +01:00
"github.com/deluan/navidrome/engine/auth"
2020-01-24 01:44:08 +01:00
"github.com/deluan/navidrome/model"
2020-01-20 02:40:18 +01:00
"github.com/deluan/rest"
2020-01-20 01:34:54 +01:00
"github.com/go-chi/chi"
2020-01-20 15:54:29 +01:00
"github.com/go-chi/jwtauth"
2020-01-20 01:34:54 +01:00
)
type Router struct {
2020-04-03 23:50:42 +02:00
ds model.DataStore
mux http.Handler
2020-01-20 01:34:54 +01:00
}
2020-04-03 23:50:42 +02:00
func New(ds model.DataStore) *Router {
return &Router{ds: ds}
}
func (app *Router) Setup(path string) {
app.mux = app.routes(path)
2020-01-20 01:34:54 +01:00
}
func (app *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
app.mux.ServeHTTP(w, r)
}
2020-04-03 23:50:42 +02:00
func (app *Router) routes(path string) http.Handler {
2020-01-20 01:34:54 +01:00
r := chi.NewRouter()
2020-01-20 02:40:18 +01:00
2020-01-20 15:54:29 +01:00
r.Post("/login", Login(app.ds))
r.Post("/createAdmin", CreateAdmin(app.ds))
2020-01-20 15:54:29 +01:00
2020-01-20 02:40:18 +01:00
r.Route("/api", func(r chi.Router) {
r.Use(mapAuthHeader())
2020-02-06 22:48:35 +01:00
r.Use(jwtauth.Verifier(auth.TokenAuth))
r.Use(authenticator(app.ds))
app.R(r, "/user", model.User{}, true)
app.R(r, "/song", model.MediaFile{}, true)
app.R(r, "/album", model.Album{}, true)
app.R(r, "/artist", model.Artist{}, true)
app.R(r, "/player", model.Player{}, true)
2020-05-04 02:05:03 +02:00
app.R(r, "/playlist", model.Playlist{}, true)
app.R(r, "/transcoding", model.Transcoding{}, conf.Server.EnableTranscodingConfig)
app.RX(r, "/translation", newTranslationRepository, false)
2020-05-16 02:47:15 +02:00
app.addPlaylistTrackRoute(r)
// Keepalive endpoint to be used to keep the session valid (ex: while playing songs)
2020-04-26 18:35:26 +02:00
r.Get("/keepalive/*", func(w http.ResponseWriter, r *http.Request) { _, _ = w.Write([]byte(`{"response":"ok"}`)) })
2020-01-20 02:40:18 +01:00
})
2020-01-23 00:35:44 +01:00
// Serve UI app assets
2020-04-06 23:01:32 +02:00
r.Handle("/", ServeIndex(app.ds, assets.AssetFile()))
2020-04-03 23:50:42 +02:00
r.Handle("/*", http.StripPrefix(path, http.FileServer(assets.AssetFile())))
2020-01-23 00:35:44 +01:00
2020-01-20 01:34:54 +01:00
return r
}
2020-01-20 02:40:18 +01:00
func (app *Router) R(r chi.Router, pathPrefix string, model interface{}, persistable bool) {
constructor := func(ctx context.Context) rest.Repository {
return app.ds.Resource(ctx, model)
}
app.RX(r, pathPrefix, constructor, persistable)
2020-05-02 00:29:50 +02:00
}
func (app *Router) RX(r chi.Router, pathPrefix string, constructor rest.RepositoryConstructor, persistable bool) {
2020-01-20 02:40:18 +01:00
r.Route(pathPrefix, func(r chi.Router) {
r.Get("/", rest.GetAll(constructor))
if persistable {
r.Post("/", rest.Post(constructor))
}
2020-05-02 00:29:50 +02:00
r.Route("/{id}", func(r chi.Router) {
2020-01-20 02:40:18 +01:00
r.Use(UrlParams)
r.Get("/", rest.Get(constructor))
if persistable {
r.Put("/", rest.Put(constructor))
r.Delete("/", rest.Delete(constructor))
}
2020-01-20 02:40:18 +01:00
})
})
}
type restHandler = func(rest.RepositoryConstructor, ...rest.Logger) http.HandlerFunc
2020-05-16 02:47:15 +02:00
func (app *Router) addPlaylistTrackRoute(r chi.Router) {
2020-06-05 01:05:41 +02:00
// Add a middleware to capture the playlistId
wrapper := func(f restHandler) http.HandlerFunc {
return func(res http.ResponseWriter, req *http.Request) {
c := func(ctx context.Context) rest.Repository {
plsRepo := app.ds.Resource(ctx, model.Playlist{})
plsId := chi.URLParam(req, "playlistId")
return plsRepo.(model.PlaylistRepository).Tracks(plsId)
}
f(c).ServeHTTP(res, req)
}
}
r.Route("/playlist/{playlistId}/tracks", func(r chi.Router) {
r.Get("/", wrapper(rest.GetAll))
r.Route("/{id}", func(r chi.Router) {
r.Use(UrlParams)
r.Get("/", wrapper(rest.Get))
2020-06-05 01:05:41 +02:00
r.Put("/", func(w http.ResponseWriter, r *http.Request) {
reorderItem(app.ds)(w, r)
})
2020-05-17 00:10:08 +02:00
r.Delete("/", func(w http.ResponseWriter, r *http.Request) {
deleteFromPlaylist(app.ds)(w, r)
})
})
2020-05-16 02:47:15 +02:00
r.With(UrlParams).Post("/", func(w http.ResponseWriter, r *http.Request) {
addToPlaylist(app.ds)(w, r)
})
})
}
2020-01-20 02:40:18 +01:00
// Middleware to convert Chi URL params (from Context) to query params, as expected by our REST package
func UrlParams(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := chi.RouteContext(r.Context())
parts := make([]string, 0)
for i, key := range ctx.URLParams.Keys {
value := ctx.URLParams.Values[i]
if key == "*" {
continue
}
parts = append(parts, url.QueryEscape(":"+key)+"="+url.QueryEscape(value))
}
q := strings.Join(parts, "&")
if r.URL.RawQuery == "" {
r.URL.RawQuery = q
} else {
r.URL.RawQuery += "&" + q
}
next.ServeHTTP(w, r)
})
}