generated from fahricansecer/boilerplate-be
384 lines
11 KiB
TypeScript
384 lines
11 KiB
TypeScript
import {
|
||
Controller,
|
||
Get,
|
||
Post,
|
||
Put,
|
||
Delete,
|
||
Param,
|
||
Body,
|
||
Query,
|
||
UseInterceptors,
|
||
Inject,
|
||
} from '@nestjs/common';
|
||
import {
|
||
CacheInterceptor,
|
||
CacheKey,
|
||
CacheTTL,
|
||
CACHE_MANAGER,
|
||
} from '@nestjs/cache-manager';
|
||
import * as cacheManager from 'cache-manager';
|
||
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
|
||
import { Roles } from '../../common/decorators';
|
||
import { PrismaService } from '../../database/prisma.service';
|
||
import { PaginationDto } from '../../common/dto/pagination.dto';
|
||
import {
|
||
ApiResponse,
|
||
createSuccessResponse,
|
||
createPaginatedResponse,
|
||
PaginatedData,
|
||
} from '../../common/types/api-response.type';
|
||
import { plainToInstance } from 'class-transformer';
|
||
import { UserResponseDto } from '../users/dto/user.dto';
|
||
import {
|
||
PermissionResponseDto,
|
||
RolePermissionResponseDto,
|
||
RoleResponseDto,
|
||
UserRoleResponseDto,
|
||
} from './dto/admin.dto';
|
||
import { AdminService } from './admin.service';
|
||
|
||
@ApiTags('Admin')
|
||
@ApiBearerAuth()
|
||
@Controller('admin')
|
||
@Roles('admin')
|
||
export class AdminController {
|
||
constructor(
|
||
private readonly prisma: PrismaService,
|
||
private readonly adminService: AdminService,
|
||
@Inject(CACHE_MANAGER) private cacheManager: cacheManager.Cache,
|
||
) {}
|
||
|
||
// ================== System Stats ==================
|
||
|
||
@Get('stats')
|
||
@ApiOperation({ summary: 'Sistem istatistiklerini getir' })
|
||
async getSystemStats(): Promise<ApiResponse<any>> {
|
||
const stats = await this.adminService.getSystemStats();
|
||
return createSuccessResponse(stats);
|
||
}
|
||
|
||
// ================== Plan Management ==================
|
||
|
||
@Get('plans')
|
||
@ApiOperation({ summary: 'Tüm planları getir (admin detay)' })
|
||
async getAllPlans(): Promise<ApiResponse<any>> {
|
||
const plans = await this.adminService.getAllPlans();
|
||
return createSuccessResponse(plans);
|
||
}
|
||
|
||
@Put('plans/:id')
|
||
@ApiOperation({ summary: 'Plan güncelle' })
|
||
async updatePlan(
|
||
@Param('id') id: string,
|
||
@Body() data: any,
|
||
): Promise<ApiResponse<any>> {
|
||
const plan = await this.adminService.updatePlan(id, data);
|
||
return createSuccessResponse(plan, 'Plan güncellendi');
|
||
}
|
||
|
||
// ================== Credit Management ==================
|
||
|
||
@Post('users/:userId/credits')
|
||
@ApiOperation({ summary: 'Kullanıcıya kredi ver' })
|
||
async grantCredits(
|
||
@Param('userId') userId: string,
|
||
@Body() data: { amount: number; description: string },
|
||
): Promise<ApiResponse<any>> {
|
||
const tx = await this.adminService.grantCredits(userId, data.amount, data.description);
|
||
return createSuccessResponse(tx, 'Kredi yüklendi');
|
||
}
|
||
|
||
// ================== User Detail ==================
|
||
|
||
@Get('users/:id/detail')
|
||
@ApiOperation({ summary: 'Kullanıcı detay — abonelik, projeler, krediler' })
|
||
async getUserDetail(
|
||
@Param('id') id: string,
|
||
): Promise<ApiResponse<any>> {
|
||
const user = await this.adminService.getUserDetail(id);
|
||
return createSuccessResponse(user);
|
||
}
|
||
|
||
// ================== Users Management ==================
|
||
|
||
@Get('users')
|
||
@ApiOperation({ summary: 'Get all users (admin)' })
|
||
async getAllUsers(
|
||
@Query() pagination: PaginationDto,
|
||
): Promise<ApiResponse<PaginatedData<UserResponseDto>>> {
|
||
const { skip, take, orderBy } = pagination;
|
||
|
||
const [users, total] = await Promise.all([
|
||
this.prisma.user.findMany({
|
||
skip,
|
||
take,
|
||
orderBy,
|
||
include: {
|
||
roles: {
|
||
include: {
|
||
role: true,
|
||
},
|
||
},
|
||
},
|
||
}),
|
||
this.prisma.user.count(),
|
||
]);
|
||
|
||
const dtos = plainToInstance(
|
||
UserResponseDto,
|
||
users,
|
||
) as unknown as UserResponseDto[];
|
||
|
||
return createPaginatedResponse(
|
||
dtos,
|
||
total,
|
||
pagination.page || 1,
|
||
pagination.limit || 10,
|
||
);
|
||
}
|
||
|
||
@Put('users/:id/toggle-active')
|
||
@ApiOperation({ summary: 'Toggle user active status' })
|
||
async toggleUserActive(
|
||
@Param('id') id: string,
|
||
): Promise<ApiResponse<UserResponseDto>> {
|
||
const user = await this.prisma.user.findUnique({ where: { id } });
|
||
|
||
const updated = await this.prisma.user.update({
|
||
where: { id },
|
||
data: { isActive: !user?.isActive },
|
||
});
|
||
|
||
return createSuccessResponse(
|
||
plainToInstance(UserResponseDto, updated),
|
||
'User status updated',
|
||
);
|
||
}
|
||
|
||
@Post('users/:userId/roles/:roleId')
|
||
@ApiOperation({ summary: 'Assign role to user' })
|
||
async assignRole(
|
||
@Param('userId') userId: string,
|
||
@Param('roleId') roleId: string,
|
||
): Promise<ApiResponse<UserRoleResponseDto>> {
|
||
const userRole = await this.prisma.userRole.create({
|
||
data: { userId, roleId },
|
||
});
|
||
return createSuccessResponse(
|
||
plainToInstance(UserRoleResponseDto, userRole),
|
||
'Role assigned to user',
|
||
);
|
||
}
|
||
|
||
@Delete('users/:userId/roles/:roleId')
|
||
@ApiOperation({ summary: 'Remove role from user' })
|
||
async removeRole(
|
||
@Param('userId') userId: string,
|
||
@Param('roleId') roleId: string,
|
||
): Promise<ApiResponse<null>> {
|
||
await this.prisma.userRole.deleteMany({
|
||
where: { userId, roleId },
|
||
});
|
||
return createSuccessResponse(null, 'Role removed from user');
|
||
}
|
||
|
||
// ================== Roles Management ==================
|
||
|
||
@Get('roles')
|
||
@UseInterceptors(CacheInterceptor)
|
||
@CacheKey('roles_list')
|
||
@CacheTTL(60 * 1000)
|
||
@ApiOperation({ summary: 'Get all roles' })
|
||
async getAllRoles(): Promise<ApiResponse<RoleResponseDto[]>> {
|
||
const roles = await this.prisma.role.findMany({
|
||
include: {
|
||
permissions: {
|
||
include: {
|
||
permission: true,
|
||
},
|
||
},
|
||
_count: {
|
||
select: { users: true },
|
||
},
|
||
},
|
||
});
|
||
|
||
// Transform Prisma structure to DTO structure
|
||
const transformedRoles = roles.map((role) => ({
|
||
...role,
|
||
permissions: role.permissions.map((rp) => rp.permission),
|
||
}));
|
||
|
||
return createSuccessResponse(
|
||
plainToInstance(
|
||
RoleResponseDto,
|
||
transformedRoles,
|
||
) as unknown as RoleResponseDto[],
|
||
);
|
||
}
|
||
|
||
@Post('roles')
|
||
@ApiOperation({ summary: 'Create a new role' })
|
||
async createRole(
|
||
@Body() data: { name: string; description?: string },
|
||
): Promise<ApiResponse<RoleResponseDto>> {
|
||
const role = await this.prisma.role.create({ data });
|
||
await this.cacheManager.del('roles_list');
|
||
return createSuccessResponse(
|
||
plainToInstance(RoleResponseDto, role),
|
||
'Role created',
|
||
201,
|
||
);
|
||
}
|
||
|
||
@Put('roles/:id')
|
||
@ApiOperation({ summary: 'Update a role' })
|
||
async updateRole(
|
||
@Param('id') id: string,
|
||
@Body() data: { name?: string; description?: string },
|
||
): Promise<ApiResponse<RoleResponseDto>> {
|
||
const role = await this.prisma.role.update({ where: { id }, data });
|
||
await this.cacheManager.del('roles_list');
|
||
return createSuccessResponse(
|
||
plainToInstance(RoleResponseDto, role),
|
||
'Role updated',
|
||
);
|
||
}
|
||
|
||
@Delete('roles/:id')
|
||
@ApiOperation({ summary: 'Delete a role' })
|
||
async deleteRole(@Param('id') id: string): Promise<ApiResponse<null>> {
|
||
await this.prisma.role.delete({ where: { id } });
|
||
await this.cacheManager.del('roles_list');
|
||
return createSuccessResponse(null, 'Role deleted');
|
||
}
|
||
|
||
// ================== Permissions Management ==================
|
||
|
||
@Get('permissions')
|
||
@UseInterceptors(CacheInterceptor)
|
||
@CacheKey('permissions_list')
|
||
@CacheTTL(60 * 1000)
|
||
@ApiOperation({ summary: 'Get all permissions' })
|
||
async getAllPermissions(): Promise<ApiResponse<PermissionResponseDto[]>> {
|
||
const permissions = await this.prisma.permission.findMany();
|
||
return createSuccessResponse(
|
||
plainToInstance(
|
||
PermissionResponseDto,
|
||
permissions,
|
||
) as unknown as PermissionResponseDto[],
|
||
);
|
||
}
|
||
|
||
@Post('permissions')
|
||
@ApiOperation({ summary: 'Create a new permission' })
|
||
async createPermission(
|
||
@Body()
|
||
data: {
|
||
name: string;
|
||
description?: string;
|
||
resource: string;
|
||
action: string;
|
||
},
|
||
): Promise<ApiResponse<PermissionResponseDto>> {
|
||
const permission = await this.prisma.permission.create({ data });
|
||
await this.cacheManager.del('permissions_list');
|
||
return createSuccessResponse(
|
||
plainToInstance(PermissionResponseDto, permission),
|
||
'Permission created',
|
||
201,
|
||
);
|
||
}
|
||
|
||
@Post('roles/:roleId/permissions/:permissionId')
|
||
@ApiOperation({ summary: 'Assign permission to role' })
|
||
async assignPermission(
|
||
@Param('roleId') roleId: string,
|
||
@Param('permissionId') permissionId: string,
|
||
): Promise<ApiResponse<RolePermissionResponseDto>> {
|
||
const rolePermission = await this.prisma.rolePermission.create({
|
||
data: { roleId, permissionId },
|
||
});
|
||
// Invalidate roles_list because permissions are nested in roles
|
||
await this.cacheManager.del('roles_list');
|
||
return createSuccessResponse(
|
||
plainToInstance(RolePermissionResponseDto, rolePermission),
|
||
'Permission assigned to role',
|
||
);
|
||
}
|
||
|
||
@Delete('roles/:roleId/permissions/:permissionId')
|
||
@ApiOperation({ summary: 'Remove permission from role' })
|
||
async removePermission(
|
||
@Param('roleId') roleId: string,
|
||
@Param('permissionId') permissionId: string,
|
||
): Promise<ApiResponse<null>> {
|
||
await this.prisma.rolePermission.deleteMany({
|
||
where: { roleId, permissionId },
|
||
});
|
||
await this.cacheManager.del('roles_list');
|
||
return createSuccessResponse(null, 'Permission removed from role');
|
||
}
|
||
|
||
// ================== Project Management (Admin) ==================
|
||
|
||
@Get('projects')
|
||
@ApiOperation({ summary: 'Tüm projeleri getir (admin)' })
|
||
async getAllProjects(
|
||
@Query() query: { page?: number; limit?: number; status?: string; userId?: string },
|
||
): Promise<ApiResponse<any>> {
|
||
const result = await this.adminService.getAllProjects({
|
||
page: query.page ? Number(query.page) : 1,
|
||
limit: query.limit ? Number(query.limit) : 20,
|
||
status: query.status,
|
||
userId: query.userId,
|
||
});
|
||
return createSuccessResponse(result);
|
||
}
|
||
|
||
@Delete('projects/:id')
|
||
@ApiOperation({ summary: 'Projeyi sil (soft delete)' })
|
||
async adminDeleteProject(@Param('id') id: string): Promise<ApiResponse<any>> {
|
||
const result = await this.adminService.adminDeleteProject(id);
|
||
return createSuccessResponse(result, 'Proje silindi');
|
||
}
|
||
|
||
// ================== Render Job Management (Admin) ==================
|
||
|
||
@Get('render-jobs')
|
||
@ApiOperation({ summary: 'Tüm render jobları getir (admin)' })
|
||
async getAllRenderJobs(
|
||
@Query() query: { page?: number; limit?: number; status?: string },
|
||
): Promise<ApiResponse<any>> {
|
||
const result = await this.adminService.getAllRenderJobs({
|
||
page: query.page ? Number(query.page) : 1,
|
||
limit: query.limit ? Number(query.limit) : 20,
|
||
status: query.status,
|
||
});
|
||
return createSuccessResponse(result);
|
||
}
|
||
|
||
// ================== Ban / Activate User ==================
|
||
|
||
@Put('users/:id/ban')
|
||
@ApiOperation({ summary: 'Kullanıcıyı banla' })
|
||
async banUser(@Param('id') id: string): Promise<ApiResponse<any>> {
|
||
const user = await this.adminService.setUserActive(id, false);
|
||
return createSuccessResponse(
|
||
plainToInstance(UserResponseDto, user),
|
||
'Kullanıcı banlandı',
|
||
);
|
||
}
|
||
|
||
@Put('users/:id/activate')
|
||
@ApiOperation({ summary: 'Kullanıcıyı aktif et' })
|
||
async activateUser(@Param('id') id: string): Promise<ApiResponse<any>> {
|
||
const user = await this.adminService.setUserActive(id, true);
|
||
return createSuccessResponse(
|
||
plainToInstance(UserResponseDto, user),
|
||
'Kullanıcı aktif edildi',
|
||
);
|
||
}
|
||
}
|
||
|