|
| 1 | +// This requires the modern firebase-admin, so it can't be used by functions-v1 |
| 2 | +import {Timestamp, getFirestore} from "firebase-admin/firestore"; |
| 3 | +import {getDatabase} from "firebase-admin/database"; |
| 4 | + |
| 5 | +const HOUR = 1000 * 60 * 60; |
| 6 | + |
| 7 | +interface Logger { |
| 8 | + info(...args: any[]): void; |
| 9 | +} |
| 10 | + |
| 11 | +interface Params { |
| 12 | + appMode: "qa" | "dev"; |
| 13 | + hoursAgo: number; |
| 14 | + logger: Logger; |
| 15 | + dryRun?: boolean; |
| 16 | +} |
| 17 | + |
| 18 | +export async function cleanFirebaseRoots( |
| 19 | + { appMode, hoursAgo, logger, dryRun }: Params |
| 20 | +) { |
| 21 | + |
| 22 | + // Be extra careful so we don't delete production data |
| 23 | + if (!["qa", "dev"].includes(appMode)) { |
| 24 | + throw new Error(`Invalid appMode ${appMode}`); |
| 25 | + } |
| 26 | + |
| 27 | + // Clean up Firestore and Realtime database roots that haven't been updated in a 6 hours |
| 28 | + const cutOffMillis = Date.now() - hoursAgo*HOUR; |
| 29 | + const qaRootsResult = await getFirestore() |
| 30 | + .collection(appMode) |
| 31 | + .where("lastLaunchTime", "<", Timestamp.fromMillis(cutOffMillis)) |
| 32 | + .get(); |
| 33 | + |
| 34 | + logger.info(`Found ${qaRootsResult.size} roots to delete`); |
| 35 | + |
| 36 | + // Need to be careful to clean up the root in the realtime database |
| 37 | + // first. The record in Firestore is our only way to figure out which |
| 38 | + // roots in the realtime database need to be deleted. |
| 39 | + for (const root of qaRootsResult.docs) { |
| 40 | + // The Realtime database root is deleted first incase it fails. |
| 41 | + // This way the root in firestore will remain so we can find it |
| 42 | + // and try again later. |
| 43 | + const databasePath = `/${appMode}/${root.id}`; |
| 44 | + logger.info(`Deleting Realtime Database root: ${databasePath} ...`); |
| 45 | + // TODO: what if the ref doesn't exist? |
| 46 | + if (!dryRun) await getDatabase().ref(`/${appMode}/${root.id}`).remove(); |
| 47 | + logger.info(`Deleting Firestore root: ${root.ref.path} ...`); |
| 48 | + if (!dryRun) await getFirestore().recursiveDelete(root.ref); |
| 49 | + } |
| 50 | + |
| 51 | +} |
0 commit comments