logger

Self-hosted WhatsApp logger us...
Log | Files | Refs | README | LICENSE

commit 528acda3d918feba7fc6354c67b9636b48d3ba12
parent 7b1c28bca7ac5a4083df641565eac1286ba83f9f
Author: AMIT DUTTA <amitdutta4255@gmail.com>
Date:   Mon, 16 Feb 2026 18:37:12 +0530

Add session management and authentication features
Diffstat:
Mindex.js | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
1 file changed, 100 insertions(+), 25 deletions(-)

diff --git a/index.js b/index.js @@ -10,6 +10,7 @@ const pino = require('pino'); const admin = require('firebase-admin'); const fs = require('fs'); const path = require('path'); +const crypto = require('crypto'); // --- CONFIGURATION --- const PORT = process.env.PORT || 3000; @@ -18,6 +19,7 @@ const AUTH_PASS = process.env.AUTH_PASS; // Initialize Express const app = express(); +app.use(express.urlencoded({ extended: true })); // Parse form data // --- FIREBASE SETUP --- let serviceAccount; @@ -64,11 +66,11 @@ async function startWhatsApp() { if (qr) { qrCodeData = qr; - isConnected = false; // Ensure we know we aren't logged in yet + isConnected = false; } if (connection === 'close') { - isConnected = false; // Reset on disconnect + isConnected = false; const shouldReconnect = (lastDisconnect.error)?.output?.statusCode !== DisconnectReason.loggedOut; if (shouldReconnect) { startWhatsApp(); @@ -76,25 +78,20 @@ async function startWhatsApp() { } else if (connection === 'open') { console.log("System: Connection Open and Authenticated"); qrCodeData = null; - isConnected = true; // Only true when fully open + isConnected = true; } }); sock.ev.on('creds.update', saveCreds); - // --- MESSAGE HANDLING --- sock.ev.on('messages.upsert', async ({ messages, type }) => { - // Allow 'notify' (incoming) and 'append' (sent from phone/history sync) if (type !== 'notify' && type !== 'append') return; for (const msg of messages) { try { if (!msg.message) continue; - // For sent messages, remoteJid is the Recipient. - // For received messages, remoteJid is the Sender. const remoteJid = msg.key.remoteJid; - if (remoteJid === 'status@broadcast') continue; const textContent = @@ -111,17 +108,12 @@ async function startWhatsApp() { : Math.floor(Date.now() / 1000); const isFromMe = msg.key.fromMe || false; - - // Determine name: Use "Me" for outgoing, otherwise pushName or Unknown const senderName = isFromMe ? "Me" : (msg.pushName || "Unknown"); - // --- SAVE TO FIRESTORE --- - // UPDATE: Using .doc(msg.key.id).set(...) instead of .add(...) - // This ensures IDEMPOTENCY. If the same message arrives twice, it won't duplicate. await db.collection('Chats') .doc(remoteJid) .collection('Messages') - .doc(msg.key.id) // <--- CRITICAL CHANGE: Use WhatsApp ID as Doc ID + .doc(msg.key.id) .set({ text: textContent, senderId: remoteJid, @@ -129,7 +121,7 @@ async function startWhatsApp() { timestamp: timestamp, fromMe: isFromMe, id: msg.key.id - }, { merge: true }); // Merge ensures we don't accidentally wipe fields if we add more later + }, { merge: true }); } catch (err) { // Silent error handling @@ -138,39 +130,115 @@ async function startWhatsApp() { }); } +// --- AUTH UTILS --- +// Generate a simple session token (in production use a real secret) +const SESSION_SECRET = crypto.createHash('sha256').update(AUTH_PASS || 'default').digest('hex'); + +function parseCookies(request) { + const list = {}; + const rc = request.headers.cookie; + if (rc) { + rc.split(';').forEach((cookie) => { + const parts = cookie.split('='); + list[parts.shift().trim()] = decodeURI(parts.join('=')); + }); + } + return list; +} + // --- EXPRESS ROUTES --- -// 1. PUBLIC ROUTE: Ping (Must be before Auth middleware) +// 1. PUBLIC ROUTE: Ping (UptimeRobot) app.get('/ping', (req, res) => { res.status(200).send('Pong'); }); -// --- MIDDLEWARE: BASIC AUTH --- +// 2. LOGIN PAGE (GET) +app.get('/login', (req, res) => { + res.send(` + <html> + <body style="font-family: sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; background: #f0f2f5;"> + <form action="/login" method="POST" style="background: white; padding: 2rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); width: 300px;"> + <h2 style="margin-top: 0; text-align: center;">WhatsApp Logger</h2> + <div style="margin-bottom: 1rem;"> + <label style="display: block; margin-bottom: 0.5rem;">Username</label> + <input type="text" name="username" required style="width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;"> + </div> + <div style="margin-bottom: 1rem;"> + <label style="display: block; margin-bottom: 0.5rem;">Password</label> + <input type="password" name="password" required style="width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; box-sizing: border-box;"> + </div> + <div style="margin-bottom: 1rem;"> + <label style="display: flex; align-items: center; font-size: 0.9rem;"> + <input type="checkbox" name="remember" value="yes" style="margin-right: 0.5rem;"> + Keep me logged in for 5 mins + </label> + </div> + <button type="submit" style="width: 100%; padding: 0.75rem; background: #25D366; color: white; border: none; border-radius: 4px; font-weight: bold; cursor: pointer;">Login</button> + </form> + </body> + </html> + `); +}); + +// 3. LOGIN ACTION (POST) +app.post('/login', (req, res) => { + const { username, password, remember } = req.body; + + if (username === AUTH_USER && password === AUTH_PASS) { + let cookieSettings = 'HttpOnly; Path=/;'; // HttpOnly prevents JS access (Security) + + if (remember === 'yes') { + // Add Max-Age = 300 seconds (5 minutes) + cookieSettings += ' Max-Age=300;'; + } + // If not remembered, no Max-Age = Session Cookie (Expires when browser closes) + + res.setHeader('Set-Cookie', `auth_session=${SESSION_SECRET}; ${cookieSettings}`); + return res.redirect('/'); + } + + res.status(401).send('Invalid credentials. <a href="/login">Try again</a>'); +}); + +// 4. LOGOUT ACTION +app.get('/logout', (req, res) => { + res.setHeader('Set-Cookie', 'auth_session=; Max-Age=0; Path=/;'); + res.redirect('/login'); +}); + +// --- MIDDLEWARE: FORM AUTH --- const checkAuth = (req, res, next) => { if (!AUTH_USER || !AUTH_PASS) return next(); - const b64auth = (req.headers.authorization || '').split(' ')[1] || ''; - const [login, password] = Buffer.from(b64auth, 'base64').toString().split(':'); - - if (login && password && login === AUTH_USER && password === AUTH_PASS) { + const cookies = parseCookies(req); + // Check if the cookie exists and matches our secret + if (cookies.auth_session === SESSION_SECRET) { return next(); } - res.set('WWW-Authenticate', 'Basic realm="401"'); - res.status(401).send('Authentication required to access WhatsApp Logger.'); + // If API/Asset request, send 401, else redirect to login + if (req.path.startsWith('/api')) { + res.status(401).send('Unauthorized'); + } else { + res.redirect('/login'); + } }; app.use(checkAuth); -// 2. PROTECTED ROUTE: Serve Static Frontend +// 5. PROTECTED ROUTE: Serve Static Frontend app.use(express.static(path.join(__dirname, 'public'))); -// 3. PROTECTED ROUTE: Main Page (QR Code or Status) +// 6. PROTECTED ROUTE: Main Page (QR Code or Status) app.get('/', async (req, res) => { + const logoutBtn = `<a href="/logout" style="position: absolute; top: 10px; right: 10px; padding: 10px; background: #ff4444; color: white; text-decoration: none; border-radius: 4px; font-size: 14px;">Logout</a>`; + if (isConnected) { return res.send(` <html> <body style="font-family: sans-serif; text-align: center; padding-top: 50px;"> + ${logoutBtn} <h2 style="color: green;">System Operational</h2> <p>Connected to WhatsApp.</p> </body> @@ -185,6 +253,7 @@ app.get('/', async (req, res) => { <html> <head><meta http-equiv="refresh" content="5"></head> <body style="font-family: sans-serif; text-align: center; padding-top: 50px;"> + ${logoutBtn} <h2>Scan to Link</h2> <img src="${qrImage}" alt="QR Code" /> <p>Refreshes every 5 seconds...</p> @@ -208,4 +277,10 @@ app.get('/', async (req, res) => { app.listen(PORT, () => { startWhatsApp(); console.log(`Server running on port ${PORT}`); + + if (AUTH_USER && AUTH_PASS) { + console.log("Security: Form Authentication is ENABLED."); + } else { + console.log("Security: Form Authentication is DISABLED (Env vars missing)."); + } });
© notamitgamer • Site Built: 2026-07-21 13:58:23 UTC • git-mirror commit: 1037f62 [view raw info]
Originally created with stagit • modified by notamitgamer
Forked from github.com/notamitgamer/git-mirror