feat: option A — visibility gating + author submission system
Some checks failed
Release / Test backend (push) Successful in 1m1s
Release / Check ui (push) Successful in 1m0s
Release / Docker (push) Successful in 11m19s
Release / Deploy to prod (push) Successful in 2m10s
Release / Deploy to homelab (push) Failing after 7s
Release / Gitea Release (push) Successful in 2m25s
Some checks failed
Release / Test backend (push) Successful in 1m1s
Release / Check ui (push) Successful in 1m0s
Release / Docker (push) Successful in 11m19s
Release / Deploy to prod (push) Successful in 2m10s
Release / Deploy to homelab (push) Failing after 7s
Release / Gitea Release (push) Successful in 2m25s
Content visibility:
- Add `visibility` field to books ("public" | "admin_only"); new migration
backfills all existing scraped books to admin_only
- Meilisearch: add visibility as filterable attribute; catalogue/search
endpoints filter to public-only for non-admin requests
- Admin users identified by bearer token bypass the filter and see all books
- All PocketBase discovery queries (trending, recommended, recently-updated,
audio shelf, discover, subscription feed) now filter to visibility=public
- New scraped books default to admin_only; WriteMetadata preserves existing
visibility on PATCH (never overwrites)
Author submission:
- POST /api/admin/books/submit — creates a public book with submitted_by
- PATCH /api/admin/books/{slug}/publish / unpublish — toggle visibility
- SvelteKit proxies: /api/admin/books/[slug]/publish|unpublish
- /api/books/[slug] endpoint for admin book lookup
Frontend:
- backendFetchAdmin() helper sends admin token on any path
- Catalogue server load uses admin fetch when user is admin
- /submit page: author submission form with genre picker and rights assertion
- "Publish" nav link shown to all logged-in users
- Admin catalogue-tools: visibility management panel (load book by slug, toggle)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
75
backend/migrations/20260414000003_visibility.go
Normal file
75
backend/migrations/20260414000003_visibility.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Migration 3 — add visibility + submitted_by fields to books.
|
||||
//
|
||||
// visibility: "public" | "admin_only"
|
||||
// All existing (scraped) books are backfilled to "admin_only".
|
||||
// New author-submitted books are created with "public".
|
||||
//
|
||||
// submitted_by: optional app_users ID for books submitted by a registered author.
|
||||
// Empty for scraped books.
|
||||
//
|
||||
// The backfill iterates books in pages of 200. It is idempotent: books whose
|
||||
// visibility is already set are skipped.
|
||||
package migrations
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
m "github.com/pocketbase/pocketbase/migrations"
|
||||
)
|
||||
|
||||
func init() {
|
||||
m.Register(func(app core.App) error {
|
||||
coll, err := app.FindCollectionByNameOrId("books")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changed := false
|
||||
if coll.Fields.GetByName("visibility") == nil {
|
||||
coll.Fields.Add(&core.TextField{Name: "visibility"})
|
||||
changed = true
|
||||
}
|
||||
if coll.Fields.GetByName("submitted_by") == nil {
|
||||
coll.Fields.Add(&core.TextField{Name: "submitted_by"})
|
||||
changed = true
|
||||
}
|
||||
if changed {
|
||||
if err := app.Save(coll); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Backfill: mark all existing books as admin_only where visibility is empty.
|
||||
// These are scraped books that pre-date this migration.
|
||||
const perPage = 200
|
||||
for page := 1; ; page++ {
|
||||
records, err := app.FindRecordsByFilter(
|
||||
"books", `visibility=""`, "+id", perPage, (page-1)*perPage, nil,
|
||||
)
|
||||
if err != nil || len(records) == 0 {
|
||||
break
|
||||
}
|
||||
for _, rec := range records {
|
||||
rec.Set("visibility", "admin_only")
|
||||
// Best-effort: ignore individual save errors (don't abort migration).
|
||||
_ = app.Save(rec)
|
||||
}
|
||||
if len(records) < perPage {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}, func(app core.App) error {
|
||||
coll, err := app.FindCollectionByNameOrId("books")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
for _, name := range []string{"visibility", "submitted_by"} {
|
||||
f := coll.Fields.GetByName(name)
|
||||
if f == nil {
|
||||
continue
|
||||
}
|
||||
coll.Fields.RemoveById(f.GetId())
|
||||
}
|
||||
return app.Save(coll)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user