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