-
-
Notifications
You must be signed in to change notification settings - Fork 0
Parse → Supabase DB foundation: read-scope schema, sample importer, local dev #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
hatton
wants to merge
17
commits into
develop
Choose a base branch
from
parse-to-supabase-db-foundation
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 6 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
b618ffe
Local Supabase + Blorg milestone: schema, sample importer, Podman loc…
hatton c515c59
Track supabase/snippets/ (Podman requires bind-mount sources to exist)
hatton f346cf3
Add generated books.tags_text column for wildcard tag matching
hatton 4319597
Merge remote-tracking branch 'origin/develop' into parse-to-supabase-…
hatton 41a9c9d
docs: point db README at renamed FUNCTIONS-MIGRATION-PLAN.md
hatton 9901bf2
Apply preflight review decisions for PR #9
hatton 667563a
Gate book_languages and related_books reads on book visibility
hatton 7990f7b
Dedupe per-book junction rows so a duplicated langPointer can't abort…
hatton ac679c1
Add copyright/license fields to the shared Book type
hatton a9e8a66
Add send-concern-email edge function
hatton c2d9827
Add tests for send-concern-email
hatton 790633a
Document send-concern-email in the migration plan and api-spec-next
hatton 8492977
sync-tool: make SAMPLE_TARGET actually scale the sample size
hatton ce5ed22
Merge send-concern-email edge function into the foundation PR
hatton 8fa208a
Add match_topic_tags RPC for non-canonical topic filters
hatton be6db75
Wire send-concern-email's Mailgun secrets into the local edge runtime
hatton f6aa757
Document the 200k-books performance design goal and query-pattern design
hatton File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| # The database: Parse → Supabase migration notes | ||
|
|
||
| Status (2026-07-17): the **"Local Supabase + Blorg" milestone** is implemented — the | ||
| read-scope schema exists as a migration, `packages/sync-tool` imports a ~100-book sample | ||
| from production Parse, and blorg (branch `SupabaseMigration`) can browse it anonymously | ||
| against a local stack. Parse remains the production system of record; nothing here is | ||
| serving production traffic yet. | ||
|
|
||
| See `FUNCTIONS-MIGRATION-PLAN.md` at the repo root for the *functions* migration (Azure → Edge | ||
| Functions, phases F0–F8). This doc covers the *database* side. | ||
|
|
||
| ## Schema conventions | ||
|
|
||
| - **IDs**: `id TEXT PRIMARY KEY` preserving legacy Parse objectIds. New rows get a | ||
| DB-generated legacy-style 10-char alphanumeric id (`generate_legacy_style_id()`). | ||
| - **Names**: Parse camelCase → snake_case (`bookInstanceId` → `book_instance_id`). | ||
| The conversion is capital-run-aware, so `bloomPUBVersion` → `bloom_pub_version` with no | ||
| special-casing. (The `analytics_*` fields already carry underscores in Parse.) | ||
| - **Types**: Parse String → `text`, Number → `integer`/`numeric`/`bigint` (timestamps), | ||
| Boolean → `boolean`, Date → `timestamptz`, Array-of-strings → `text[]`, | ||
| Object / Array-of-objects → `jsonb` (`show`, `internet_limits`, `tools`). | ||
| - **Field authority**: the private repo `BloomBooks/bloom-parser-server-schema` | ||
| (`schema/production.json`), NOT `setupTables` in bloom-parse-server's cloud code (its | ||
| header admits it's out of date). `publisher_book_id` postdates the dump (bloom-parse-server | ||
| PR #76). | ||
| - **Relations**: `books.lang_pointers text[]` keeps the raw language ids for sync fidelity, | ||
| AND `book_languages` (junction, FKs) exists so PostgREST/supabase-js can embed language | ||
| records with books — the equivalent of Parse's `include=langPointers`: | ||
| `select("*, languages(*)")` (PostgREST resolves the many-to-many through the junction | ||
| automatically). This works because Parse's legacy `languages` array field is deliberately | ||
| not ported (see correction 8) — the name is reserved for the embed. | ||
| `books.uploader_id` → `users(id)`. | ||
| - **RLS**: enabled everywhere; anonymous public read via `Public read` policies + table | ||
| grants; no client write policies (the sync tool writes with the service role, which | ||
| bypasses RLS). Two policies are row-gated: soft-deleted books (`is_deleted`, the future | ||
| sync's tombstones) are never served, and a `users` row is only readable while the user | ||
| has at least one visible book — uploader emails stay embeddable for book display without | ||
| the table being a listable email directory. | ||
| - **Deliberately absent (post-milestone work)**: derivation triggers replacing Parse's | ||
| `beforeSave` (search string, tag normalization, moderator-field preservation — these must | ||
| be gated off for sync connections when they arrive), `updated_at` triggers (same gating | ||
| problem — see correction 7), write policies, Firebase third-party | ||
| auth wiring, and the classes `apiAccount` (needed for the opds function), | ||
| `appSpecification`/`appDetailsInLanguage`/`booksInApp` (verify they're used at all before | ||
| porting), `downloadHistory`, `version`, `bookDeletion` tombstones. | ||
|
|
||
| ## Corrections to the earlier draft plans | ||
|
|
||
| The `supabase/*.md` docs in bloom-parse-server were a useful starting point; things fixed | ||
| or superseded here: | ||
|
|
||
| 1. `uploader_id REFERENCES auth.users(id)` was wrong — under Firebase third-party auth, | ||
| `auth.users` stays empty. It references `public.users` (TEXT legacy ids). | ||
| 2. The draft DDL was missing `edition`, `upload_pending_timestamp`, `banner_image_url` | ||
| (languages), and the whole `apiAccount` class; and misnamed `analytics_started_count`. | ||
| 3. `language.isoCode` is NOT unique in production (multiple rows per code) — the draft's | ||
| UNIQUE constraint would have broken the import. | ||
| 4. `tools` can hold objects — `jsonb`, not `text[]`. | ||
| 5. The draft's legacy-id generator could produce <10 chars (base64 of 8 bytes minus | ||
| stripped symbols); ours draws 24 bytes. | ||
| 6. The auth plan (custom login endpoint + bespoke session tokens) is superseded by | ||
| Supabase's native **third-party Firebase auth** (`[auth.third_party.firebase]`, | ||
| supabase-js `accessToken`, `auth.jwt()` in RLS; requires a `role: 'authenticated'` | ||
| custom claim on Firebase users — a backfill task when we get there). | ||
| 7. The sync plan's derivation-trigger idea conflicts with sync writes: triggers re-deriving | ||
| `search`/tags would fight the Parse-computed values the sync delivers. When triggers | ||
| arrive they must be gated (e.g. a `bloom.sync` session flag) so sync writes pass through | ||
| verbatim — which also gives us a parity test (replay a synced row through the triggers, | ||
| diff against Parse's output). This bit immediately: the v0 schema shipped `updated_at` | ||
| triggers stamping `now()` on update, which meant every importer re-run (an upsert's | ||
| update path) silently replaced Parse's real timestamps with the import time. They're | ||
| removed until the gating mechanism exists. | ||
| 8. Parse's legacy `books.languages` array is not ported. Production evidence (2026-07-18): | ||
| only 146 books have the field, none created after Jan 2015, and every value is `[]` — | ||
| it carries zero information. Keeping it would also have collided with the natural | ||
| PostgREST embed name: a `languages` *column* on `books` and an embedded `languages` | ||
| *relation* can't both appear in one response, forcing every consumer to alias the embed | ||
| forever. `lang_pointers` + `book_languages` carry the real language data. | ||
|
|
||
| ## Roadmap after this milestone (summary) | ||
|
|
||
| 1. **Full sync tool**: watermark-based incremental sync (order: users → languages → tags → | ||
| books), tombstones for book deletions (the ONE change bloom-parse-server needs: an | ||
| `afterDelete("books")` trigger), scheduled runs + validation harness (sampled deep-diffs, | ||
| count checks, lag alerts). | ||
| 2. **Auth**: enable Firebase third-party auth; `role` claim backfill; RLS policies for | ||
| uploader/moderator writes (email-verified, username==email semantics from | ||
| `bloomFirebaseAuthAdapter`). | ||
| 3. **Read-path flips**: edge functions (opds, stats, books-GET) switch from | ||
| `BloomParseServer.ts` to Postgres, one at a time behind env switches, staging first; | ||
| byte-diff outputs against the Parse-sourced versions. | ||
| 4. **blorg**: contract tests green on both backends, production flip of reads+writes | ||
| together at cutover. | ||
| 5. **Write cutover** (joint with F8 books/upload): freeze Parse writes → final sync → | ||
| flip → 1–2 week read-only rollback window → decommission Parse + MongoDB Atlas. | ||
|
|
||
| Key risks: search-semantics parity (Mongo `$text` vs Postgres), the untested `beforeSave` | ||
| behaviors, Parse Pointer-shaped JSON in frozen API contracts, and Firebase claim coverage. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| { | ||
| "name": "@bloom/sync-tool", | ||
| "version": "0.1.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "description": "Parse -> Supabase data import. v0: imports a diverse sample of production books into a local Supabase for the 'Local Supabase + Blorg' milestone. Grows into the full incremental sync tool.", | ||
| "scripts": { | ||
| "import-sample": "node src/import-sample.mjs" | ||
| }, | ||
| "dependencies": { | ||
| "@supabase/supabase-js": "2.110.2" | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.