Files
silo-server/internal/userstore/collection_sort_cleanup.go
T
af37c53838 feat(collections): add profile-scoped default sorting (#489)
* feat(collections): add profile-scoped default sorting

* fix(collections): address review feedback

* chore(collections): preserve rebase migration coverage

* chore(collections): satisfy changed-line lint

* fix(collections): address remaining review feedback

---------

Co-authored-by: Quick104 <31828688+Quick104@users.noreply.github.com>
2026-08-14 15:45:47 -04:00

63 lines
2.4 KiB
Go

package userstore
import (
"context"
"log/slog"
"sort"
)
// CollectionSortPreferenceCleaner removes cross-user viewer preferences when
// a shared library collection is deleted. Library collections live in the
// central catalog while preferences may live in either shared PostgreSQL or
// per-user SQLite, so database foreign keys cannot provide this cleanup.
type CollectionSortPreferenceCleaner struct {
users UserLister
stores UserStoreProvider
logger *slog.Logger
}
// NewCollectionSortPreferenceCleaner wires the cross-user cleanup sweep.
func NewCollectionSortPreferenceCleaner(users UserLister, stores UserStoreProvider) *CollectionSortPreferenceCleaner {
return &CollectionSortPreferenceCleaner{
users: users,
stores: stores,
logger: slog.Default().With("component", "userstore.collection_sort_cleanup"),
}
}
// DeleteForCollection clears every profile's preference for one collection.
// Failures are logged per user and do not undo the owning collection delete;
// stale rows are unreachable because preference writes and reads both require
// the collection to remain accessible.
func (c *CollectionSortPreferenceCleaner) DeleteForCollection(ctx context.Context, kind, collectionID string) {
if c == nil || c.users == nil || c.stores == nil {
return
}
users, err := c.users.List(ctx)
if err != nil {
c.logger.WarnContext(ctx, "collection sort cleanup: list users failed", "kind", kind, "collection_id", collectionID, "error", err)
return
}
sort.Slice(users, func(i, j int) bool { return users[i].ID < users[j].ID })
for _, user := range users {
if ctx.Err() != nil {
return
}
store, err := c.stores.ForUser(ctx, user.ID)
if err != nil {
c.logger.WarnContext(ctx, "collection sort cleanup: open user store failed", "kind", kind, "collection_id", collectionID, "user_id", user.ID, "error", err)
continue
}
profiles, err := store.ListProfiles(ctx)
if err != nil {
c.logger.WarnContext(ctx, "collection sort cleanup: list profiles failed", "kind", kind, "collection_id", collectionID, "user_id", user.ID, "error", err)
continue
}
for _, profile := range profiles {
if err := store.ClearCollectionSortPreference(ctx, profile.ID, kind, collectionID); err != nil {
c.logger.WarnContext(ctx, "collection sort cleanup: delete failed", "kind", kind, "collection_id", collectionID, "user_id", user.ID, "profile_id", profile.ID, "error", err)
}
}
}
}