This commit is contained in:
2026-05-10 10:37:45 +03:00
parent 4f7090e2d9
commit c525b12dfd
32 changed files with 2374 additions and 209 deletions
+62 -30
View File
@@ -10,6 +10,7 @@ import {
UseInterceptors,
Inject,
NotFoundException,
BadRequestException,
} from "@nestjs/common";
import {
CacheInterceptor,
@@ -36,6 +37,8 @@ import {
import { plainToInstance } from "class-transformer";
import { UserResponseDto } from "../users/dto/user.dto";
import { UserRole } from "@prisma/client";
import { SubscriptionsService } from "../subscriptions/subscriptions.service";
import { PlanType } from "../subscriptions/dto/subscription.dto";
@ApiTags("Admin")
@ApiBearerAuth()
@@ -45,6 +48,7 @@ export class AdminController {
constructor(
private readonly prisma: PrismaService,
@Inject(CACHE_MANAGER) private cacheManager: cacheManager.Cache,
private readonly subscriptionsService: SubscriptionsService,
) {}
// ================== Users Management ==================
@@ -122,7 +126,7 @@ export class AdminController {
return createSuccessResponse(
plainToInstance(UserResponseDto, updated),
"User status updated",
"common.SUCCESS_USER_STATUS_UPDATED",
);
}
@@ -140,31 +144,7 @@ export class AdminController {
return createSuccessResponse(
plainToInstance(UserResponseDto, user),
"User role updated",
);
}
@Put("users/:id/subscription")
@ApiOperation({ summary: "Update user subscription" })
@SwaggerResponse({ status: 200, type: UserResponseDto })
async updateUserSubscription(
@Param("id") id: string,
@Body()
data: { subscriptionStatus: string; subscriptionExpiresAt?: string },
): Promise<ApiResponse<UserResponseDto>> {
const user = await this.prisma.user.update({
where: { id },
data: {
subscriptionStatus: data.subscriptionStatus as any,
subscriptionExpiresAt: data.subscriptionExpiresAt
? new Date(data.subscriptionExpiresAt)
: null,
},
});
return createSuccessResponse(
plainToInstance(UserResponseDto, user),
"User subscription updated",
"common.SUCCESS_USER_ROLE_UPDATED",
);
}
@@ -176,7 +156,7 @@ export class AdminController {
where: { id },
data: { deletedAt: new Date() },
});
return createSuccessResponse(null, "User deleted");
return createSuccessResponse(null, "common.SUCCESS_USER_DELETED");
}
// ================== App Settings ==================
@@ -220,7 +200,7 @@ export class AdminController {
await this.cacheManager.del("app_settings");
return createSuccessResponse(
{ key: setting.key, value: setting.value || "" },
"Setting updated",
"common.SUCCESS_SETTING_UPDATED",
);
}
@@ -274,7 +254,57 @@ export class AdminController {
return createSuccessResponse(
{ count: result.count },
"All usage limits reset",
"common.SUCCESS_ALL_LIMITS_RESET",
);
}
@Post("usage-limits/reset/:userId")
@ApiOperation({ summary: "Reset usage limits for a single user" })
@SwaggerResponse({ status: 200 })
async resetUserUsageLimits(
@Param("userId") userId: string,
): Promise<ApiResponse<null>> {
const user = await this.prisma.user.findUnique({ where: { id: userId } });
if (!user) throw new NotFoundException("USER_NOT_FOUND");
await this.prisma.usageLimit.update({
where: { userId },
data: {
analysisCount: 0,
couponCount: 0,
lastResetDate: new Date(),
},
});
return createSuccessResponse(null, "common.SUCCESS_USER_LIMITS_RESET");
}
@Put("users/:userId/subscription")
@ApiOperation({ summary: "Update a user's subscription tier" })
@SwaggerResponse({ status: 200 })
async updateUserSubscription(
@Param("userId") userId: string,
@Body() data: { plan: string },
): Promise<ApiResponse<null>> {
const user = await this.prisma.user.findUnique({ where: { id: userId } });
if (!user) throw new NotFoundException("USER_NOT_FOUND");
const validPlans = [PlanType.FREE, PlanType.PLUS, PlanType.PREMIUM];
const newPlan = data.plan as PlanType;
if (!validPlans.includes(newPlan)) {
throw new BadRequestException("INVALID_PLAN_TYPE");
}
await this.prisma.user.update({
where: { id: userId },
data: { subscriptionStatus: newPlan },
});
await this.subscriptionsService.syncLimitsWithPlan(userId, newPlan);
return createSuccessResponse(
null,
"common.SUCCESS_USER_SUBSCRIPTION_UPDATED",
);
}
@@ -294,7 +324,9 @@ export class AdminController {
] = await Promise.all([
this.prisma.user.count(),
this.prisma.user.count({ where: { isActive: true } }),
this.prisma.user.count({ where: { subscriptionStatus: "active" } }),
this.prisma.user.count({
where: { subscriptionStatus: { in: ["plus", "premium"] } },
}),
this.prisma.match.count(),
this.prisma.prediction.count(),
this.prisma.userCoupon.count(),