Skip to content

Commit 610e647

Browse files
committed
fixing update process
1 parent 40abe70 commit 610e647

1 file changed

Lines changed: 137 additions & 29 deletions

File tree

.dev/sync-geometries-table.js

Lines changed: 137 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
/**
44
* Sync Geometries Table with Storage Bucket
55
*
6-
* This script ensures all GeoJSON files in the storage bucket are registered
7-
* in the service_area_geometries table with proper metadata.
6+
* This script ensures the geometries table matches the storage bucket exactly:
7+
* - Inserts new entries for files that exist in bucket but not in table
8+
* - Updates entries with incorrect storage_url (e.g., pointing to wrong bucket)
9+
* - Removes orphaned entries for files that no longer exist in bucket
10+
* - Removes orphaned files from bucket that don't exist locally
811
*
912
* Usage:
1013
* STAGING=true node sync-geometries-table.js (syncs staging)
@@ -13,6 +16,7 @@
1316

1417
import { createClient } from '@supabase/supabase-js'
1518
import { config } from 'dotenv'
19+
import { readdirSync } from 'fs'
1620

1721
// Load .env file
1822
if (!process.env.GITHUB_ACTIONS) {
@@ -69,52 +73,156 @@ async function syncGeometriesTable() {
6973
const existingNames = new Set(existingEntries?.map(e => e.geometry_name) || [])
7074
console.log(` Found ${existingNames.size} existing entries in table\n`)
7175

72-
// Step 3: Insert missing entries
76+
// Step 3: Upsert entries (insert new, update existing with correct URLs)
7377
console.log('📝 Syncing table with storage...')
7478

7579
let inserted = 0
76-
let skipped = 0
80+
let updated = 0
81+
let unchanged = 0
7782
let failed = 0
7883

84+
// Get full existing entries to compare URLs
85+
const { data: fullExistingEntries } = await supabase
86+
.from(table)
87+
.select('geometry_name, storage_url')
88+
89+
const existingUrlMap = new Map(
90+
(fullExistingEntries || []).map(e => [e.geometry_name, e.storage_url])
91+
)
92+
7993
for (const file of geojsonFiles) {
8094
const geometryName = file.name.replace('.geojson', '')
8195

82-
if (existingNames.has(geometryName)) {
83-
console.log(` ⏭️ ${geometryName} (already exists)`)
84-
skipped++
85-
continue
86-
}
87-
88-
// Create public URL
96+
// Create correct public URL for this environment's bucket
8997
const { data: { publicUrl } } = supabase.storage
9098
.from(bucket)
9199
.getPublicUrl(file.name)
92100

93-
// Insert into table
94-
const { error: insertError } = await supabase
95-
.from(table)
96-
.insert({
97-
geometry_name: geometryName,
98-
display_name: geometryName.replace(/-/g, ' '),
99-
storage_url: publicUrl,
100-
file_size: file.metadata?.size || 0,
101-
created_at: file.created_at || new Date().toISOString()
102-
})
103-
104-
if (insertError) {
105-
console.error(` ❌ ${geometryName}: ${insertError.message}`)
106-
failed++
101+
const existingUrl = existingUrlMap.get(geometryName)
102+
103+
if (existingUrl === publicUrl) {
104+
// URL is already correct, skip
105+
unchanged++
106+
continue
107+
}
108+
109+
if (existingNames.has(geometryName)) {
110+
// Entry exists but URL is wrong - update it
111+
const { error: updateError } = await supabase
112+
.from(table)
113+
.update({
114+
storage_url: publicUrl,
115+
file_size: file.metadata?.size || 0
116+
})
117+
.eq('geometry_name', geometryName)
118+
119+
if (updateError) {
120+
console.error(` ❌ ${geometryName}: ${updateError.message}`)
121+
failed++
122+
} else {
123+
console.log(` 🔄 ${geometryName} (updated URL)`)
124+
updated++
125+
}
107126
} else {
108-
console.log(` ✅ ${geometryName} (inserted)`)
109-
inserted++
127+
// New entry - insert it
128+
const { error: insertError } = await supabase
129+
.from(table)
130+
.insert({
131+
geometry_name: geometryName,
132+
display_name: geometryName.replace(/-/g, ' '),
133+
storage_url: publicUrl,
134+
file_size: file.metadata?.size || 0,
135+
created_at: file.created_at || new Date().toISOString()
136+
})
137+
138+
if (insertError) {
139+
console.error(` ❌ ${geometryName}: ${insertError.message}`)
140+
failed++
141+
} else {
142+
console.log(` ✅ ${geometryName} (inserted)`)
143+
inserted++
144+
}
145+
}
146+
}
147+
148+
// Step 4: Read local geometry files to determine what should exist
149+
console.log('\n📂 Reading local geometry files...')
150+
let localFiles
151+
try {
152+
localFiles = readdirSync('./geometries')
153+
.filter(f => f.endsWith('.geojson'))
154+
.map(f => f.replace('.geojson', ''))
155+
} catch (err) {
156+
console.log(' ⚠️ Could not read local geometries folder (running in CI?)')
157+
localFiles = null
158+
}
159+
160+
const localFileSet = localFiles ? new Set(localFiles) : null
161+
if (localFileSet) {
162+
console.log(` Found ${localFileSet.size} local geometry files`)
163+
}
164+
165+
// Step 5: Clean up orphans (in table but not in local files)
166+
let deletedFromTable = 0
167+
let deletedFromBucket = 0
168+
169+
if (localFileSet) {
170+
console.log('\n🧹 Cleaning up orphaned entries...')
171+
172+
// Find orphans in table
173+
const bucketFileNames = new Set(geojsonFiles.map(f => f.name.replace('.geojson', '')))
174+
const tableOrphans = [...existingNames].filter(name => !localFileSet.has(name))
175+
const bucketOrphans = [...bucketFileNames].filter(name => !localFileSet.has(name))
176+
177+
// Delete orphans from table
178+
if (tableOrphans.length > 0) {
179+
console.log(` Found ${tableOrphans.length} orphaned table entries`)
180+
const { error: deleteError } = await supabase
181+
.from(table)
182+
.delete()
183+
.in('geometry_name', tableOrphans)
184+
185+
if (deleteError) {
186+
console.error(` ❌ Error deleting from table: ${deleteError.message}`)
187+
} else {
188+
deletedFromTable = tableOrphans.length
189+
for (const orphan of tableOrphans) {
190+
console.log(` 🗑️ ${orphan} (removed from table)`)
191+
}
192+
}
193+
}
194+
195+
// Delete orphans from bucket
196+
if (bucketOrphans.length > 0) {
197+
console.log(` Found ${bucketOrphans.length} orphaned bucket files`)
198+
const filesToDelete = bucketOrphans.map(name => name + '.geojson')
199+
const { error: storageError } = await supabase.storage
200+
.from(bucket)
201+
.remove(filesToDelete)
202+
203+
if (storageError) {
204+
console.error(` ❌ Error deleting from bucket: ${storageError.message}`)
205+
} else {
206+
deletedFromBucket = bucketOrphans.length
207+
for (const orphan of bucketOrphans) {
208+
console.log(` 🗑️ ${orphan} (removed from bucket)`)
209+
}
210+
}
211+
}
212+
213+
if (tableOrphans.length === 0 && bucketOrphans.length === 0) {
214+
console.log(' ✅ No orphans found')
110215
}
111216
}
112217

113218
console.log('\n📊 Sync Summary:')
114219
console.log(` ✅ Inserted: ${inserted}`)
115-
console.log(` ⏭️ Skipped (existing): ${skipped}`)
220+
console.log(` 🔄 Updated: ${updated}`)
221+
console.log(` ⏭️ Unchanged: ${unchanged}`)
222+
console.log(` 🗑️ Deleted from table: ${deletedFromTable}`)
223+
console.log(` 🗑️ Deleted from bucket: ${deletedFromBucket}`)
116224
console.log(` ❌ Failed: ${failed}`)
117-
console.log(` 📁 Total files: ${geojsonFiles.length}`)
225+
console.log(` 📁 Local files: ${localFileSet ? localFileSet.size : 'N/A'}`)
118226

119227
if (failed === 0) {
120228
console.log('\n✅ Geometries table synced successfully!')

0 commit comments

Comments
 (0)