auth-app-api / src /controllers /admin.controller.js
AndreaNotes1's picture
Upload 46 files
4c6b1e8 verified
Raw History Blame Contribute Delete
5.07 kB
const Order = require("../models/Order");
const User = require("../models/User");
const AuditLog = require("../models/AuditLog");
const { sendPaymentRejectedEmail } = require("../utils/email");
const { confirmOrderAndGrantAccess } = require("../utils/orderConfirmation");
const logger = require("../utils/logger");
async function listOrders(req, res, next) {
try {
const { status } = req.query;
const filter = {};
if (status) filter.status = status;
const orders = await Order.find(filter)
.sort({ createdAt: -1 })
.populate("user", "name email")
.populate("course", "title");
res.json({
orders: orders.map((o) => ({
...o.toPublicJSON(),
user: o.user ? { id: o.user._id, name: o.user.name, email: o.user.email } : null,
})),
});
} catch (err) {
next(err);
}
}
/** Admin views the decrypted payment reference only when actively reviewing a specific order. */
async function getOrderDetail(req, res, next) {
try {
const order = await Order.findById(req.params.id)
.select("+paymentReferenceEncrypted +payerNoteEncrypted")
.populate("user", "name email")
.populate("course", "title price");
if (!order) return res.status(404).json({ error: "Order not found." });
await AuditLog.create({
actor: req.user._id,
actorEmail: req.user.email,
action: "admin.order_viewed",
targetType: "Order",
targetId: order._id,
ip: req.ip,
userAgent: req.headers["user-agent"],
});
res.json({
order: {
...order.toPublicJSON({ includeSensitive: true }),
user: order.user ? { id: order.user._id, name: order.user.name, email: order.user.email } : null,
},
});
} catch (err) {
next(err);
}
}
/**
* Confirms a payment: marks the order confirmed, grants course access to
* the user, and emails the customer. Wrapped in a transaction so a
* partial failure can't leave the order confirmed without access
* granted (or vice versa).
*/
async function confirmOrder(req, res, next) {
try {
const existing = await Order.findById(req.params.id);
if (!existing) return res.status(404).json({ error: "Order not found." });
if (existing.status === "confirmed") return res.status(409).json({ error: "Order already confirmed." });
const { order } = await confirmOrderAndGrantAccess({
orderId: req.params.id,
confirmedBy: req.user._id,
ip: req.ip,
userAgent: req.headers["user-agent"],
});
res.json({ order: order.toPublicJSON() });
} catch (err) {
next(err);
}
}
async function rejectOrder(req, res, next) {
try {
const { reason } = req.body;
const order = await Order.findById(req.params.id).populate("user", "name email");
if (!order) return res.status(404).json({ error: "Order not found." });
if (order.status === "confirmed") {
return res.status(409).json({ error: "A confirmed order cannot be rejected. Consider revoking access separately." });
}
order.status = "rejected";
order.rejectionReason = reason || null;
await order.save();
await AuditLog.create({
actor: req.user._id,
actorEmail: req.user.email,
action: "admin.order_rejected",
targetType: "Order",
targetId: order._id,
ip: req.ip,
userAgent: req.headers["user-agent"],
metadata: { reason: reason || null },
});
if (order.user) {
sendPaymentRejectedEmail({
to: order.user.email,
name: order.user.name,
order,
courseTitle: order.courseTitleSnapshot,
reason,
}).catch((err) => logger.error("Payment-rejected email failed", { error: err.message }));
}
res.json({ order: order.toPublicJSON() });
} catch (err) {
next(err);
}
}
async function dashboardStats(req, res, next) {
try {
const [pendingCount, confirmedCount, rejectedCount, totalUsers, revenueAgg] = await Promise.all([
Order.countDocuments({ status: "awaiting_confirmation" }),
Order.countDocuments({ status: "confirmed" }),
Order.countDocuments({ status: "rejected" }),
User.countDocuments({ role: "user" }),
Order.aggregate([{ $match: { status: "confirmed" } }, { $group: { _id: "$currency", total: { $sum: "$amount" } } }]),
]);
res.json({
pendingOrders: pendingCount,
confirmedOrders: confirmedCount,
rejectedOrders: rejectedCount,
totalUsers,
revenueByCurrency: revenueAgg.map((r) => ({ currency: r._id, total: r.total })),
});
} catch (err) {
next(err);
}
}
async function listUsers(req, res, next) {
try {
const users = await User.find({}).sort({ createdAt: -1 }).limit(500);
res.json({ users: users.map((u) => u.toSafeJSON()) });
} catch (err) {
next(err);
}
}
module.exports = { listOrders, getOrderDetail, confirmOrder, rejectOrder, dashboardStats, listUsers };