feat(api): consolidate user profile update endpoints
Add delete_user, reset_user_password, and update_user endpoints to replace individual update operations. Update database schema to include deleted_at column for soft deletion. Add DeleteUser, ResetUserPassword, and UpdateUserAdmin queries. Update Querier with new methods for user management.
This commit is contained in:
@@ -902,14 +902,8 @@ CREATE INDEX IF NOT EXISTS idx_collection_items_media ON collection_items(media_
|
|||||||
CREATE INDEX IF NOT EXISTS idx_collection_items_excluded ON collection_items(collection_id, excluded)
|
CREATE INDEX IF NOT EXISTS idx_collection_items_excluded ON collection_items(collection_id, excluded)
|
||||||
WHERE excluded = true;
|
WHERE excluded = true;
|
||||||
|
|
||||||
-- Insert 4 system collections (pre-seeded defaults)
|
-- System collections are now created per-user upon registration
|
||||||
-- These are user_id NULL to indicate system ownership
|
-- See CreateDefaultCollectionsForUser in auth.go
|
||||||
INSERT INTO collections (user_id, name, description, icon, color, show_on_dashboard, query_type, priority, is_system_collection, auto_assign_rules) VALUES
|
|
||||||
(NULL, 'continue-reading', 'Books you''re currently reading (0 < progress < 1)', '📖', '#7aa2f7', true, 'continue-reading', 1, true, 'null'),
|
|
||||||
(NULL, 'recently-added', 'Newly added items to this library', '🆕', '#9ece6a', true, 'recently-added', 2, true, 'null'),
|
|
||||||
(NULL, 'recently-read', 'Books you''ve finished (progress >= 1)', '✅', '#e0af68', true, 'recently-read', 3, true, 'null'),
|
|
||||||
(NULL, 'not-started', 'Books you haven''t read yet (progress = 0 or no record)', '📕', '#f7768e', true, 'not-started', 4, true, 'null')
|
|
||||||
ON CONFLICT (user_id, name) DO NOTHING;
|
|
||||||
|
|
||||||
-- Create device_shelf_mappings table (map Bookhoard collections to device-specific shelf names)
|
-- Create device_shelf_mappings table (map Bookhoard collections to device-specific shelf names)
|
||||||
CREATE TABLE IF NOT EXISTS device_shelf_mappings (
|
CREATE TABLE IF NOT EXISTS device_shelf_mappings (
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ type Querier interface {
|
|||||||
CreateSyncHistoryEntry(ctx context.Context, arg CreateSyncHistoryEntryParams) (SyncQueue, error)
|
CreateSyncHistoryEntry(ctx context.Context, arg CreateSyncHistoryEntryParams) (SyncQueue, error)
|
||||||
// Sync Queue Management
|
// Sync Queue Management
|
||||||
CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueItemParams) (SyncQueue, error)
|
CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueItemParams) (SyncQueue, error)
|
||||||
|
CreateSystemCollection(ctx context.Context, arg CreateSystemCollectionParams) (Collections, error)
|
||||||
// ============================================
|
// ============================================
|
||||||
// ENHANCED KOBO SYNC
|
// ENHANCED KOBO SYNC
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -215,7 +216,7 @@ type Querier interface {
|
|||||||
GetSyncQueueItem(ctx context.Context, id pgtype.UUID) (SyncQueue, error)
|
GetSyncQueueItem(ctx context.Context, id pgtype.UUID) (SyncQueue, error)
|
||||||
GetSyncQueueStats(ctx context.Context, deviceID pgtype.UUID) (GetSyncQueueStatsRow, error)
|
GetSyncQueueStats(ctx context.Context, deviceID pgtype.UUID) (GetSyncQueueStatsRow, error)
|
||||||
// Dashboard collections queries
|
// Dashboard collections queries
|
||||||
GetSystemCollectionsForDashboard(ctx context.Context) ([]Collections, error)
|
GetSystemCollectionsForDashboard(ctx context.Context, userID pgtype.UUID) ([]Collections, error)
|
||||||
// SYSTEM CONFIG QUERIES
|
// SYSTEM CONFIG QUERIES
|
||||||
// Get system config
|
// Get system config
|
||||||
GetSystemConfig(ctx context.Context, key string) (SystemConfig, error)
|
GetSystemConfig(ctx context.Context, key string) (SystemConfig, error)
|
||||||
@@ -269,6 +270,7 @@ type Querier interface {
|
|||||||
// Remove book from collection
|
// Remove book from collection
|
||||||
RemoveBookFromCollection(ctx context.Context, arg RemoveBookFromCollectionParams) error
|
RemoveBookFromCollection(ctx context.Context, arg RemoveBookFromCollectionParams) error
|
||||||
RemoveBookFromKoboShelf(ctx context.Context, arg RemoveBookFromKoboShelfParams) error
|
RemoveBookFromKoboShelf(ctx context.Context, arg RemoveBookFromKoboShelfParams) error
|
||||||
|
ResetSystemCollectionMetadata(ctx context.Context, arg ResetSystemCollectionMetadataParams) error
|
||||||
ResolveSyncConflict(ctx context.Context, arg ResolveSyncConflictParams) (SyncConflicts, error)
|
ResolveSyncConflict(ctx context.Context, arg ResolveSyncConflictParams) (SyncConflicts, error)
|
||||||
// Resolve unlinked book
|
// Resolve unlinked book
|
||||||
ResolveUnlinkedBook(ctx context.Context, arg ResolveUnlinkedBookParams) (UnlinkedBooks, error)
|
ResolveUnlinkedBook(ctx context.Context, arg ResolveUnlinkedBookParams) (UnlinkedBooks, error)
|
||||||
@@ -331,6 +333,7 @@ type Querier interface {
|
|||||||
UpdateUniversalProgress(ctx context.Context, arg UpdateUniversalProgressParams) (ReadingProgress, error)
|
UpdateUniversalProgress(ctx context.Context, arg UpdateUniversalProgressParams) (ReadingProgress, error)
|
||||||
UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDevicesParams) (Users, error)
|
UpdateUserMaxDevices(ctx context.Context, arg UpdateUserMaxDevicesParams) (Users, error)
|
||||||
UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) error
|
UpdateUserProfile(ctx context.Context, arg UpdateUserProfileParams) error
|
||||||
|
UpdateUserRole(ctx context.Context, arg UpdateUserRoleParams) (UpdateUserRoleRow, error)
|
||||||
UpdateUserTheme(ctx context.Context, arg UpdateUserThemeParams) error
|
UpdateUserTheme(ctx context.Context, arg UpdateUserThemeParams) error
|
||||||
UpdateUsername(ctx context.Context, arg UpdateUsernameParams) error
|
UpdateUsername(ctx context.Context, arg UpdateUsernameParams) error
|
||||||
UpsertDashboardPreferences(ctx context.Context, arg UpsertDashboardPreferencesParams) (UserDashboardPreferences, error)
|
UpsertDashboardPreferences(ctx context.Context, arg UpsertDashboardPreferencesParams) (UserDashboardPreferences, error)
|
||||||
|
|||||||
@@ -1072,6 +1072,53 @@ func (q *Queries) CreateSyncQueueItem(ctx context.Context, arg CreateSyncQueueIt
|
|||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CreateSystemCollection = `-- name: CreateSystemCollection :one
|
||||||
|
INSERT INTO collections (user_id, name, description, icon, color, show_on_dashboard, query_type, priority, is_system_collection, auto_assign_rules)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true, 'null'::jsonb)
|
||||||
|
RETURNING id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at
|
||||||
|
`
|
||||||
|
|
||||||
|
type CreateSystemCollectionParams struct {
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Description pgtype.Text `db:"description" json:"description"`
|
||||||
|
Icon pgtype.Text `db:"icon" json:"icon"`
|
||||||
|
Color pgtype.Text `db:"color" json:"color"`
|
||||||
|
ShowOnDashboard pgtype.Bool `db:"show_on_dashboard" json:"show_on_dashboard"`
|
||||||
|
QueryType pgtype.Text `db:"query_type" json:"query_type"`
|
||||||
|
Priority pgtype.Int4 `db:"priority" json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) CreateSystemCollection(ctx context.Context, arg CreateSystemCollectionParams) (Collections, error) {
|
||||||
|
row := q.db.QueryRow(ctx, CreateSystemCollection,
|
||||||
|
arg.UserID,
|
||||||
|
arg.Name,
|
||||||
|
arg.Description,
|
||||||
|
arg.Icon,
|
||||||
|
arg.Color,
|
||||||
|
arg.ShowOnDashboard,
|
||||||
|
arg.QueryType,
|
||||||
|
arg.Priority,
|
||||||
|
)
|
||||||
|
var i Collections
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.UserID,
|
||||||
|
&i.Name,
|
||||||
|
&i.Description,
|
||||||
|
&i.Color,
|
||||||
|
&i.Icon,
|
||||||
|
&i.AutoAssignRules,
|
||||||
|
&i.ViewSettings,
|
||||||
|
&i.ShowOnDashboard,
|
||||||
|
&i.QueryType,
|
||||||
|
&i.Priority,
|
||||||
|
&i.IsSystemCollection,
|
||||||
|
&i.CreatedAt,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const CreateUnlinkedBook = `-- name: CreateUnlinkedBook :one
|
const CreateUnlinkedBook = `-- name: CreateUnlinkedBook :one
|
||||||
|
|
||||||
INSERT INTO unlinked_books (device_id, content_id, file_path, title, author, confidence_score)
|
INSERT INTO unlinked_books (device_id, content_id, file_path, title, author, confidence_score)
|
||||||
@@ -1901,12 +1948,16 @@ func (q *Queries) GetCollectionsForBook(ctx context.Context, mediaItemID pgtype.
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GetContinueReadingItems = `-- name: GetContinueReadingItems :many
|
const GetContinueReadingItems = `-- name: GetContinueReadingItems :many
|
||||||
SELECT DISTINCT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||||
INNER JOIN reading_progress rp ON rp.media_item_id = mi.id
|
INNER JOIN (
|
||||||
|
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||||
|
FROM reading_progress
|
||||||
|
WHERE user_id = $2
|
||||||
|
AND percentage > 0
|
||||||
|
AND percentage < 1
|
||||||
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE mi.library_id = $1
|
WHERE mi.library_id = $1
|
||||||
AND rp.user_id = $2
|
|
||||||
AND rp.percentage > 0
|
|
||||||
AND rp.percentage < 1
|
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
LIMIT $3
|
LIMIT $3
|
||||||
`
|
`
|
||||||
@@ -4157,11 +4208,15 @@ func (q *Queries) GetRecentlyAddedItems(ctx context.Context, arg GetRecentlyAdde
|
|||||||
}
|
}
|
||||||
|
|
||||||
const GetRecentlyReadItems = `-- name: GetRecentlyReadItems :many
|
const GetRecentlyReadItems = `-- name: GetRecentlyReadItems :many
|
||||||
SELECT DISTINCT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
SELECT mi.id, mi.library_id, mi.title, mi.author, mi.isbn, mi.description, mi.file_path, mi.file_size, mi.mime_type, mi.cover_image_path, mi.series, mi.series_number, mi.tags, mi.asin, mi.date_published, mi.publisher, mi.contributors, mi.language, mi.edition, mi.page_count, mi.genre, mi.copyright_year, mi.goodreads_id, mi.openlibrary_id, mi.google_books_id, mi.added_by_admin_id, mi.created_at, mi.updated_at, mi.format_group, mi.format_mimetype, mi.is_reflowable, mi.has_fixed_layout, mi.total_characters, mi.chapter_count, mi.entitlement_id, mi.revision_number, mi.kobo_content_id, mi.kobo_metadata, mi.tags_search, mi.contributors_search, mi.file_sha256, mi.opf_identifier, mi.opf_uuid, mi.hash_confidence FROM media_items mi
|
||||||
INNER JOIN reading_progress rp ON rp.media_item_id = mi.id
|
INNER JOIN (
|
||||||
|
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||||
|
FROM reading_progress
|
||||||
|
WHERE user_id = $2
|
||||||
|
AND percentage >= 1
|
||||||
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE mi.library_id = $1
|
WHERE mi.library_id = $1
|
||||||
AND rp.user_id = $2
|
|
||||||
AND rp.percentage >= 1
|
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
LIMIT $3
|
LIMIT $3
|
||||||
`
|
`
|
||||||
@@ -4392,14 +4447,15 @@ func (q *Queries) GetSyncQueueStats(ctx context.Context, deviceID pgtype.UUID) (
|
|||||||
|
|
||||||
const GetSystemCollectionsForDashboard = `-- name: GetSystemCollectionsForDashboard :many
|
const GetSystemCollectionsForDashboard = `-- name: GetSystemCollectionsForDashboard :many
|
||||||
SELECT id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at FROM collections
|
SELECT id, user_id, name, description, color, icon, auto_assign_rules, view_settings, show_on_dashboard, query_type, priority, is_system_collection, created_at FROM collections
|
||||||
WHERE user_id IS NULL
|
WHERE user_id = $1
|
||||||
|
AND is_system_collection = true
|
||||||
AND show_on_dashboard = true
|
AND show_on_dashboard = true
|
||||||
ORDER BY priority ASC
|
ORDER BY priority ASC
|
||||||
`
|
`
|
||||||
|
|
||||||
// Dashboard collections queries
|
// Dashboard collections queries
|
||||||
func (q *Queries) GetSystemCollectionsForDashboard(ctx context.Context) ([]Collections, error) {
|
func (q *Queries) GetSystemCollectionsForDashboard(ctx context.Context, userID pgtype.UUID) ([]Collections, error) {
|
||||||
rows, err := q.db.Query(ctx, GetSystemCollectionsForDashboard)
|
rows, err := q.db.Query(ctx, GetSystemCollectionsForDashboard, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -6649,6 +6705,38 @@ func (q *Queries) RemoveBookFromKoboShelf(ctx context.Context, arg RemoveBookFro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ResetSystemCollectionMetadata = `-- name: ResetSystemCollectionMetadata :exec
|
||||||
|
UPDATE collections
|
||||||
|
SET description = $3,
|
||||||
|
icon = $4,
|
||||||
|
color = $5,
|
||||||
|
priority = $6
|
||||||
|
WHERE user_id = $1
|
||||||
|
AND name = $2
|
||||||
|
AND is_system_collection = true
|
||||||
|
`
|
||||||
|
|
||||||
|
type ResetSystemCollectionMetadataParams struct {
|
||||||
|
UserID pgtype.UUID `db:"user_id" json:"user_id"`
|
||||||
|
Name string `db:"name" json:"name"`
|
||||||
|
Description pgtype.Text `db:"description" json:"description"`
|
||||||
|
Icon pgtype.Text `db:"icon" json:"icon"`
|
||||||
|
Color pgtype.Text `db:"color" json:"color"`
|
||||||
|
Priority pgtype.Int4 `db:"priority" json:"priority"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) ResetSystemCollectionMetadata(ctx context.Context, arg ResetSystemCollectionMetadataParams) error {
|
||||||
|
_, err := q.db.Exec(ctx, ResetSystemCollectionMetadata,
|
||||||
|
arg.UserID,
|
||||||
|
arg.Name,
|
||||||
|
arg.Description,
|
||||||
|
arg.Icon,
|
||||||
|
arg.Color,
|
||||||
|
arg.Priority,
|
||||||
|
)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
const ResolveSyncConflict = `-- name: ResolveSyncConflict :one
|
const ResolveSyncConflict = `-- name: ResolveSyncConflict :one
|
||||||
UPDATE sync_conflicts
|
UPDATE sync_conflicts
|
||||||
SET
|
SET
|
||||||
@@ -8430,6 +8518,35 @@ func (q *Queries) UpdateUserProfile(ctx context.Context, arg UpdateUserProfilePa
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UpdateUserRole = `-- name: UpdateUserRole :one
|
||||||
|
UPDATE users SET role = $2, updated_at = NOW() WHERE id = $1
|
||||||
|
RETURNING id, email, username, role
|
||||||
|
`
|
||||||
|
|
||||||
|
type UpdateUserRoleParams struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
Role string `db:"role" json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UpdateUserRoleRow struct {
|
||||||
|
ID pgtype.UUID `db:"id" json:"id"`
|
||||||
|
Email string `db:"email" json:"email"`
|
||||||
|
Username string `db:"username" json:"username"`
|
||||||
|
Role string `db:"role" json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) UpdateUserRole(ctx context.Context, arg UpdateUserRoleParams) (UpdateUserRoleRow, error) {
|
||||||
|
row := q.db.QueryRow(ctx, UpdateUserRole, arg.ID, arg.Role)
|
||||||
|
var i UpdateUserRoleRow
|
||||||
|
err := row.Scan(
|
||||||
|
&i.ID,
|
||||||
|
&i.Email,
|
||||||
|
&i.Username,
|
||||||
|
&i.Role,
|
||||||
|
)
|
||||||
|
return i, err
|
||||||
|
}
|
||||||
|
|
||||||
const UpdateUserTheme = `-- name: UpdateUserTheme :exec
|
const UpdateUserTheme = `-- name: UpdateUserTheme :exec
|
||||||
UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1
|
UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -337,6 +337,10 @@ UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1;
|
|||||||
UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1
|
UPDATE users SET max_devices = $2, updated_at = NOW() WHERE id = $1
|
||||||
RETURNING *;
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: UpdateUserRole :one
|
||||||
|
UPDATE users SET role = $2, updated_at = NOW() WHERE id = $1
|
||||||
|
RETURNING id, email, username, role;
|
||||||
|
|
||||||
-- name: CountUserDevices :one
|
-- name: CountUserDevices :one
|
||||||
SELECT COUNT(*) FROM devices WHERE user_id = $1;
|
SELECT COUNT(*) FROM devices WHERE user_id = $1;
|
||||||
|
|
||||||
@@ -1621,7 +1625,8 @@ RETURNING *;
|
|||||||
-- Dashboard collections queries
|
-- Dashboard collections queries
|
||||||
-- name: GetSystemCollectionsForDashboard :many
|
-- name: GetSystemCollectionsForDashboard :many
|
||||||
SELECT * FROM collections
|
SELECT * FROM collections
|
||||||
WHERE user_id IS NULL
|
WHERE user_id = $1
|
||||||
|
AND is_system_collection = true
|
||||||
AND show_on_dashboard = true
|
AND show_on_dashboard = true
|
||||||
ORDER BY priority ASC;
|
ORDER BY priority ASC;
|
||||||
|
|
||||||
@@ -1638,14 +1643,33 @@ WHERE user_id = $1
|
|||||||
AND name = $2
|
AND name = $2
|
||||||
AND is_system_collection = true;
|
AND is_system_collection = true;
|
||||||
|
|
||||||
|
-- name: CreateSystemCollection :one
|
||||||
|
INSERT INTO collections (user_id, name, description, icon, color, show_on_dashboard, query_type, priority, is_system_collection, auto_assign_rules)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, true, 'null'::jsonb)
|
||||||
|
RETURNING *;
|
||||||
|
|
||||||
|
-- name: ResetSystemCollectionMetadata :exec
|
||||||
|
UPDATE collections
|
||||||
|
SET description = $3,
|
||||||
|
icon = $4,
|
||||||
|
color = $5,
|
||||||
|
priority = $6
|
||||||
|
WHERE user_id = $1
|
||||||
|
AND name = $2
|
||||||
|
AND is_system_collection = true;
|
||||||
|
|
||||||
-- Smart section queries (for system collections)
|
-- Smart section queries (for system collections)
|
||||||
-- name: GetContinueReadingItems :many
|
-- name: GetContinueReadingItems :many
|
||||||
SELECT DISTINCT mi.* FROM media_items mi
|
SELECT mi.* FROM media_items mi
|
||||||
INNER JOIN reading_progress rp ON rp.media_item_id = mi.id
|
INNER JOIN (
|
||||||
|
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||||
|
FROM reading_progress
|
||||||
|
WHERE user_id = $2
|
||||||
|
AND percentage > 0
|
||||||
|
AND percentage < 1
|
||||||
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE mi.library_id = $1
|
WHERE mi.library_id = $1
|
||||||
AND rp.user_id = $2
|
|
||||||
AND rp.percentage > 0
|
|
||||||
AND rp.percentage < 1
|
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
LIMIT $3;
|
LIMIT $3;
|
||||||
|
|
||||||
@@ -1656,11 +1680,15 @@ ORDER BY mi.created_at DESC
|
|||||||
LIMIT $2;
|
LIMIT $2;
|
||||||
|
|
||||||
-- name: GetRecentlyReadItems :many
|
-- name: GetRecentlyReadItems :many
|
||||||
SELECT DISTINCT mi.* FROM media_items mi
|
SELECT mi.* FROM media_items mi
|
||||||
INNER JOIN reading_progress rp ON rp.media_item_id = mi.id
|
INNER JOIN (
|
||||||
|
SELECT DISTINCT ON (media_item_id) media_item_id, last_read_at
|
||||||
|
FROM reading_progress
|
||||||
|
WHERE user_id = $2
|
||||||
|
AND percentage >= 1
|
||||||
|
ORDER BY media_item_id, last_read_at DESC
|
||||||
|
) rp ON rp.media_item_id = mi.id
|
||||||
WHERE mi.library_id = $1
|
WHERE mi.library_id = $1
|
||||||
AND rp.user_id = $2
|
|
||||||
AND rp.percentage >= 1
|
|
||||||
ORDER BY rp.last_read_at DESC
|
ORDER BY rp.last_read_at DESC
|
||||||
LIMIT $3;
|
LIMIT $3;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user