245 lines
6.8 KiB
JavaScript
245 lines
6.8 KiB
JavaScript
require('dotenv').config();
|
|
const express = require('express');
|
|
const path = require('path');
|
|
const cors = require('cors');
|
|
const admin = require('firebase-admin');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Middleware
|
|
app.use(cors());
|
|
app.use(express.json());
|
|
app.use(express.static('public'));
|
|
|
|
// In-memory storage for FCM tokens (in production, use a database)
|
|
const userTokens = new Map();
|
|
|
|
// Load tokens from file on startup (for persistence)
|
|
const fs = require('fs');
|
|
const TOKENS_FILE = './data/tokens.json';
|
|
|
|
function loadTokens() {
|
|
try {
|
|
if (fs.existsSync(TOKENS_FILE)) {
|
|
const data = fs.readFileSync(TOKENS_FILE, 'utf8');
|
|
const tokens = JSON.parse(data);
|
|
for (const [user, tokenArray] of Object.entries(tokens)) {
|
|
userTokens.set(user, new Set(tokenArray));
|
|
}
|
|
console.log(`Loaded tokens for ${userTokens.size} users from file`);
|
|
}
|
|
} catch (error) {
|
|
console.log('No existing tokens file found, starting fresh');
|
|
}
|
|
}
|
|
|
|
function saveTokens() {
|
|
const tokens = {};
|
|
for (const [user, tokenSet] of userTokens.entries()) {
|
|
tokens[user] = Array.from(tokenSet);
|
|
}
|
|
fs.writeFileSync(TOKENS_FILE, JSON.stringify(tokens, null, 2));
|
|
}
|
|
|
|
// Load existing tokens on startup
|
|
loadTokens();
|
|
|
|
// Auto-save tokens every 30 seconds
|
|
setInterval(() => {
|
|
try {
|
|
saveTokens();
|
|
} catch (error) {
|
|
console.error('Auto-save tokens failed:', error);
|
|
}
|
|
}, 30000);
|
|
|
|
// Initialize Firebase Admin
|
|
if (process.env.FIREBASE_PRIVATE_KEY) {
|
|
const serviceAccount = {
|
|
projectId: process.env.FIREBASE_PROJECT_ID,
|
|
privateKeyId: process.env.FIREBASE_PRIVATE_KEY_ID,
|
|
privateKey: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'),
|
|
clientEmail: process.env.FIREBASE_CLIENT_EMAIL,
|
|
clientId: process.env.FIREBASE_CLIENT_ID,
|
|
authUri: process.env.FIREBASE_AUTH_URI,
|
|
tokenUri: process.env.FIREBASE_TOKEN_URI,
|
|
authProviderX509CertUrl: process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL,
|
|
clientC509CertUrl: process.env.FIREBASE_CLIENT_X509_CERT_URL
|
|
};
|
|
|
|
admin.initializeApp({
|
|
credential: admin.credential.cert(serviceAccount)
|
|
});
|
|
|
|
console.log('Firebase Admin initialized successfully');
|
|
} else {
|
|
console.log('Firebase Admin not configured. Please set up .env file');
|
|
}
|
|
|
|
// Routes
|
|
app.get('/', (req, res) => {
|
|
res.sendFile(path.join(__dirname, 'public', 'index.html'));
|
|
});
|
|
|
|
// Register FCM token
|
|
app.post('/register-token', (req, res) => {
|
|
const { username, token } = req.body;
|
|
|
|
console.log(`Token registration request:`, { username, token: token?.substring(0, 20) + '...' });
|
|
|
|
if (!username || !token) {
|
|
console.log('Token registration failed: missing username or token');
|
|
return res.status(400).json({ error: 'Username and token are required' });
|
|
}
|
|
|
|
// Store token for user
|
|
if (!userTokens.has(username)) {
|
|
userTokens.set(username, new Set());
|
|
}
|
|
|
|
const userTokenSet = userTokens.get(username);
|
|
if (userTokenSet.has(token)) {
|
|
console.log(`Token already registered for user: ${username}`);
|
|
} else {
|
|
userTokenSet.add(token);
|
|
console.log(`New token registered for user: ${username}`);
|
|
// Save immediately after new registration
|
|
try {
|
|
saveTokens();
|
|
} catch (saveError) {
|
|
console.error('Failed to persist tokens to disk:', saveError);
|
|
}
|
|
}
|
|
|
|
console.log(`Total tokens for ${username}: ${userTokenSet.size}`);
|
|
console.log(`Total registered users: ${userTokens.size}`);
|
|
|
|
res.json({ success: true, message: 'Token registered successfully' });
|
|
});
|
|
|
|
// Send notification to all other users
|
|
app.post('/send-notification', async (req, res) => {
|
|
const { fromUser, title, body } = req.body;
|
|
|
|
if (!fromUser || !title || !body) {
|
|
return res.status(400).json({ error: 'fromUser, title, and body are required' });
|
|
}
|
|
|
|
if (!admin.apps.length) {
|
|
return res.status(500).json({ error: 'Firebase Admin not initialized' });
|
|
}
|
|
|
|
try {
|
|
let totalRecipients = 0;
|
|
const promises = [];
|
|
|
|
// Send to all users except the sender
|
|
for (const [username, tokens] of userTokens.entries()) {
|
|
if (username === fromUser) continue; // Skip sender
|
|
|
|
for (const token of tokens) {
|
|
const message = {
|
|
token: token,
|
|
notification: {
|
|
title: title,
|
|
body: body
|
|
},
|
|
webpush: {
|
|
headers: {
|
|
'Urgency': 'high'
|
|
},
|
|
notification: {
|
|
icon: '/icon-192.png',
|
|
badge: '/icon-192.png',
|
|
tag: 'fcm-test'
|
|
},
|
|
fcm_options: {
|
|
link: '/'
|
|
}
|
|
},
|
|
android: {
|
|
priority: 'high',
|
|
notification: {
|
|
sound: 'default',
|
|
click_action: '/'
|
|
}
|
|
},
|
|
apns: {
|
|
payload: {
|
|
aps: {
|
|
sound: 'default',
|
|
badge: 1
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
promises.push(
|
|
admin.messaging().send(message)
|
|
.then(() => {
|
|
console.log(`Notification sent to ${username} successfully`);
|
|
totalRecipients++;
|
|
})
|
|
.catch((error) => {
|
|
console.error(`Error sending notification to ${username}:`, error);
|
|
// Remove invalid token
|
|
if (error.code === 'messaging/registration-token-not-registered') {
|
|
tokens.delete(token);
|
|
}
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
await Promise.all(promises);
|
|
|
|
res.json({
|
|
success: true,
|
|
recipients: totalRecipients,
|
|
message: `Notification sent to ${totalRecipients} recipient(s)`
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Error sending notifications:', error);
|
|
res.status(500).json({ error: 'Failed to send notifications' });
|
|
}
|
|
});
|
|
|
|
// Get all registered users (for debugging)
|
|
app.get('/users', (req, res) => {
|
|
const users = {};
|
|
console.log('Current userTokens map:', userTokens);
|
|
console.log('Number of registered users:', userTokens.size);
|
|
|
|
for (const [username, tokens] of userTokens.entries()) {
|
|
users[username] = {
|
|
tokenCount: tokens.size,
|
|
tokens: Array.from(tokens)
|
|
};
|
|
}
|
|
res.json(users);
|
|
});
|
|
|
|
// Debug endpoint to check server status
|
|
app.get('/debug', (req, res) => {
|
|
res.json({
|
|
firebaseAdminInitialized: admin.apps.length > 0,
|
|
registeredUsers: userTokens.size,
|
|
userTokens: Object.fromEntries(
|
|
Array.from(userTokens.entries()).map(([user, tokens]) => [user, {
|
|
count: tokens.size,
|
|
tokens: Array.from(tokens)
|
|
}])
|
|
),
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
app.listen(PORT, '0.0.0.0', () => {
|
|
console.log(`FCM Test PWA server running on port ${PORT}`);
|
|
console.log(`Open http://localhost:${PORT} in your browser`);
|
|
console.log(`Server listening on all interfaces (0.0.0.0:${PORT})`);
|
|
});
|