- FCMService now supports both HTTP v1 API (service account) and legacy API (server key) - v1 API uses OAuth2 JWT auth with Firebase service account; tries this first - Falls back to legacy HTTP API if only server key is configured - Removes invalid tokens from DB automatically (NotRegistered, INVALID_ARGUMENT, etc.) - AdminController now logs push result status and shows in UI response - .env updated with FCM_SERVER_KEY placeholder and docs - Migration 010 run in dev
310 lines
9.9 KiB
PHP
Executable File
310 lines
9.9 KiB
PHP
Executable File
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace ServerManager\Controllers;
|
|
|
|
use ServerManager\Core\App;
|
|
use ServerManager\Core\Database;
|
|
use ServerManager\Core\Session;
|
|
use ServerManager\Core\Validator;
|
|
use ServerManager\Models\Notification;
|
|
use ServerManager\Models\User;
|
|
use ServerManager\Services\AuditService;
|
|
use ServerManager\Services\FCMService;
|
|
|
|
class AdminController
|
|
{
|
|
private \ServerManager\Core\View $view;
|
|
private User $userModel;
|
|
private AuditService $auditService;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->view = App::getInstance()->getView();
|
|
$this->userModel = new User();
|
|
$this->auditService = new AuditService();
|
|
}
|
|
|
|
private function requireSuperAdmin(): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
Session::setFlash('error', 'You do not have permission to access this page.');
|
|
$this->view->redirect('/dashboard');
|
|
}
|
|
}
|
|
|
|
public function users(): void
|
|
{
|
|
$this->requireSuperAdmin();
|
|
|
|
$page = (int) ($_GET['page'] ?? 1);
|
|
$users = $this->userModel->getAll($page, 20);
|
|
|
|
$this->view->display('admin.users', [
|
|
'title' => 'User Management - ServerManager',
|
|
'users' => $users,
|
|
]);
|
|
}
|
|
|
|
public function createUser(): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
|
}
|
|
|
|
$validator = new Validator();
|
|
$rules = [
|
|
'username' => 'required|min:3|max:50|alphaNum',
|
|
'email' => 'required|email',
|
|
'password' => 'required|min:8',
|
|
'role' => 'required|in:super_admin,admin,operator',
|
|
];
|
|
|
|
if (!$validator->validate($_POST, $rules)) {
|
|
$this->view->json(['success' => false, 'message' => $validator->getFirstError()]);
|
|
}
|
|
|
|
$existingUser = $this->userModel->findByUsername($_POST['username']);
|
|
if ($existingUser) {
|
|
$this->view->json(['success' => false, 'message' => 'Username already exists.']);
|
|
}
|
|
|
|
$existingEmail = $this->userModel->findByEmail($_POST['email']);
|
|
if ($existingEmail) {
|
|
$this->view->json(['success' => false, 'message' => 'Email already exists.']);
|
|
}
|
|
|
|
$id = $this->userModel->create([
|
|
'username' => $_POST['username'],
|
|
'email' => $_POST['email'],
|
|
'password' => $_POST['password'],
|
|
'role' => $_POST['role'] ?? 'admin',
|
|
'is_active' => 1,
|
|
]);
|
|
|
|
$this->auditService->logUserManagement($id, 'user_created');
|
|
|
|
$this->view->json([
|
|
'success' => true,
|
|
'message' => 'User created successfully.',
|
|
'user_id' => $id,
|
|
]);
|
|
}
|
|
|
|
public function updateUser(int $id): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
|
}
|
|
|
|
$user = $this->userModel->findById($id);
|
|
|
|
if (!$user) {
|
|
$this->view->json(['success' => false, 'message' => 'User not found'], 404);
|
|
}
|
|
|
|
$updateData = [];
|
|
|
|
if (!empty($_POST['email'])) {
|
|
$updateData['email'] = $_POST['email'];
|
|
}
|
|
|
|
if (!empty($_POST['role'])) {
|
|
$updateData['role'] = $_POST['role'];
|
|
}
|
|
|
|
if (!empty($_POST['password'])) {
|
|
$updateData['password'] = $_POST['password'];
|
|
}
|
|
|
|
if (isset($_POST['is_active'])) {
|
|
$updateData['is_active'] = (int) $_POST['is_active'];
|
|
}
|
|
|
|
if (empty($updateData)) {
|
|
$this->view->json(['success' => false, 'message' => 'No data to update.']);
|
|
}
|
|
|
|
$this->userModel->update($id, $updateData);
|
|
|
|
$this->auditService->logUserManagement($id, 'user_updated');
|
|
|
|
$this->view->json([
|
|
'success' => true,
|
|
'message' => 'User updated successfully.',
|
|
]);
|
|
}
|
|
|
|
public function deleteUser(int $id): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
$this->view->json(['success' => false, 'message' => 'Forbidden'], 403);
|
|
}
|
|
|
|
$user = $this->userModel->findById($id);
|
|
|
|
if (!$user) {
|
|
$this->view->json(['success' => false, 'message' => 'User not found'], 404);
|
|
}
|
|
|
|
if ($user['id'] === Session::get('user_id')) {
|
|
$this->view->json(['success' => false, 'message' => 'You cannot delete your own account.']);
|
|
}
|
|
|
|
$this->userModel->delete($id);
|
|
|
|
$this->auditService->logUserManagement($id, 'user_deleted');
|
|
|
|
$this->view->json([
|
|
'success' => true,
|
|
'message' => 'User deleted successfully.',
|
|
]);
|
|
}
|
|
|
|
public function auditLogs(): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
Session::setFlash('error', 'You do not have permission to access this page.');
|
|
$this->view->redirect('/dashboard');
|
|
}
|
|
|
|
$page = (int) ($_GET['page'] ?? 1);
|
|
$action = $_GET['action'] ?? '';
|
|
$username = $_GET['username'] ?? '';
|
|
$ipAddress = $_GET['ip_address'] ?? '';
|
|
$entityType = $_GET['entity_type'] ?? '';
|
|
$details = $_GET['details'] ?? '';
|
|
$dateFrom = $_GET['date_from'] ?? '';
|
|
$dateTo = $_GET['date_to'] ?? '';
|
|
|
|
$filters = [];
|
|
if ($action !== '') $filters['action'] = $action;
|
|
if ($username !== '') $filters['username'] = $username;
|
|
if ($ipAddress !== '') $filters['ip_address'] = $ipAddress;
|
|
if ($entityType !== '') $filters['entity_type'] = $entityType;
|
|
if ($details !== '') $filters['details'] = $details;
|
|
if ($dateFrom !== '') $filters['date_from'] = $dateFrom;
|
|
if ($dateTo !== '') $filters['date_to'] = $dateTo;
|
|
|
|
$logs = $this->auditService->getAuditLogs($page, 25, $filters);
|
|
|
|
$this->view->display('admin.audit', [
|
|
'title' => 'Audit Logs - ServerManager',
|
|
'logs' => $logs,
|
|
'action' => $action,
|
|
'username' => $username,
|
|
'ipAddress' => $ipAddress,
|
|
'entityType' => $entityType,
|
|
'details' => $details,
|
|
'dateFrom' => $dateFrom,
|
|
'dateTo' => $dateTo,
|
|
]);
|
|
}
|
|
|
|
public function securitySettings(): void
|
|
{
|
|
if (Session::get('user_role') !== 'super_admin') {
|
|
Session::setFlash('error', 'You do not have permission to access this page.');
|
|
$this->view->redirect('/dashboard');
|
|
}
|
|
|
|
$this->view->display('admin.security', [
|
|
'title' => 'Security Settings - ServerManager',
|
|
]);
|
|
}
|
|
|
|
public function appVersion(): void
|
|
{
|
|
$this->requireSuperAdmin();
|
|
|
|
$db = Database::getInstance();
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|
$fields = ['app_version', 'apk_url', 'force_update', 'release_notes'];
|
|
foreach ($fields as $field) {
|
|
if (isset($_POST[$field])) {
|
|
$db->update('app_config', ['value' => $_POST[$field]], '`key` = ?', [$field]);
|
|
}
|
|
}
|
|
$this->auditService->log('app_version_updated', 'config', null, ['details' => 'App version config updated']);
|
|
Session::setFlash('success', 'App version updated successfully.');
|
|
$this->view->redirect('/admin/app-version');
|
|
return;
|
|
}
|
|
|
|
$configs = $db->fetchAll('SELECT * FROM app_config');
|
|
$config = [];
|
|
foreach ($configs as $row) {
|
|
$config[$row['key']] = $row['value'];
|
|
}
|
|
|
|
$this->view->display('admin.app_version', [
|
|
'title' => 'App Version - ServerManager',
|
|
'config' => $config,
|
|
]);
|
|
}
|
|
|
|
public function notifications(): void
|
|
{
|
|
$this->requireSuperAdmin();
|
|
|
|
$notificationModel = new Notification();
|
|
$page = (int) ($_GET['page'] ?? 1);
|
|
$allNotifications = $notificationModel->getAll($page, 20);
|
|
|
|
$userModel = new User();
|
|
$users = $userModel->getAll(1, 200);
|
|
|
|
$totalActiveUsers = $notificationModel->getTotalActiveUsers();
|
|
|
|
$this->view->display('admin.notifications', [
|
|
'title' => 'Notifications - ServerManager',
|
|
'notifications' => $allNotifications,
|
|
'users' => $users,
|
|
'totalActiveUsers' => $totalActiveUsers,
|
|
]);
|
|
}
|
|
|
|
public function sendNotification(): void
|
|
{
|
|
$this->requireSuperAdmin();
|
|
|
|
$title = $_POST['title'] ?? '';
|
|
$message = $_POST['message'] ?? '';
|
|
$type = $_POST['type'] ?? 'info';
|
|
$userId = !empty($_POST['user_id']) ? (int) $_POST['user_id'] : null;
|
|
|
|
if (empty($title) || empty($message)) {
|
|
$this->view->json(['success' => false, 'message' => 'Title and message are required.']);
|
|
return;
|
|
}
|
|
|
|
$notificationModel = new Notification();
|
|
$noteId = $notificationModel->create([
|
|
'user_id' => $userId,
|
|
'title' => $title,
|
|
'message' => $message,
|
|
'type' => $type,
|
|
]);
|
|
|
|
$fcm = new FCMService();
|
|
$pushResult = '';
|
|
if ($userId) {
|
|
$sent = $fcm->sendToUser($userId, $title, $message, $type, $noteId);
|
|
$pushResult = $sent ? ' (push sent)' : ($fcm->isConfigured() ? ' (push failed)' : '');
|
|
} else {
|
|
$count = $fcm->sendToAll($title, $message, $type, $noteId);
|
|
$pushResult = $fcm->isConfigured() ? " (push sent to {$count} devices)" : '';
|
|
}
|
|
|
|
$this->auditService->log('notification_sent', 'notification', null, [
|
|
'title' => $title,
|
|
'target' => $userId ? "user #{$userId}" : 'all users',
|
|
'push' => $fcm->isConfigured() ? 'sent' : 'not_configured',
|
|
]);
|
|
|
|
$this->view->json(['success' => true, 'message' => 'Notification sent successfully.' . $pushResult]);
|
|
}
|
|
}
|