fix: admin controller audit — merge duplicate controllers, add Post import, validate params
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 44s
All checks were successful
Deploy API Server / build-and-deploy (push) Successful in 44s
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
4414f9cc55
commit
1442b9b69e
@ -1,10 +1,12 @@
|
|||||||
import { Controller, Get, Param, Query } from '@nestjs/common';
|
import { Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/common';
|
||||||
import { PrismaService } from '../../infrastructure/database/prisma.service';
|
import { PrismaService } from '../../infrastructure/database/prisma.service';
|
||||||
|
|
||||||
@Controller('admin/learning')
|
@Controller('admin/learning')
|
||||||
export class AdminReadingController {
|
export class AdminReadingController {
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
// ── Dashboard ──
|
||||||
|
|
||||||
@Get('dashboard')
|
@Get('dashboard')
|
||||||
async getDashboard() {
|
async getDashboard() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
@ -35,7 +37,6 @@ export class AdminReadingController {
|
|||||||
materials: { totalRead, totalMarkedRead },
|
materials: { totalRead, totalMarkedRead },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ── ReadingEvent ──
|
// ── ReadingEvent ──
|
||||||
|
|
||||||
@ -169,16 +170,12 @@ export class AdminReadingController {
|
|||||||
async getRecord(@Param('id') id: string) {
|
async getRecord(@Param('id') id: string) {
|
||||||
return this.prisma.learningRecord.findUnique({ where: { id } });
|
return this.prisma.learningRecord.findUnique({ where: { id } });
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ── Diagnosis ──
|
// ── Diagnosis ──
|
||||||
|
|
||||||
@Controller('admin/learning')
|
|
||||||
export class AdminDiagnosticController {
|
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
|
||||||
|
|
||||||
@Get('user-timeline')
|
@Get('user-timeline')
|
||||||
async userTimeline(@Query('userId') userId: string) {
|
async userTimeline(@Query('userId') userId?: string) {
|
||||||
|
if (!userId) return { error: 'userId is required' };
|
||||||
const events = await this.prisma.readingEvent.findMany({
|
const events = await this.prisma.readingEvent.findMany({
|
||||||
where: { userId }, orderBy: { clientTimestampMs: 'asc' }, take: 200,
|
where: { userId }, orderBy: { clientTimestampMs: 'asc' }, take: 200,
|
||||||
select: { eventType: true, materialId: true, clientTimestampMs: true, activeSecondsDelta: true },
|
select: { eventType: true, materialId: true, clientTimestampMs: true, activeSecondsDelta: true },
|
||||||
@ -187,7 +184,8 @@ export class AdminDiagnosticController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('user-diagnose')
|
@Get('user-diagnose')
|
||||||
async userDiagnose(@Query('userId') userId: string) {
|
async userDiagnose(@Query('userId') userId?: string) {
|
||||||
|
if (!userId) return { error: 'userId is required' };
|
||||||
const [sessions, progressList, dailyActivities, records] = await Promise.all([
|
const [sessions, progressList, dailyActivities, records] = await Promise.all([
|
||||||
this.prisma.learningSession.findMany({ where: { userId }, orderBy: { startedAt: 'desc' }, take: 50 }),
|
this.prisma.learningSession.findMany({ where: { userId }, orderBy: { startedAt: 'desc' }, take: 50 }),
|
||||||
this.prisma.materialReadingProgress.findMany({ where: { userId } }),
|
this.prisma.materialReadingProgress.findMany({ where: { userId } }),
|
||||||
@ -198,7 +196,8 @@ export class AdminDiagnosticController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Get('material-diagnose')
|
@Get('material-diagnose')
|
||||||
async materialDiagnose(@Query('materialId') materialId: string) {
|
async materialDiagnose(@Query('materialId') materialId?: string) {
|
||||||
|
if (!materialId) return { error: 'materialId is required' };
|
||||||
const [events, progress] = await Promise.all([
|
const [events, progress] = await Promise.all([
|
||||||
this.prisma.readingEvent.findMany({ where: { materialId }, orderBy: { clientTimestampMs: 'desc' }, take: 100 }),
|
this.prisma.readingEvent.findMany({ where: { materialId }, orderBy: { clientTimestampMs: 'desc' }, take: 100 }),
|
||||||
this.prisma.materialReadingProgress.findMany({ where: { materialId } }),
|
this.prisma.materialReadingProgress.findMany({ where: { materialId } }),
|
||||||
@ -230,13 +229,8 @@ export class AdminDiagnosticController {
|
|||||||
]);
|
]);
|
||||||
return { items, total, page, limit };
|
return { items, total, page, limit };
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ── Operations ──
|
// ── Operations ──
|
||||||
|
|
||||||
@Controller('admin/learning')
|
|
||||||
export class AdminOperationsController {
|
|
||||||
constructor(private readonly prisma: PrismaService) {}
|
|
||||||
|
|
||||||
@Post('recalculate')
|
@Post('recalculate')
|
||||||
async recalculate() {
|
async recalculate() {
|
||||||
@ -249,14 +243,17 @@ export class AdminOperationsController {
|
|||||||
if (startDate) where.createdAt = { gte: new Date(startDate) };
|
if (startDate) where.createdAt = { gte: new Date(startDate) };
|
||||||
if (endDate) where.createdAt = { ...where.createdAt, lte: new Date(endDate) };
|
if (endDate) where.createdAt = { ...where.createdAt, lte: new Date(endDate) };
|
||||||
|
|
||||||
let data: any[] = [];
|
const take = 5000;
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'events': data = await this.prisma.readingEvent.findMany({ where, take: 10000 }); break;
|
case 'events': {
|
||||||
case 'sessions': data = await this.prisma.learningSession.findMany({ where, take: 10000 }); break;
|
const data = await this.prisma.readingEvent.findMany({ where, take, orderBy: { createdAt: 'desc' } });
|
||||||
|
return { type, count: data.length, data };
|
||||||
|
}
|
||||||
|
case 'sessions': {
|
||||||
|
const data = await this.prisma.learningSession.findMany({ where, take, orderBy: { startedAt: 'desc' } });
|
||||||
|
return { type, count: data.length, data };
|
||||||
|
}
|
||||||
default: return { error: 'Invalid export type. Use: events, sessions' };
|
default: return { error: 'Invalid export type. Use: events, sessions' };
|
||||||
}
|
}
|
||||||
return { type, count: data.length, data };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EOF
|
|
||||||
echo "Admin diagnostic + operations endpoints added"
|
|
||||||
Loading…
x
Reference in New Issue
Block a user