Files
coop/backend/src/routes/transactions.ts
T

152 lines
4.0 KiB
TypeScript

import { Router } from "express";
import {
type AuthenticatedRequest,
authenticate,
requireAdmin,
} from "../middleware/auth";
import { asyncHandler } from "../middleware/errorHandler";
import { prisma } from "../utils/prisma";
const router = Router();
router.get(
"/",
authenticate,
asyncHandler(async (req: AuthenticatedRequest, res) => {
const { page = "1", limit = "20", type } = req.query;
const pageNum = parseInt(page as string);
const limitNum = Math.min(parseInt(limit as string), 100);
const skip = (pageNum - 1) * limitNum;
const where: any = { userId: req.user!.id };
if (type) where.type = type;
const [transactions, total] = await Promise.all([
prisma.transaction.findMany({
where,
orderBy: { createdAt: "desc" },
skip,
take: limitNum,
include: {
watchEvent: { select: { duration: true, percentComplete: true } },
request: { select: { mediaType: true, status: true } },
},
}),
prisma.transaction.count({ where }),
]);
res.json({
transactions,
pagination: {
page: pageNum,
limit: limitNum,
total,
totalPages: Math.ceil(total / limitNum),
},
});
}),
);
router.get(
"/stats",
authenticate,
asyncHandler(async (req: AuthenticatedRequest, res) => {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const [totalStats, recentStats, byType] = await Promise.all([
prisma.transaction.groupBy({
by: ["type"],
where: { userId: req.user!.id },
_sum: { amount: true },
_count: { id: true },
}),
prisma.transaction.groupBy({
by: ["type"],
where: { userId: req.user!.id, createdAt: { gte: thirtyDaysAgo } },
_sum: { amount: true },
_count: { id: true },
}),
prisma.transaction.findMany({
where: { userId: req.user!.id },
select: { type: true, amount: true, createdAt: true },
orderBy: { createdAt: "desc" },
take: 100,
}),
]);
const dailyEarnings =
await prisma.$queryRaw`SELECT DATE(created_at) as date, SUM(CASE WHEN type = 'EARN' THEN amount ELSE 0 END) as earned, SUM(CASE WHEN type = 'SPEND' THEN amount ELSE 0 END) as spent FROM transactions WHERE user_id = ${req.user!.id} AND created_at >= ${thirtyDaysAgo} GROUP BY DATE(created_at) ORDER BY date DESC`;
res.json({
total: totalStats,
recent: recentStats,
dailyEarnings,
recentTransactions: byType.slice(0, 10),
});
}),
);
router.get(
"/admin/all",
authenticate,
requireAdmin,
asyncHandler(async (req: AuthenticatedRequest, res) => {
const { page = "1", limit = "50", userId, type } = req.query;
const pageNum = parseInt(page as string);
const limitNum = Math.min(parseInt(limit as string), 100);
const skip = (pageNum - 1) * limitNum;
const where: any = {};
if (userId) where.userId = userId;
if (type) where.type = type;
const [transactions, total] = await Promise.all([
prisma.transaction.findMany({
where,
orderBy: { createdAt: "desc" },
skip,
take: limitNum,
include: { user: { select: { plexUsername: true } } },
}),
prisma.transaction.count({ where }),
]);
res.json({
transactions,
pagination: {
page: pageNum,
limit: limitNum,
total,
totalPages: Math.ceil(total / limitNum),
},
});
}),
);
router.get(
"/admin/stats",
authenticate,
requireAdmin,
asyncHandler(async (_req: AuthenticatedRequest, res) => {
const [total, recent] = await Promise.all([
prisma.transaction.groupBy({
by: ["type"],
_sum: { amount: true },
_count: { id: true },
}),
prisma.transaction.groupBy({
by: ["type"],
where: {
createdAt: { gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) },
},
_sum: { amount: true },
_count: { id: true },
}),
]);
res.json({ total, recent });
}),
);
router.get(
"/recent",
authenticate,
asyncHandler(async (_req: AuthenticatedRequest, res) => {
const transactions = await prisma.transaction.findMany({
orderBy: { createdAt: "desc" },
take: 10,
include: { user: { select: { plexUsername: true } } },
});
res.json({ transactions });
}),
);
export { router as transactionsRouter };