summaryrefslogtreecommitdiff
path: root/internal/service/admin.go
blob: f16037a2cffb470286d39b2fac1eb86d256e981b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package service

import (
	"context"
	"fmt"

	"codeberg.org/snonux/play/internal/auth"
	"codeberg.org/snonux/play/internal/clock"
	"codeberg.org/snonux/play/internal/model"
	"codeberg.org/snonux/play/internal/repository"
	"codeberg.org/snonux/play/internal/scanner"
)

// adminService is the concrete implementation of AdminService.
type adminService struct {
	store     repository.AdminServiceStore
	clock     clock.Clock
	hasher    auth.Hasher
	scanner   scanner.Scanner
	mediaRoot string
}

// NewAdminService creates a concrete AdminService.
func NewAdminService(store repository.AdminServiceStore, clk clock.Clock, hasher auth.Hasher, sc scanner.Scanner, mediaRoot string) AdminService {
	return &adminService{
		store:     store,
		clock:     clk,
		hasher:    hasher,
		scanner:   sc,
		mediaRoot: mediaRoot,
	}
}

func (s *adminService) ListTrash(ctx context.Context) ([]model.Media, error) {
	return s.store.ListDeletedMedia(ctx)
}

func (s *adminService) TriggerRescan(ctx context.Context) error {
	if s.scanner == nil {
		return fmt.Errorf("scanner not configured")
	}
	// Use a background context so the scan isn't canceled when the HTTP request finishes.
	if err := s.scanner.Scan(context.Background(), s.mediaRoot); err != nil {
		return fmt.Errorf("scan failed: %w", err)
	}
	return nil
}

func (s *adminService) ListUsers(ctx context.Context) ([]model.User, error) {
	return s.store.ListUsers(ctx)
}

func (s *adminService) CreateUser(ctx context.Context, username, password string, isAdmin bool) (*model.User, error) {
	hash, err := s.hasher.Hash(password)
	if err != nil {
		return nil, fmt.Errorf("hash password: %w", err)
	}

	user := &model.User{
		Username:     username,
		PasswordHash: hash,
		IsAdmin:      isAdmin,
		CreatedAt:    s.clock.Now(),
	}

	id, err := s.store.CreateUser(ctx, user)
	if err != nil {
		return nil, fmt.Errorf("create user: %w", err)
	}
	user.ID = id
	return user, nil
}

func (s *adminService) DeleteUser(ctx context.Context, id int64) error {
	return s.store.DeleteUser(ctx, id)
}

func (s *adminService) ListPermissions(ctx context.Context) (*PermissionsMatrix, error) {
	sets, err := s.store.ListSets(ctx)
	if err != nil {
		return nil, fmt.Errorf("list sets: %w", err)
	}

	users, err := s.store.ListUsers(ctx)
	if err != nil {
		return nil, fmt.Errorf("list users: %w", err)
	}

	var perms []model.SetPermission
	for _, set := range sets {
		setPerms, err := s.store.ListPermissionsBySet(ctx, set.ID)
		if err != nil {
			return nil, fmt.Errorf("list permissions by set: %w", err)
		}
		perms = append(perms, setPerms...)
	}

	return &PermissionsMatrix{
		Sets:        sets,
		Users:       users,
		Permissions: perms,
	}, nil
}

func (s *adminService) GrantPermission(ctx context.Context, setID, userID int64, role model.Role) error {
	perm := &model.SetPermission{
		SetID:     setID,
		UserID:    userID,
		Role:      role,
		CreatedAt: s.clock.Now(),
	}
	return s.store.GrantPermission(ctx, perm)
}

func (s *adminService) RevokePermission(ctx context.Context, setID, userID int64) error {
	return s.store.RevokePermission(ctx, setID, userID)
}