feat: create admin dashboard with user preferences and library settings

- Add admin dashboard at /admin route consolidating all user settings
- Move user preferences (username, email, password, theme) from separate page
- Add ebook library preferences section with folder management
- Add scan settings (frequency and auto-scan toggle) with database persistence
- Create database migration for scan_frequency_minutes and auto_scan_enabled columns
- Add API endpoints for scan settings management:
  - PUT /api/library/scan-settings - Update scan preferences
  - GET /api/library/scan-settings - Get current scan settings
- Update dashboard navigation to link to admin dashboard
- Remove old preferences.html template (functionality moved to admin)
- Create Bruno API testing files for library endpoints
- Add real-time folder loading and management in admin interface
- Implement scan settings persistence and retrieval from database
This commit is contained in:
2026-01-23 09:38:40 -05:00
parent 2dba1d6eb9
commit 11621462f2
12 changed files with 551 additions and 192 deletions
+43 -4
View File
@@ -131,7 +131,7 @@ func (q *Queries) CreateEbookRating(ctx context.Context, arg CreateEbookRatingPa
const CreateUser = `-- name: CreateUser :one
INSERT INTO users (email, username, password_hash, theme)
VALUES ($1, $2, $3, $4)
RETURNING id, email, username, password_hash, theme, created_at, updated_at
RETURNING id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled
`
type CreateUserParams struct {
@@ -157,6 +157,8 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (Users,
&i.Theme,
&i.CreatedAt,
&i.UpdatedAt,
&i.ScanFrequencyMinutes,
&i.AutoScanEnabled,
)
return i, err
}
@@ -373,6 +375,22 @@ func (q *Queries) GetReadingProgress(ctx context.Context, arg GetReadingProgress
return i, err
}
const GetScanSettings = `-- name: GetScanSettings :one
SELECT scan_frequency_minutes, auto_scan_enabled FROM users WHERE id = $1
`
type GetScanSettingsRow struct {
ScanFrequencyMinutes pgtype.Int4 `db:"scan_frequency_minutes" json:"scan_frequency_minutes"`
AutoScanEnabled pgtype.Bool `db:"auto_scan_enabled" json:"auto_scan_enabled"`
}
func (q *Queries) GetScanSettings(ctx context.Context, id pgtype.UUID) (GetScanSettingsRow, error) {
row := q.db.QueryRow(ctx, GetScanSettings, id)
var i GetScanSettingsRow
err := row.Scan(&i.ScanFrequencyMinutes, &i.AutoScanEnabled)
return i, err
}
const GetUser = `-- name: GetUser :one
SELECT id, email, username, theme, created_at, updated_at FROM users WHERE id = $1
`
@@ -401,7 +419,7 @@ func (q *Queries) GetUser(ctx context.Context, id pgtype.UUID) (GetUserRow, erro
}
const GetUserByEmail = `-- name: GetUserByEmail :one
SELECT id, email, username, password_hash, theme, created_at, updated_at FROM users WHERE email = $1
SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE email = $1
`
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (Users, error) {
@@ -415,12 +433,14 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email string) (Users, erro
&i.Theme,
&i.CreatedAt,
&i.UpdatedAt,
&i.ScanFrequencyMinutes,
&i.AutoScanEnabled,
)
return i, err
}
const GetUserByEmailOrUsername = `-- name: GetUserByEmailOrUsername :one
SELECT id, email, username, password_hash, theme, created_at, updated_at FROM users WHERE email = $1 OR username = $1
SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE email = $1 OR username = $1
`
func (q *Queries) GetUserByEmailOrUsername(ctx context.Context, email string) (Users, error) {
@@ -434,12 +454,14 @@ func (q *Queries) GetUserByEmailOrUsername(ctx context.Context, email string) (U
&i.Theme,
&i.CreatedAt,
&i.UpdatedAt,
&i.ScanFrequencyMinutes,
&i.AutoScanEnabled,
)
return i, err
}
const GetUserByUsername = `-- name: GetUserByUsername :one
SELECT id, email, username, password_hash, theme, created_at, updated_at FROM users WHERE username = $1
SELECT id, email, username, password_hash, theme, created_at, updated_at, scan_frequency_minutes, auto_scan_enabled FROM users WHERE username = $1
`
func (q *Queries) GetUserByUsername(ctx context.Context, username string) (Users, error) {
@@ -453,6 +475,8 @@ func (q *Queries) GetUserByUsername(ctx context.Context, username string) (Users
&i.Theme,
&i.CreatedAt,
&i.UpdatedAt,
&i.ScanFrequencyMinutes,
&i.AutoScanEnabled,
)
return i, err
}
@@ -753,6 +777,21 @@ func (q *Queries) UpdateReadingProgress(ctx context.Context, arg UpdateReadingPr
return i, err
}
const UpdateScanSettings = `-- name: UpdateScanSettings :exec
UPDATE users SET scan_frequency_minutes = $2, auto_scan_enabled = $3, updated_at = NOW() WHERE id = $1
`
type UpdateScanSettingsParams struct {
ID pgtype.UUID `db:"id" json:"id"`
ScanFrequencyMinutes pgtype.Int4 `db:"scan_frequency_minutes" json:"scan_frequency_minutes"`
AutoScanEnabled pgtype.Bool `db:"auto_scan_enabled" json:"auto_scan_enabled"`
}
func (q *Queries) UpdateScanSettings(ctx context.Context, arg UpdateScanSettingsParams) error {
_, err := q.db.Exec(ctx, UpdateScanSettings, arg.ID, arg.ScanFrequencyMinutes, arg.AutoScanEnabled)
return err
}
const UpdateUserTheme = `-- name: UpdateUserTheme :exec
UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1
`