init
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.env
|
||||
database-key.json
|
||||
dist
|
||||
node_modules
|
||||
22
package.json
Normal file
22
package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "mongo-firebase-backuper",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@types/node": "^25.0.3",
|
||||
"dotenv": "^17.2.3",
|
||||
"firebase-admin": "^13.6.0",
|
||||
"mongodb": "^7.0.0",
|
||||
"node-cron": "^4.2.1",
|
||||
"ts-node": "^10.9.2",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
12
src/firebase.ts
Normal file
12
src/firebase.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import admin from "firebase-admin";
|
||||
import serviceAccount from "../database-key.json";
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(
|
||||
serviceAccount as admin.ServiceAccount
|
||||
),
|
||||
databaseURL: "https://pixel-battlegrounds-6c5a7-default-rtdb.firebaseio.com"
|
||||
});
|
||||
|
||||
export const rtdb = admin.database();
|
||||
|
||||
41
src/index.ts
Normal file
41
src/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import "dotenv/config";
|
||||
import cron from "node-cron";
|
||||
import { syncPlayers } from "./sync";
|
||||
import { syncMongoToFirebase } from "./syncReverse"; // new function
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.includes("--one-time")) {
|
||||
console.log("🔹 Running one-time Mongo → Firebase sync...");
|
||||
try {
|
||||
await syncMongoToFirebase();
|
||||
console.log("✅ One-time sync finished");
|
||||
} catch (e) {
|
||||
console.error("One-time sync error:", e);
|
||||
}
|
||||
process.exit(0); // exit after one-time run
|
||||
}
|
||||
|
||||
// Default: cron + immediate run
|
||||
console.log("Firebase → Mongo sync running (every 15 minutes)");
|
||||
|
||||
// Run immediately
|
||||
try {
|
||||
await syncPlayers();
|
||||
} catch (e) {
|
||||
console.error("Players sync error:", e);
|
||||
}
|
||||
|
||||
// Schedule every 15 minutes
|
||||
cron.schedule("*/15 * * * *", async () => {
|
||||
try {
|
||||
await syncPlayers();
|
||||
} catch (e) {
|
||||
console.error("Players sync error:", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
14
src/mongo.ts
Normal file
14
src/mongo.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { MongoClient, Db } from "mongodb";
|
||||
|
||||
const uri = process.env.MONGO_URI!;
|
||||
const client = new MongoClient(uri);
|
||||
let db: Db | null = null;
|
||||
|
||||
export async function connectMongo(): Promise<Db> {
|
||||
if (db) return db;
|
||||
|
||||
await client.connect();
|
||||
db = client.db(); // database name is taken from URI
|
||||
return db;
|
||||
}
|
||||
|
||||
38
src/sync.ts
Normal file
38
src/sync.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { rtdb } from "./firebase";
|
||||
import { connectMongo } from "./mongo";
|
||||
import { Document } from "mongodb";
|
||||
|
||||
type PlayerDoc = Document & { _id: number };
|
||||
|
||||
export async function syncPlayers() {
|
||||
console.log("Players sync started:", new Date().toISOString());
|
||||
|
||||
const db = await connectMongo();
|
||||
const collection = db.collection<PlayerDoc>("players");
|
||||
|
||||
// 🔹 RTDB path
|
||||
const snapshot = await rtdb.ref("players").once("value");
|
||||
|
||||
const playersDict = snapshot.val();
|
||||
|
||||
if (!playersDict) {
|
||||
console.log("No players data found");
|
||||
return;
|
||||
}
|
||||
|
||||
const bulkOps = Object.entries(playersDict).map(([id, data]) => ({
|
||||
replaceOne: {
|
||||
filter: { _id: Number(id) },
|
||||
replacement: {
|
||||
_id: Number(id),
|
||||
...(data as object)
|
||||
},
|
||||
upsert: true
|
||||
}
|
||||
}));
|
||||
|
||||
await collection.bulkWrite(bulkOps, { ordered: false });
|
||||
|
||||
console.log("Players synced:", bulkOps.length);
|
||||
}
|
||||
|
||||
24
src/syncReverse.ts
Normal file
24
src/syncReverse.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { rtdb } from "./firebase";
|
||||
import { connectMongo } from "./mongo";
|
||||
|
||||
export async function syncMongoToFirebase() {
|
||||
console.log("Mongo → Firebase sync started:", new Date().toISOString());
|
||||
|
||||
const db = await connectMongo();
|
||||
const collection = db.collection("players");
|
||||
|
||||
const allPlayers = await collection.find().toArray();
|
||||
|
||||
// Convert to dictionary: { numericId: {...playerFields} }
|
||||
const playersDict: Record<string, any> = {};
|
||||
for (const player of allPlayers) {
|
||||
const { _id, ...fields } = player;
|
||||
playersDict[_id.toString()] = fields;
|
||||
}
|
||||
|
||||
// Push to Firebase Realtime Database
|
||||
await rtdb.ref("players").set(playersDict);
|
||||
|
||||
console.log("Mongo → Firebase sync finished:", allPlayers.length, "players");
|
||||
}
|
||||
|
||||
14
tsconfig.json
Normal file
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"module": "CommonJS",
|
||||
"rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user