This commit is contained in:
2026-01-01 15:12:52 +01:00
commit 7bc14d7bbc
8 changed files with 169 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.env
database-key.json
dist
node_modules

22
package.json Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node"
}
}