/* * LOCAL TEST DIP/MNRL LOOKUP SERVICE — NOT A REAL DoT ENDPOINT. * * This stands in for the Department of Telecommunications Digital Intelligence Platform so the * client-onboarding flow can be exercised end to end without a live DoT connection. It invents * its answers. NOTHING HERE IS A REAL DIP OR MNRL CHECK. * * Mirrors scripts/mock-sms-gateway.js in intent: an unconfigured external provider gets a local * stand-in rather than a dangling URL, and every response is stamped so it can never be mistaken * for a genuine DoT result in the audit trail. * * Usage: * node scripts/mock-dip-lookup-service.js # listens on 8090 * MOCK_DIP_PORT=9100 node scripts/mock-dip-lookup-service.js * * Point the backend at it: * FINERACT_MOBILE_LOOKUP_BASE_URL=http://127.0.0.1:8090 * * BEFORE PRODUCTION: point FINERACT_MOBILE_LOOKUP_BASE_URL at the real DIP/MNRL provider and do * not run this. See the banner in application.properties. */ 'use strict'; const http = require('http'); const { URL } = require('url'); const PORT = Number(process.env.MOCK_DIP_PORT || 8090); /** Stamped into every response so a mock lookup is identifiable in the stored raw JSON. */ const PROVIDER = 'MOCK_DIP_LOOKUP_TEST_ONLY'; /* * Fixed verdicts for numbers used in demos and manual testing. Anything not listed here comes * back CLEAN — the common case, so an ordinary test number does not trip the flagged-number gate. * * These overlap deliberately with DEMO_SUSPECTED_NUMBERS in MobileLookupServiceImpl: those are * short-circuited before the HTTP call ever happens, so they are here only to keep the two lists * telling the same story if that short-circuit is ever removed. */ const SUSPECTED = new Set([ '9827123456', '9827456789', '9826234567', '9826987654', '9893123456', '9893765432' ]); const DISCONNECTED = new Set([ '9000000001', '9000000002' ]); const SUSPECTED_AND_DISCONNECTED = new Set([ '9000000003' ]); /** Strips a leading country code so 919827123456 and 9827123456 resolve alike. */ function normalise(raw) { const digits = String(raw || '').replace(/\D/g, ''); if (digits.length > 10 && digits.startsWith('91')) { return digits.slice(2); } return digits; } function suspectedDetail() { return { dipStatus: 'active', reason: 'Reported in cyber-fraud complaints (mock data)', reportedBy: PROVIDER }; } function disconnectedDetail() { return { disconnectionReason: 'Number revoked and listed on MNRL (mock data)', operator: 'MOCK TELECOM' }; } /* * The backend parser reads suspectedDetail/disconnectedDetail unconditionally and only guards * with isJsonNull(), so both keys must always be present — an absent key would NPE there rather * than surface as a clean parse failure. */ function verdictFor(mobileNumber) { const n = normalise(mobileNumber); if (SUSPECTED_AND_DISCONNECTED.has(n)) { return { status: 'SUSPECTED_AND_DISCONNECTED', suspected: true, disconnected: true, suspectedDetail: suspectedDetail(), disconnectedDetail: disconnectedDetail(), message: 'Number is SUSPECTED and DISCONNECTED — found in both DIP and MNRL records' }; } if (SUSPECTED.has(n)) { return { status: 'SUSPECTED', suspected: true, disconnected: false, suspectedDetail: suspectedDetail(), disconnectedDetail: null, message: 'Number is SUSPECTED — found in DIP records' }; } if (DISCONNECTED.has(n)) { return { status: 'DISCONNECTED', suspected: false, disconnected: true, suspectedDetail: null, disconnectedDetail: disconnectedDetail(), message: 'Number is DISCONNECTED — found in MNRL records' }; } return { status: 'CLEAN', suspected: false, disconnected: false, suspectedDetail: null, disconnectedDetail: null, message: 'Number is CLEAN — not found in DIP or MNRL records' }; } const server = http.createServer((req, res) => { const url = new URL(req.url, `http://${req.headers.host}`); if (url.pathname !== '/api/v1/lookup/check') { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, message: 'Not found', provider: PROVIDER })); return; } const mobileNumber = url.searchParams.get('mobileNumber'); if (!mobileNumber || !mobileNumber.trim()) { res.writeHead(400, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ success: false, message: 'mobileNumber is required', provider: PROVIDER })); return; } const verdict = verdictFor(mobileNumber); const body = { success: true, message: verdict.message, timestamp: new Date().toISOString(), provider: PROVIDER, data: { mobileNumber: mobileNumber.trim(), status: verdict.status, suspected: verdict.suspected, disconnected: verdict.disconnected, suspectedDetail: verdict.suspectedDetail, disconnectedDetail: verdict.disconnectedDetail } }; console.log(`[${PROVIDER}] ${mobileNumber} -> ${verdict.status}`); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify(body)); }); server.listen(PORT, '127.0.0.1', () => { console.log(`${PROVIDER} listening on http://127.0.0.1:${PORT}`); console.log('This is NOT a real DoT DIP/MNRL service. Responses are invented.'); console.log(`Set FINERACT_MOBILE_LOOKUP_BASE_URL=http://127.0.0.1:${PORT}`); });