From d1c0112b4356a26df1ec291efcc88c21065ffcb1 Mon Sep 17 00:00:00 2001 From: wixarm Date: Sun, 1 Feb 2026 10:26:22 +0330 Subject: [PATCH] add: all payments page authentication --- index.js | 165 ++++++++++++++++++++++++++++++++++++++-------- package-lock.json | 39 +++++++++++ package.json | 1 + 3 files changed, 178 insertions(+), 27 deletions(-) diff --git a/index.js b/index.js index 89d8c97..d1c961f 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,10 @@ app.use(express.json()); app.use(cors()); const querystring = require("querystring"); const https = require("https"); +const cookieParser = require("cookie-parser"); +const crypto = require("crypto"); + +app.use(cookieParser()); // const mellat = new mellatCheckout({ // terminalId: "7269507", @@ -59,7 +63,6 @@ const { getAllCities } = require("./lib/getAllCities"); const { getAllProvinces } = require("./lib/getAllProvinces"); const { MongoClient, ObjectId } = require("mongodb"); -// MongoDB for SEP pay requests (use MONGODB_URI env to override) const MONGODB_URI = process.env.MONGODB_URI || "mongodb://root:2pCCFs4wrsLDsO1pjQVA9jORT2WCjLNO5uauS6FUUaGLXCcfjw28IJmAO8RxlEJN@31.7.78.133:14365/?authSource=admin"; @@ -869,7 +872,6 @@ app.post("/sep-pay-request", async (req, res) => { }, ); - // Save to MongoDB before returning try { const coll = await getSepPayCollection(); const token = response.data?.Token ?? response.data?.token ?? null; @@ -961,11 +963,48 @@ app.post("/sepverify", async (req, res) => { } }); -// all-payments/send: send one payment to Taavon (like sepverify) -app.post("/all-payments/send", async (req, res) => { +const ALL_PAYMENTS_USER = "09011110919"; +const ALL_PAYMENTS_PASS = "666666"; +const ALL_PAYMENTS_SECRET = + process.env.ALL_PAYMENTS_SECRET || "rasadyar_all_payments_secret_2026"; +const ALL_PAYMENTS_AUTH_TOKEN = crypto + .createHmac("sha256", ALL_PAYMENTS_SECRET) + .update(ALL_PAYMENTS_USER + ":" + ALL_PAYMENTS_PASS) + .digest("hex"); + +function requireAllPaymentsAuth(req, res, next) { + const token = req.cookies && req.cookies.all_payments_auth; + if (token === ALL_PAYMENTS_AUTH_TOKEN) return next(); + res.status(401).json({ error: "Unauthorized" }); +} + +app.post("/all-payments/login", (req, res) => { + const { username, password } = req.body || {}; + if ( + String(username).trim() === ALL_PAYMENTS_USER && + String(password) === ALL_PAYMENTS_PASS + ) { + res + .cookie("all_payments_auth", ALL_PAYMENTS_AUTH_TOKEN, { + httpOnly: true, + path: "/", + maxAge: 24 * 60 * 60 * 1000, + sameSite: "lax", + }) + .json({ ok: true }); + } else { + res.status(401).json({ error: "نام کاربری یا رمز عبور اشتباه است" }); + } +}); + +app.post("/all-payments/logout", (req, res) => { + res.clearCookie("all_payments_auth", { path: "/" }).json({ ok: true }); +}); + +app.post("/all-payments/send", requireAllPaymentsAuth, async (req, res) => { const { id } = req.body; if (!id) { - return res.status(400).json({ error: "id is required" }); + return res.status(400).json({ error: "شناسه الزامی است" }); } try { const coll = await getSepPayCollection(); @@ -994,8 +1033,7 @@ app.post("/all-payments/send", async (req, res) => { } }); -// all-payments/remove: remove one payment from MongoDB -app.post("/all-payments/remove", async (req, res) => { +app.post("/all-payments/remove", requireAllPaymentsAuth, async (req, res) => { const { id } = req.body; if (!id) { return res.status(400).json({ error: "id is required" }); @@ -1013,23 +1051,25 @@ app.post("/all-payments/remove", async (req, res) => { } }); -// all-payments/remove-all: remove all payments from MongoDB -app.post("/all-payments/remove-all", async (req, res) => { - try { - const coll = await getSepPayCollection(); - const result = await coll.deleteMany({}); - return res.json({ - ok: true, - message: "همه حذف شد", - deletedCount: result.deletedCount, - }); - } catch (err) { - console.error("all-payments remove-all error", err); - return res.status(500).json({ error: err.message }); - } -}); +app.post( + "/all-payments/remove-all", + requireAllPaymentsAuth, + async (req, res) => { + try { + const coll = await getSepPayCollection(); + const result = await coll.deleteMany({}); + return res.json({ + ok: true, + message: "همه حذف شد", + deletedCount: result.deletedCount, + }); + } catch (err) { + console.error("all-payments remove-all error", err); + return res.status(500).json({ error: err.message }); + } + }, +); -// Province code (first 2 digits) -> name for all-payments const PROVINCE_NAMES = { 10: "تست", 18: "همدان", @@ -1037,8 +1077,7 @@ const PROVINCE_NAMES = { 51: "کردستان", }; -// all-payments/data: paginated + filtered list (JSON API) -app.get("/all-payments/data", async (req, res) => { +app.get("/all-payments/data", requireAllPaymentsAuth, async (req, res) => { const page = Math.max(1, parseInt(req.query.page, 10) || 1); const limit = Math.min(100, Math.max(5, parseInt(req.query.limit, 10) || 20)); let dateFrom = (req.query.dateFrom || "").trim(); // YYYY-MM-DD @@ -1054,7 +1093,6 @@ app.get("/all-payments/data", async (req, res) => { const coll = await getSepPayCollection(); const filter = {}; - // Iran timezone UTC+3:30 - treat selected date as full calendar day in Iran const IRAN_OFFSET_MS = 3.5 * 60 * 60 * 1000; if (dateFrom || dateTo) { filter.createdAt = {}; @@ -1115,8 +1153,76 @@ function escapeRegex(s) { return String(s).replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } -// all-payments: modern UI with pagination, date filter, search +const allPaymentsLoginHtml = ` + + + + + ورود - همه پرداخت‌ها + + + + +
+

ورود به بخش پرداخت‌ها

+
+
+ + +
+
+ + +
+ +
+
+
+ + +`; + app.get("/all-payments", async (req, res) => { + const token = req.cookies && req.cookies.all_payments_auth; + if (token !== ALL_PAYMENTS_AUTH_TOKEN) { + res.setHeader("Content-Type", "text/html; charset=utf-8"); + return res.send(allPaymentsLoginHtml); + } const html = ` @@ -1208,6 +1314,7 @@ app.get("/all-payments", async (req, res) => { +
@@ -1414,6 +1521,10 @@ app.get("/all-payments", async (req, res) => { .catch(function(e) { alert(e.message); }) .finally(function() { btn.disabled = false; }); }; + document.getElementById('btn-logout').onclick = function() { + fetch('/all-payments/logout', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: '{}' }) + .then(function() { window.location.href = '/all-payments'; }); + }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { initDatepickers(); load(); }); diff --git a/package-lock.json b/package-lock.json index 75fcafc..6fa9f04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.7.2", "body-parser": "^1.20.2", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "crypto-js": "^4.2.0", "express": "^4.18.2", @@ -284,6 +285,28 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -1719,6 +1742,22 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" }, + "cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==", + "requires": { + "cookie": "0.7.2", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==" + } + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", diff --git a/package.json b/package.json index 74b79ee..63496b8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "license": "ISC", "dependencies": { "axios": "^1.7.2", + "cookie-parser": "^1.4.6", "mongodb": "^6.10.0", "body-parser": "^1.20.2", "cors": "^2.8.5",