From a02827897c720711b42839f2f961cc9c33ebd516 Mon Sep 17 00:00:00 2001 From: Wizznu Date: Thu, 26 Mar 2026 18:27:01 +0700 Subject: [PATCH] chore(railway): update worker and add history migration script --- railway-worker/migrate-history-source.js | 105 +++++++++++++++++++++++ railway-worker/worker.js | 2 + 2 files changed, 107 insertions(+) create mode 100644 railway-worker/migrate-history-source.js diff --git a/railway-worker/migrate-history-source.js b/railway-worker/migrate-history-source.js new file mode 100644 index 0000000..097390b --- /dev/null +++ b/railway-worker/migrate-history-source.js @@ -0,0 +1,105 @@ +require('dotenv').config(); +const admin = require('firebase-admin'); + +function inferSource(entry) { + const rawSource = (entry && entry.source ? String(entry.source) : '').trim().toLowerCase(); + + if (rawSource === 'server' || rawSource === 'worker') return 'server'; + if (rawSource === 'app' || rawSource === 'mobile') return 'app'; + + const rawType = (entry && entry.type ? String(entry.type) : '').trim(); + if (rawType.length > 0) return 'server'; + + return 'app'; +} + +async function main() { + const isDryRun = process.argv.includes('--dry-run'); + + const requiredEnvs = [ + 'FIREBASE_PROJECT_ID', + 'FIREBASE_CLIENT_EMAIL', + 'FIREBASE_PRIVATE_KEY', + 'FIREBASE_DATABASE_URL', + ]; + + const missing = requiredEnvs.filter((key) => !process.env[key]); + if (missing.length > 0) { + throw new Error(`Missing env vars: ${missing.join(', ')}`); + } + + admin.initializeApp({ + credential: admin.credential.cert({ + projectId: process.env.FIREBASE_PROJECT_ID, + clientEmail: process.env.FIREBASE_CLIENT_EMAIL, + privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'), + }), + databaseURL: process.env.FIREBASE_DATABASE_URL, + }); + + const db = admin.database(); + const snap = await db.ref('history').get(); + + if (!snap.exists()) { + console.log('No history node found. Nothing to migrate.'); + await admin.app().delete(); + return; + } + + const history = snap.val(); + const updates = {}; + + let totalEntries = 0; + let changedEntries = 0; + let serverAssigned = 0; + let appAssigned = 0; + + for (const dateKey of Object.keys(history)) { + const dateNode = history[dateKey]; + if (!dateNode || typeof dateNode !== 'object') continue; + + for (const timeKey of Object.keys(dateNode)) { + const entry = dateNode[timeKey]; + if (!entry || typeof entry !== 'object') continue; + + totalEntries += 1; + const target = inferSource(entry); + const currentRaw = entry.source == null ? '' : String(entry.source).trim().toLowerCase(); + const current = currentRaw === 'worker' ? 'server' : currentRaw === 'mobile' ? 'app' : currentRaw; + + if (current !== target) { + updates[`history/${dateKey}/${timeKey}/source`] = target; + changedEntries += 1; + if (target === 'server') { + serverAssigned += 1; + } else { + appAssigned += 1; + } + } + } + } + + console.log('=== History Source Migration ==='); + console.log(`Mode: ${isDryRun ? 'DRY RUN' : 'APPLY'}`); + console.log(`Total entries scanned: ${totalEntries}`); + console.log(`Entries to update: ${changedEntries}`); + console.log(`Assign source=server: ${serverAssigned}`); + console.log(`Assign source=app: ${appAssigned}`); + + if (!isDryRun && changedEntries > 0) { + await db.ref().update(updates); + console.log('Migration applied successfully.'); + } else if (!isDryRun) { + console.log('No updates needed.'); + } + + await admin.app().delete(); +} + +main().catch(async (err) => { + console.error('Migration failed:', err.message); + process.exitCode = 1; + try { + await admin.app().delete(); + } catch (_) {} +}); diff --git a/railway-worker/worker.js b/railway-worker/worker.js index 56d15a6..89c3a84 100644 --- a/railway-worker/worker.js +++ b/railway-worker/worker.js @@ -1159,6 +1159,7 @@ async function logHistory(type, potNumbers, duration) { await setFirebaseSmart(`history/${dateKey}/${timeKey}`, { timestamp: now.getTime(), + source: 'server', type: type, pots: potNumbers, duration: duration, @@ -1185,6 +1186,7 @@ const autoLogJob = new cron.CronJob('*/30 * * * *', async () => { await setFirebaseSmart(`history/${dateKey}/${timeKey}`, { timestamp: now.getTime(), + source: 'server', type: 'auto_log', ...sensorData, });