fix: repair profile data migrations

This commit is contained in:
Jesse_Chen
2026-07-19 14:46:57 +08:00
parent 3df5490ea2
commit 293f6216e3
8 changed files with 52 additions and 55 deletions
@@ -1,53 +0,0 @@
begin;
grant select, insert, update on table public.profiles to service_role;
grant select (
id,
email,
credits,
created_at,
updated_at,
name,
birth_date,
birth_time,
country_code,
province_code,
city_code,
district_code,
latitude,
longitude,
timezone_offset
) on public.profiles to service_role;
grant insert (
id,
email,
name,
birth_date,
birth_time,
country_code,
province_code,
city_code,
district_code,
latitude,
longitude,
timezone_offset,
updated_at
) on public.profiles to service_role;
grant update (
name,
birth_date,
birth_time,
country_code,
province_code,
city_code,
district_code,
latitude,
longitude,
timezone_offset,
updated_at
) on public.profiles to service_role;
commit;
@@ -0,0 +1,9 @@
begin;
-- Existing-row upserts must read every submitted column to resolve the result.
grant select (
district_code,
updated_at
) on table public.profiles to service_role;
commit;
@@ -19,3 +19,21 @@ test("account route can fall back when coordinate columns are not deployed", ()
assert.match(source, /withoutCoordinates/);
assert.match(source, /PGRST204|42703|schema cache|column/i);
});
test("service role can read every column used by account profile upserts", () => {
// Given: the least-privilege grant omitted two columns submitted by /api/account.
const migration = readFileSync(
new URL(
"../supabase/migrations/20260718080000_profiles_service_role_account_upsert_selects.sql",
import.meta.url,
),
"utf8",
);
// When: the corrective migration defines the account-upsert read grant.
// Then: PostgREST can read both submitted columns while resolving existing rows.
assert.match(
migration,
/grant\s+select\s*\(\s*district_code\s*,\s*updated_at\s*\)\s*on\s+table\s+public\.profiles\s+to\s+service_role/is,
);
});
+23
View File
@@ -0,0 +1,23 @@
from collections import defaultdict
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
MIGRATIONS = ROOT / "frontend" / "supabase" / "migrations"
def test_supabase_migration_versions_are_unique() -> None:
# Given: Supabase records the timestamp prefix as the migration identity.
versions: defaultdict[str, list[str]] = defaultdict(list)
# When: every local migration is grouped by that identity.
for migration in sorted(MIGRATIONS.glob("*.sql")):
versions[migration.name.split("_", maxsplit=1)[0]].append(migration.name)
# Then: no migration can be silently skipped behind a duplicate identity.
duplicates = {
version: names
for version, names in versions.items()
if len(names) > 1
}
assert duplicates == {}
+2 -2
View File
@@ -28,14 +28,14 @@ CHART_PROFILE_MIGRATION = (
/ "frontend"
/ "supabase"
/ "migrations"
/ "20260717010000_chart_profiles.sql"
/ "20260718100000_repair_missing_chart_profiles.sql"
)
SYNASTRY_REPORT_MIGRATION = (
Path(__file__).resolve().parents[1]
/ "frontend"
/ "supabase"
/ "migrations"
/ "20260717020000_synastry_reports.sql"
/ "20260718101000_repair_missing_synastry_reports.sql"
)
PAGE = Path(__file__).resolve().parents[1] / "frontend" / "src" / "app" / "page.tsx"
CHART_PROFILE_ROUTE = Path(__file__).resolve().parents[1] / "frontend" / "src" / "app" / "api" / "chart-profiles" / "route.ts"