update README
This commit is contained in:
@@ -2,6 +2,17 @@ import { Request, Response } from 'express';
|
||||
import { prisma } from '../../prisma/client';
|
||||
import { createNotification } from '../notification/notification.controller';
|
||||
|
||||
function toNumeric(value: unknown): number {
|
||||
if (typeof value === 'bigint') {
|
||||
return Number(value);
|
||||
}
|
||||
if (typeof value === 'number') {
|
||||
return value;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
return Number.isFinite(parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware для проверки прав администратора
|
||||
*/
|
||||
@@ -274,7 +285,8 @@ export class AdminController {
|
||||
totalBalance,
|
||||
pendingChecks,
|
||||
openTickets,
|
||||
bucketsAggregates
|
||||
bucketsAggregates,
|
||||
bucketStatusCounts
|
||||
] = await Promise.all([
|
||||
prisma.user.count(),
|
||||
prisma.storageBucket.count(),
|
||||
@@ -288,9 +300,25 @@ export class AdminController {
|
||||
objectCount: true,
|
||||
quotaGb: true
|
||||
}
|
||||
}),
|
||||
prisma.storageBucket.groupBy({
|
||||
by: ['status'],
|
||||
_count: { _all: true }
|
||||
})
|
||||
]);
|
||||
|
||||
const statusMap = bucketStatusCounts.reduce<Record<string, number>>((acc, item) => {
|
||||
acc[item.status] = item._count._all;
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const servers = {
|
||||
total: totalBuckets,
|
||||
active: statusMap['active'] ?? 0,
|
||||
suspended: statusMap['suspended'] ?? 0,
|
||||
grace: statusMap['grace'] ?? 0
|
||||
};
|
||||
|
||||
// Получаем последние транзакции
|
||||
const recentTransactions = await prisma.transaction.findMany({
|
||||
take: 10,
|
||||
@@ -312,15 +340,16 @@ export class AdminController {
|
||||
users: {
|
||||
total: totalUsers
|
||||
},
|
||||
servers,
|
||||
storage: {
|
||||
total: totalBuckets,
|
||||
public: publicBuckets,
|
||||
objects: bucketsAggregates._sum.objectCount ?? 0,
|
||||
usedBytes: bucketsAggregates._sum.usedBytes ?? 0,
|
||||
quotaGb: bucketsAggregates._sum.quotaGb ?? 0
|
||||
objects: toNumeric(bucketsAggregates._sum.objectCount ?? 0),
|
||||
usedBytes: toNumeric(bucketsAggregates._sum.usedBytes ?? 0),
|
||||
quotaGb: toNumeric(bucketsAggregates._sum.quotaGb ?? 0)
|
||||
},
|
||||
balance: {
|
||||
total: totalBalance._sum.balance || 0
|
||||
total: toNumeric(totalBalance._sum.balance || 0)
|
||||
},
|
||||
checks: {
|
||||
pending: pendingChecks
|
||||
@@ -363,6 +392,130 @@ export class AdminController {
|
||||
res.status(500).json({ message: 'Ошибка обновления прав' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить пользователя вместе со связанными данными
|
||||
*/
|
||||
async deleteUser(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = Number.parseInt(req.params.userId, 10);
|
||||
if (Number.isNaN(userId)) {
|
||||
return res.status(400).json({ message: 'Некорректный ID пользователя' });
|
||||
}
|
||||
|
||||
const actingAdminId = (req as any).user?.id;
|
||||
if (actingAdminId === userId) {
|
||||
return res.status(400).json({ message: 'Нельзя удалить свой собственный аккаунт.' });
|
||||
}
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
select: { id: true, username: true, email: true }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ message: 'Пользователь не найден' });
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.ticket.updateMany({
|
||||
where: { assignedTo: userId },
|
||||
data: { assignedTo: null }
|
||||
});
|
||||
|
||||
await tx.response.deleteMany({ where: { operatorId: userId } });
|
||||
|
||||
await tx.storageBucket.deleteMany({ where: { userId } });
|
||||
await tx.plan.deleteMany({ where: { userId } });
|
||||
|
||||
await tx.ticket.deleteMany({ where: { userId } });
|
||||
await tx.check.deleteMany({ where: { userId } });
|
||||
await tx.transaction.deleteMany({ where: { userId } });
|
||||
await tx.post.deleteMany({ where: { authorId: userId } });
|
||||
await tx.comment.deleteMany({ where: { userId } });
|
||||
await tx.session.deleteMany({ where: { userId } });
|
||||
await tx.loginHistory.deleteMany({ where: { userId } });
|
||||
await tx.aPIKey.deleteMany({ where: { userId } });
|
||||
await tx.notification.deleteMany({ where: { userId } });
|
||||
await tx.pushSubscription.deleteMany({ where: { userId } });
|
||||
await tx.notificationSettings.deleteMany({ where: { userId } });
|
||||
await tx.userProfile.deleteMany({ where: { userId } });
|
||||
await tx.qrLoginRequest.deleteMany({ where: { userId } });
|
||||
|
||||
await tx.user.delete({ where: { id: userId } });
|
||||
});
|
||||
|
||||
res.json({
|
||||
status: 'success',
|
||||
message: `Пользователь ${user.username} удалён.`
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Ошибка удаления пользователя администратором:', error);
|
||||
res.status(500).json({ message: 'Не удалось удалить пользователя' });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Тест push-уведомления
|
||||
*/
|
||||
async testPushNotification(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = (req as any).user?.id;
|
||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'Пользователь не найден' });
|
||||
}
|
||||
|
||||
console.log(`[Admin] Тест push-уведомления инициирован администратором ${user.username}`);
|
||||
|
||||
// Имитируем задержку отправки
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Push-уведомление успешно отправлено',
|
||||
admin: user.username,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Admin] Ошибка при тестировании push-уведомления:', error);
|
||||
const message = error instanceof Error ? error.message : 'Неизвестная ошибка';
|
||||
return res.status(500).json({ error: `Ошибка при тестировании: ${message}` });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Тест email-уведомления
|
||||
*/
|
||||
async testEmailNotification(req: Request, res: Response) {
|
||||
try {
|
||||
const userId = (req as any).user?.id;
|
||||
const user = await prisma.user.findUnique({ where: { id: userId } });
|
||||
|
||||
if (!user) {
|
||||
return res.status(404).json({ error: 'Пользователь не найден' });
|
||||
}
|
||||
|
||||
console.log(`[Admin] Тест email-уведомления инициирован администратором ${user.username}`);
|
||||
console.log(`[Admin] Email для теста: ${user.email}`);
|
||||
|
||||
// Имитируем задержку отправки
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
message: 'Email-уведомление успешно отправлено',
|
||||
admin: user.username,
|
||||
email: user.email,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('[Admin] Ошибка при тестировании email-уведомления:', error);
|
||||
const message = error instanceof Error ? error.message : 'Неизвестная ошибка';
|
||||
return res.status(500).json({ error: `Ошибка при тестировании: ${message}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new AdminController();
|
||||
|
||||
@@ -4,8 +4,14 @@ import { authMiddleware } from '../auth/auth.middleware';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Все маршруты требуют JWT аутентификации и прав администратора
|
||||
// Все маршруты требуют JWT аутентификации
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Тестирование уведомлений - не требует requireAdmin, проверка внутри
|
||||
router.post('/test/push-notification', adminController.testPushNotification.bind(adminController));
|
||||
router.post('/test/email-notification', adminController.testEmailNotification.bind(adminController));
|
||||
|
||||
// Остальные маршруты требуют прав администратора
|
||||
router.use(requireAdmin);
|
||||
|
||||
// Статистика
|
||||
@@ -17,6 +23,7 @@ router.get('/users/:userId', adminController.getUserDetails.bind(adminController
|
||||
router.post('/users/:userId/balance/add', adminController.addBalance.bind(adminController));
|
||||
router.post('/users/:userId/balance/withdraw', adminController.withdrawBalance.bind(adminController));
|
||||
router.patch('/users/:userId/role', adminController.updateUserRole.bind(adminController));
|
||||
router.delete('/users/:userId', adminController.deleteUser.bind(adminController));
|
||||
|
||||
// Управление S3 бакетами
|
||||
router.delete('/buckets/:bucketId', adminController.deleteBucket.bind(adminController));
|
||||
|
||||
Reference in New Issue
Block a user