forked from 20266142/Overheard
86 lines
3.1 KiB
JavaScript
86 lines
3.1 KiB
JavaScript
// scripts/migrate-geohash-precision.js
|
|
//
|
|
// One-time migration: re-encodes every existing message's `geohash` field at
|
|
// precision 9 (previously 6), matching the precision addMessage() now writes
|
|
// for new messages (see src/lib/firebase/messages.js). lat/lng/text/etc are
|
|
// read from each doc but never written back - updateDoc() below is only ever
|
|
// called with { geohash: newGeohash }, so this cannot touch any other field.
|
|
//
|
|
// Firestore's security rules normally only allow `echoCount`/`lastEchoAt` to
|
|
// change via update() (see firestore.rules). Before running this script,
|
|
// temporarily add 'geohash' to that allowlist and deploy:
|
|
//
|
|
// firebase deploy --only firestore:rules
|
|
//
|
|
// Then run this script:
|
|
//
|
|
// node scripts/migrate-geohash-precision.js
|
|
//
|
|
// Then revert firestore.rules back to its original allowlist and redeploy.
|
|
|
|
import { initializeApp } from 'firebase/app';
|
|
import { getFirestore, collection, getDocs, doc, updateDoc } from 'firebase/firestore';
|
|
import ngeohash from 'ngeohash';
|
|
import { readFileSync } from 'fs';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
|
|
// --- load PUBLIC_FIREBASE_* values from .env (same file SvelteKit reads via
|
|
// $env/static/public, which only works inside SvelteKit - this script is a
|
|
// plain Node script, so .env is parsed by hand here instead) --------------
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
const envPath = join(__dirname, '..', '.env');
|
|
const env = {};
|
|
for (const line of readFileSync(envPath, 'utf-8').split('\n')) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith('#')) continue;
|
|
const eq = trimmed.indexOf('=');
|
|
if (eq === -1) continue;
|
|
env[trimmed.slice(0, eq).trim()] = trimmed.slice(eq + 1).trim();
|
|
}
|
|
|
|
const firebaseConfig = {
|
|
apiKey: env.PUBLIC_FIREBASE_API_KEY,
|
|
authDomain: env.PUBLIC_FIREBASE_AUTH_DOMAIN,
|
|
projectId: env.PUBLIC_FIREBASE_PROJECT_ID,
|
|
storageBucket: env.PUBLIC_FIREBASE_STORAGE_BUCKET,
|
|
messagingSenderId: env.PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
|
|
appId: env.PUBLIC_FIREBASE_APP_ID
|
|
};
|
|
|
|
const NEW_PRECISION = 9; // matches addMessage() in src/lib/firebase/messages.js
|
|
|
|
const app = initializeApp(firebaseConfig);
|
|
const db = getFirestore(app);
|
|
|
|
const snapshot = await getDocs(collection(db, 'messages'));
|
|
console.log(`found ${snapshot.docs.length} message(s)`);
|
|
|
|
let updated = 0;
|
|
let skipped = 0;
|
|
|
|
for (const docSnap of snapshot.docs) {
|
|
const data = docSnap.data();
|
|
|
|
if (typeof data.lat !== 'number' || typeof data.lng !== 'number') {
|
|
console.warn(`skipping ${docSnap.id}: missing lat/lng`);
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
const newGeohash = ngeohash.encode(data.lat, data.lng, NEW_PRECISION);
|
|
|
|
if (data.geohash === newGeohash) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
|
|
// ONLY the geohash field is written - everything else on the doc is
|
|
// left exactly as it was.
|
|
await updateDoc(doc(db, 'messages', docSnap.id), { geohash: newGeohash });
|
|
console.log(`${docSnap.id}: ${data.geohash} -> ${newGeohash}`);
|
|
updated++;
|
|
}
|
|
|
|
console.log(`done. updated ${updated}, skipped ${skipped}`);
|