diff --git a/backend/bots/serverMonitor.js b/backend/bots/serverMonitor.js index 6a1cc92..ba701da 100644 --- a/backend/bots/serverMonitor.js +++ b/backend/bots/serverMonitor.js @@ -249,10 +249,7 @@ const handleCommand = async (message) => { const command = args[0].toLowerCase(); if (command === '/start') { - await sendMessage( - chatId, - 'NakamaHost Moderation\nКоманды:\n• /load — состояние сервера\n• /admins — список админов\n• /addadmin @username — добавить админа (только владельцы)\n• /removeadmin @username — убрать админа (только владельцы)' - ); + await sendMessage(chatId, '✅ Бот активен'); return; } @@ -293,9 +290,7 @@ const handleCommand = async (message) => { return; } - if (command.startsWith('/')) { - await sendMessage(chatId, 'Неизвестная команда. Используйте /start, /load, /admins.'); - } + // Игнорируем неизвестные команды }; const processUpdate = async (update) => { diff --git a/moderation/frontend/src/App.jsx b/moderation/frontend/src/App.jsx index 5636165..733d3df 100644 --- a/moderation/frontend/src/App.jsx +++ b/moderation/frontend/src/App.jsx @@ -10,7 +10,12 @@ import { banPostAuthor, fetchReports, updateReportStatus, - publishToChannel + publishToChannel, + fetchAdmins, + initiateAddAdmin, + confirmAddAdmin, + initiateRemoveAdmin, + confirmRemoveAdmin } from './utils/api'; import { io } from 'socket.io-client'; import { @@ -23,13 +28,17 @@ import { RefreshCw, Trash2, Edit, - Ban + Ban, + UserPlus, + UserMinus, + Crown } from 'lucide-react'; const TABS = [ { id: 'users', title: 'Пользователи', icon: Users }, { id: 'posts', title: 'Посты', icon: ImageIcon }, { id: 'reports', title: 'Репорты', icon: ShieldCheck }, + { id: 'admins', title: 'Админы', icon: Crown }, { id: 'chat', title: 'Чат', icon: MessageSquare }, { id: 'publish', title: 'Публикация', icon: SendHorizontal } ]; @@ -91,6 +100,12 @@ export default function App() { }); const [publishing, setPublishing] = useState(false); + // Admins + const [adminsData, setAdminsData] = useState({ admins: [] }); + const [adminsLoading, setAdminsLoading] = useState(false); + const [adminModal, setAdminModal] = useState(null); // { action: 'add'|'remove', user/admin, adminNumber } + const [confirmCode, setConfirmCode] = useState(''); + // Chat const [chatState, setChatState] = useState(initialChatState); const [chatInput, setChatInput] = useState(''); @@ -150,6 +165,8 @@ export default function App() { loadPosts(); } else if (tab === 'reports') { loadReports(); + } else if (tab === 'admins') { + loadAdmins(); } else if (tab === 'chat' && user) { initChat(); } @@ -200,6 +217,65 @@ export default function App() { } }; + const loadAdmins = async () => { + setAdminsLoading(true); + try { + const data = await fetchAdmins(); + setAdminsData(data); + } catch (err) { + console.error(err); + } finally { + setAdminsLoading(false); + } + }; + + const handleInitiateAddAdmin = async (targetUser, adminNumber) => { + try { + const result = await initiateAddAdmin(targetUser.id, adminNumber); + alert(`Код отправлен ${result.username}. Попросите пользователя ввести код.`); + setAdminModal({ action: 'add', user: targetUser, adminNumber }); + } catch (err) { + alert(err.response?.data?.error || 'Ошибка отправки кода'); + } + }; + + const handleConfirmAddAdmin = async () => { + if (!adminModal || !confirmCode) return; + try { + await confirmAddAdmin(adminModal.user.id, confirmCode); + alert(`Админ ${adminModal.user.username} добавлен!`); + setAdminModal(null); + setConfirmCode(''); + loadAdmins(); + loadUsers(); + } catch (err) { + alert(err.response?.data?.error || 'Ошибка подтверждения'); + } + }; + + const handleInitiateRemoveAdmin = async (admin) => { + try { + const result = await initiateRemoveAdmin(admin.id); + alert(`Код отправлен ${result.username}. Попросите админа ввести код.`); + setAdminModal({ action: 'remove', admin }); + } catch (err) { + alert(err.response?.data?.error || 'Ошибка отправки кода'); + } + }; + + const handleConfirmRemoveAdmin = async () => { + if (!adminModal || !confirmCode) return; + try { + await confirmRemoveAdmin(adminModal.admin.id, confirmCode); + alert(`Админ ${adminModal.admin.username} удалён!`); + setAdminModal(null); + setConfirmCode(''); + loadAdmins(); + } catch (err) { + alert(err.response?.data?.error || 'Ошибка подтверждения'); + } + }; + const initChat = () => { if (!user || chatSocketRef.current) return; const socket = io('/mod-chat', { @@ -370,6 +446,19 @@ export default function App() {
+ {user.username === 'glpshchn00' && !u.isAdmin && ( + + )} {u.banned ? (
); + const renderAdmins = () => ( +
+
+

Админы модерации

+ +
+ + {adminsLoading ? ( +
+ +
+ ) : ( +
+ {adminsData.admins.map((admin) => ( +
+
+
+ @{admin.username} — Номер {admin.adminNumber} +
+
+ {admin.firstName} {admin.lastName || ''} +
+
+ Добавил: {admin.addedBy} + Дата: {formatDate(admin.createdAt)} +
+
+ {user.username === 'glpshchn00' && ( +
+ +
+ )} +
+ ))} + {adminsData.admins.length === 0 && ( +
+ Нет админов +
+ )} +
+ )} + + {/* Модальное окно подтверждения */} + {adminModal && ( +
+
+

+ {adminModal.action === 'add' ? 'Подтверждение добавления админа' : 'Подтверждение удаления админа'} +

+

+ {adminModal.action === 'add' + ? `Код отправлен пользователю @${adminModal.user.username}. Введите код для подтверждения:` + : `Код отправлен админу @${adminModal.admin.username}. Введите код для подтверждения:` + } +

+ setConfirmCode(e.target.value)} + style={{ + width: '100%', + padding: '12px', + fontSize: '16px', + border: '1px solid var(--border)', + borderRadius: '8px', + background: 'var(--background-secondary)', + color: 'var(--text-primary)', + marginBottom: '16px' + }} + maxLength={6} + /> +
+ + +
+
+
+ )} +
+ ); + const renderContent = () => { switch (tab) { case 'users': @@ -607,6 +816,8 @@ export default function App() { return renderPosts(); case 'reports': return renderReports(); + case 'admins': + return renderAdmins(); case 'chat': return renderChat(); case 'publish': diff --git a/moderation/frontend/src/utils/api.js b/moderation/frontend/src/utils/api.js index 93dbd36..9bcf09a 100644 --- a/moderation/frontend/src/utils/api.js +++ b/moderation/frontend/src/utils/api.js @@ -89,5 +89,20 @@ export const publishToChannel = (formData) => } }) +export const fetchAdmins = () => + api.get('/mod-app/admins').then((res) => res.data) + +export const initiateAddAdmin = (userId, adminNumber) => + api.post('/mod-app/admins/initiate-add', { userId, adminNumber }).then((res) => res.data) + +export const confirmAddAdmin = (userId, code) => + api.post('/mod-app/admins/confirm-add', { userId, code }).then((res) => res.data) + +export const initiateRemoveAdmin = (adminId) => + api.post('/mod-app/admins/initiate-remove', { adminId }).then((res) => res.data) + +export const confirmRemoveAdmin = (adminId, code) => + api.post('/mod-app/admins/confirm-remove', { adminId, code }).then((res) => res.data) + export default api