mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-01 19:47:37 +03:00
* fix(insights): show error whn reading library counts Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): wait 30 mins before send first report Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): send number of active players, grouped by client type Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): disable reports when running in dev mode Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): add Dockerfile to the docker build, to avoid `vcs.modified=true` Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): add more linux fs types Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): need admin permissions to retrieve library counts Signed-off-by: Deluan <deluan@navidrome.org> * fix(insights): dev flag to disable player insights Signed-off-by: Deluan <deluan@navidrome.org> --------- Signed-off-by: Deluan <deluan@navidrome.org>
169 lines
4.1 KiB
Go
169 lines
4.1 KiB
Go
package persistence
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
. "github.com/Masterminds/squirrel"
|
|
"github.com/deluan/rest"
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/pocketbase/dbx"
|
|
)
|
|
|
|
type playerRepository struct {
|
|
sqlRepository
|
|
}
|
|
|
|
func NewPlayerRepository(ctx context.Context, db dbx.Builder) model.PlayerRepository {
|
|
r := &playerRepository{}
|
|
r.ctx = ctx
|
|
r.db = db
|
|
r.registerModel(&model.Player{}, map[string]filterFunc{
|
|
"name": containsFilter("player.name"),
|
|
})
|
|
r.setSortMappings(map[string]string{
|
|
"user_name": "username", //TODO rename all user_name and userName to username
|
|
})
|
|
return r
|
|
}
|
|
|
|
func (r *playerRepository) Put(p *model.Player) error {
|
|
_, err := r.put(p.ID, p)
|
|
return err
|
|
}
|
|
|
|
func (r *playerRepository) selectPlayer(options ...model.QueryOptions) SelectBuilder {
|
|
return r.newSelect(options...).
|
|
Columns("player.*").
|
|
Join("user ON player.user_id = user.id").
|
|
Columns("user.user_name username")
|
|
}
|
|
|
|
func (r *playerRepository) Get(id string) (*model.Player, error) {
|
|
sel := r.selectPlayer().Where(Eq{"player.id": id})
|
|
var res model.Player
|
|
err := r.queryOne(sel, &res)
|
|
return &res, err
|
|
}
|
|
|
|
func (r *playerRepository) FindMatch(userId, client, userAgent string) (*model.Player, error) {
|
|
sel := r.selectPlayer().Where(And{
|
|
Eq{"client": client},
|
|
Eq{"user_agent": userAgent},
|
|
Eq{"user_id": userId},
|
|
})
|
|
var res model.Player
|
|
err := r.queryOne(sel, &res)
|
|
return &res, err
|
|
}
|
|
|
|
func (r *playerRepository) newRestSelect(options ...model.QueryOptions) SelectBuilder {
|
|
s := r.selectPlayer(options...)
|
|
return s.Where(r.addRestriction())
|
|
}
|
|
|
|
func (r *playerRepository) addRestriction(sql ...Sqlizer) Sqlizer {
|
|
s := And{}
|
|
if len(sql) > 0 {
|
|
s = append(s, sql[0])
|
|
}
|
|
u := loggedUser(r.ctx)
|
|
if u.IsAdmin {
|
|
return s
|
|
}
|
|
return append(s, Eq{"user_id": u.ID})
|
|
}
|
|
|
|
func (r *playerRepository) CountByClient(options ...model.QueryOptions) (map[string]int64, error) {
|
|
sel := r.newSelect(options...).
|
|
Columns(
|
|
"case when client = 'NavidromeUI' then name else client end as player",
|
|
"count(*) as count",
|
|
).GroupBy("client")
|
|
var res []struct {
|
|
Player string
|
|
Count int64
|
|
}
|
|
err := r.queryAll(sel, &res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
counts := make(map[string]int64, len(res))
|
|
for _, c := range res {
|
|
counts[c.Player] = c.Count
|
|
}
|
|
return counts, nil
|
|
}
|
|
|
|
func (r *playerRepository) CountAll(options ...model.QueryOptions) (int64, error) {
|
|
return r.count(r.newRestSelect(), options...)
|
|
}
|
|
|
|
func (r *playerRepository) Count(options ...rest.QueryOptions) (int64, error) {
|
|
return r.CountAll(r.parseRestOptions(r.ctx, options...))
|
|
}
|
|
|
|
func (r *playerRepository) Read(id string) (interface{}, error) {
|
|
sel := r.newRestSelect().Where(Eq{"player.id": id})
|
|
var res model.Player
|
|
err := r.queryOne(sel, &res)
|
|
return &res, err
|
|
}
|
|
|
|
func (r *playerRepository) ReadAll(options ...rest.QueryOptions) (interface{}, error) {
|
|
sel := r.newRestSelect(r.parseRestOptions(r.ctx, options...))
|
|
res := model.Players{}
|
|
err := r.queryAll(sel, &res)
|
|
return res, err
|
|
}
|
|
|
|
func (r *playerRepository) EntityName() string {
|
|
return "player"
|
|
}
|
|
|
|
func (r *playerRepository) NewInstance() interface{} {
|
|
return &model.Player{}
|
|
}
|
|
|
|
func (r *playerRepository) isPermitted(p *model.Player) bool {
|
|
u := loggedUser(r.ctx)
|
|
return u.IsAdmin || p.UserId == u.ID
|
|
}
|
|
|
|
func (r *playerRepository) Save(entity interface{}) (string, error) {
|
|
t := entity.(*model.Player)
|
|
if !r.isPermitted(t) {
|
|
return "", rest.ErrPermissionDenied
|
|
}
|
|
id, err := r.put(t.ID, t)
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
return "", rest.ErrNotFound
|
|
}
|
|
return id, err
|
|
}
|
|
|
|
func (r *playerRepository) Update(id string, entity interface{}, cols ...string) error {
|
|
t := entity.(*model.Player)
|
|
t.ID = id
|
|
if !r.isPermitted(t) {
|
|
return rest.ErrPermissionDenied
|
|
}
|
|
_, err := r.put(id, t, cols...)
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
return rest.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (r *playerRepository) Delete(id string) error {
|
|
filter := r.addRestriction(And{Eq{"player.id": id}})
|
|
err := r.delete(filter)
|
|
if errors.Is(err, model.ErrNotFound) {
|
|
return rest.ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
var _ model.PlayerRepository = (*playerRepository)(nil)
|
|
var _ rest.Repository = (*playerRepository)(nil)
|
|
var _ rest.Persistable = (*playerRepository)(nil)
|