152 lines
4.0 KiB
TypeScript
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 };
|