Files
FendxPHP/app/Controller/AdminController.php
Lawson 2782d765fb feat(database): 添加用户角色权限系统及相关监控功能
- 创建用户表(users)包含基本信息和认证字段
- 创建角色表(roles)用于权限控制
- 创建权限表(permissions)定义系统权限
- 创建用户角色关联表(user_roles)建立用户与角色关系
- 创建角色权限关联表(role_permissions)建立角色与权限关系
- 创建迁移记录表(migrations)追踪数据库变更
- 添加AdminController提供管理员面板功能
- 实现系统监控、配置管理、缓存清理等功能
- 添加AOP切面编程支持的各种通知类型
- 实现告警管理AlertManager支持多渠道告警
- 添加文档注解接口规范
2026-04-08 17:00:28 +08:00

462 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Controller;
use Fendx\Core\Annotation\Controller;
use Fendx\Web\Annotation\GetRoute;
use Fendx\Web\Annotation\PostRoute;
use Fendx\Web\Request\Request;
use Fendx\Web\Response\Response;
use Fendx\Monitor\Service\MonitorService;
#[Controller('/admin')]
class AdminController
{
#[GetRoute('/dashboard')]
public function dashboard(): array
{
// 检查管理员权限
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
// 获取仪表盘数据
$health = MonitorService::getHealthStatus();
$metrics = MonitorService::getMetricsSummary();
$alerts = MonitorService::getActiveAlerts();
$errors = MonitorService::getErrors([], 10);
$dashboard = [
'overview' => [
'status' => $health['status'],
'uptime' => $metrics['system']['uptime'] ?? 0,
'memory_usage' => $metrics['system']['memory_usage'] ?? 0,
'cpu_usage' => $metrics['system']['cpu_usage'] ?? 0,
'active_alerts' => count($alerts)
],
'health' => $health,
'metrics' => $metrics,
'alerts' => array_slice($alerts, 0, 5),
'recent_errors' => array_slice($errors, 0, 10),
'timestamp' => microtime(true)
];
return Response::success($dashboard);
}
#[GetRoute('/system/info')]
public function systemInfo(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
$info = [
'php_version' => PHP_VERSION,
'php_sapi' => PHP_SAPI,
'os' => PHP_OS,
'server' => $_SERVER['SERVER_SOFTWARE'] ?? 'Unknown',
'memory_limit' => ini_get('memory_limit'),
'max_execution_time' => ini_get('max_execution_time'),
'upload_max_filesize' => ini_get('upload_max_filesize'),
'post_max_size' => ini_get('post_max_size'),
'timezone' => date_default_timezone_get(),
'loaded_extensions' => get_loaded_extensions(),
'process_id' => getmypid(),
'hostname' => gethostname() ?? 'unknown'
];
return Response::success($info);
}
#[GetRoute('/system/status')]
public function systemStatus(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
$status = [
'timestamp' => microtime(true),
'uptime' => $this->getUptime(),
'memory' => [
'usage' => memory_get_usage(true),
'peak' => memory_get_peak_usage(true),
'limit' => $this->parseMemoryLimit(ini_get('memory_limit')),
'usage_percent' => $this->getMemoryUsagePercent()
],
'cpu' => [
'load_average' => function_exists('sys_getloadavg') ? sys_getloadavg() : null,
'usage' => $this->getCpuUsage()
],
'disk' => $this->getDiskUsage(),
'network' => $this->getNetworkInfo(),
'processes' => $this->getProcessInfo()
];
return Response::success($status);
}
#[GetRoute('/config')]
public function getConfig(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
// 获取监控配置(隐藏敏感信息)
$config = include dirname(__DIR__, 2) . '/config/config.php';
$safeConfig = [
'monitor' => $config['monitor'] ?? [],
'database' => [
'driver' => $config['database']['driver'] ?? 'unknown',
'host' => $config['database']['host'] ?? 'unknown',
'database' => $config['database']['database'] ?? 'unknown'
],
'cache' => [
'driver' => $config['cache']['driver'] ?? 'unknown'
]
];
return Response::success($safeConfig);
}
#[PostRoute('/config')]
public function updateConfig(Request $request): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
$data = $request->all();
// 验证配置数据
$validation = $this->validateConfig($data);
if (!$validation['valid']) {
return Response::error(400, 'Invalid config: ' . implode(', ', $validation['errors']));
}
// 这里应该更新配置文件
// 为了安全,实际项目中应该有更严格的配置管理
return Response::success(null, 'Configuration updated successfully');
}
#[PostRoute('/cache/clear')]
public function clearCache(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
try {
// 清理应用缓存
$cacheDir = dirname(__DIR__, 2) . '/runtime/cache';
if (is_dir($cacheDir)) {
$this->clearDirectory($cacheDir);
}
// 清理模板缓存
$templateDir = dirname(__DIR__, 2) . '/runtime/templates';
if (is_dir($templateDir)) {
$this->clearDirectory($templateDir);
}
return Response::success(null, 'Cache cleared successfully');
} catch (\Exception $e) {
return Response::error(500, 'Failed to clear cache: ' . $e->getMessage());
}
}
#[PostRoute('/logs/clear')]
public function clearLogs(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
try {
$logDir = dirname(__DIR__, 2) . '/runtime/logs';
if (is_dir($logDir)) {
$this->clearDirectory($logDir, ['.gitkeep']);
}
return Response::success(null, 'Logs cleared successfully');
} catch (\Exception $e) {
return Response::error(500, 'Failed to clear logs: ' . $e->getMessage());
}
}
#[GetRoute('/users')]
public function getUsers(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
// 这里应该从数据库获取用户列表
// 暂时返回模拟数据
$users = [
['id' => 1, 'username' => 'admin', 'email' => 'admin@example.com', 'role' => 'admin', 'status' => 'active', 'created_at' => '2024-01-01 00:00:00'],
['id' => 2, 'username' => 'user1', 'email' => 'user1@example.com', 'role' => 'user', 'status' => 'active', 'created_at' => '2024-01-02 00:00:00']
];
return Response::success($users);
}
#[PostRoute('/users/{id}/ban')]
public function banUser(int $id): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
// 这里应该更新数据库中的用户状态
return Response::success(null, 'User banned successfully');
}
#[PostRoute('/users/{id}/unban')]
public function unbanUser(int $id): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
// 这里应该更新数据库中的用户状态
return Response::success(null, 'User unbanned successfully');
}
#[GetRoute('/permissions')]
public function getPermissions(): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
$permissions = [
'dashboard' => ['view'],
'monitor' => ['view', 'manage'],
'logs' => ['view', 'search', 'export'],
'alerts' => ['view', 'acknowledge', 'resolve'],
'users' => ['view', 'manage'],
'config' => ['view', 'edit'],
'system' => ['view', 'cache_clear', 'logs_clear']
];
return Response::success($permissions);
}
#[GetRoute('/audit')]
public function getAuditLogs(Request $request): array
{
if (!$this->checkAdminPermission()) {
return Response::error(403, 'Permission denied');
}
$limit = (int)($request->get('limit', 50));
$offset = (int)($request->get('offset', 0));
$userId = $request->get('user_id');
$action = $request->get('action');
// 这里应该从数据库获取审计日志
// 暂时返回模拟数据
$logs = [
[
'id' => 1,
'user_id' => 1,
'username' => 'admin',
'action' => 'login',
'resource' => 'admin',
'ip' => '127.0.0.1',
'user_agent' => 'Mozilla/5.0...',
'created_at' => '2024-01-01 12:00:00'
],
[
'id' => 2,
'user_id' => 1,
'username' => 'admin',
'action' => 'config_update',
'resource' => 'monitor',
'ip' => '127.0.0.1',
'user_agent' => 'Mozilla/5.0...',
'created_at' => '2024-01-01 12:30:00'
]
];
return Response::success([
'logs' => $logs,
'total' => count($logs),
'limit' => $limit,
'offset' => $offset
]);
}
private function checkAdminPermission(): bool
{
// 这里应该实现实际的权限检查
// 可以检查用户角色、session、token等
// 简单示例检查是否有admin session
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
return isset($_SESSION['user_role']) && $_SESSION['user_role'] === 'admin';
}
private function getUptime(): int
{
if (function_exists('sys_getloadavg')) {
$load = sys_getloadavg();
return $load[0] ?? 0;
}
// 尝试从/proc/uptime读取Linux
if (file_exists('/proc/uptime')) {
$uptime = file_get_contents('/proc/uptime');
return (int)explode(' ', $uptime)[0];
}
return 0;
}
private function parseMemoryLimit(string $limit): int
{
if ($limit === '-1') {
return -1;
}
$unit = strtoupper(substr($limit, -1));
$value = (int)substr($limit, 0, -1);
return match ($unit) {
'G' => $value * 1024 * 1024 * 1024,
'M' => $value * 1024 * 1024,
'K' => $value * 1024,
default => (int)$limit
};
}
private function getMemoryUsagePercent(): float
{
$usage = memory_get_usage(true);
$limit = $this->parseMemoryLimit(ini_get('memory_limit'));
if ($limit <= 0) {
return 0;
}
return ($usage / $limit) * 100;
}
private function getCpuUsage(): float
{
// 简单的CPU使用率计算
if (function_exists('sys_getloadavg')) {
$load = sys_getloadavg();
return $load[0] ?? 0;
}
return 0;
}
private function getDiskUsage(): array
{
$paths = [
'root' => dirname(__DIR__, 2),
'runtime' => dirname(__DIR__, 2) . '/runtime'
];
$usage = [];
foreach ($paths as $name => $path) {
if (file_exists($path)) {
$free = disk_free_space($path);
$total = disk_total_space($path);
$used = $total - $free;
$usage[$name] = [
'path' => $path,
'total' => $total,
'used' => $used,
'free' => $free,
'usage_percent' => ($used / $total) * 100
];
}
}
return $usage;
}
private function getNetworkInfo(): array
{
return [
'hostname' => gethostname() ?? 'unknown',
'ip' => $_SERVER['SERVER_ADDR'] ?? '127.0.0.1',
'port' => $_SERVER['SERVER_PORT'] ?? 80,
'scheme' => $_SERVER['REQUEST_SCHEME'] ?? 'http'
];
}
private function getProcessInfo(): array
{
return [
'pid' => getmypid(),
'memory_usage' => memory_get_usage(true),
'memory_peak' => memory_get_peak_usage(true),
'included_files' => count(get_included_files()),
'classes' => count(get_declared_classes()),
'functions' => count(get_defined_functions()['user'])
];
}
private function validateConfig(array $data): array
{
$errors = [];
if (isset($data['sample_rate']) && ($data['sample_rate'] < 0 || $data['sample_rate'] > 1)) {
$errors[] = 'Sample rate must be between 0 and 1';
}
if (isset($data['retention']) && $data['retention'] < 0) {
$errors[] = 'Retention period must be positive';
}
if (isset($data['error_threshold']) && ($data['error_threshold'] < 0 || $data['error_threshold'] > 1)) {
$errors[] = 'Error threshold must be between 0 and 1';
}
if (isset($data['memory_threshold']) && ($data['memory_threshold'] < 0 || $data['memory_threshold'] > 1)) {
$errors[] = 'Memory threshold must be between 0 and 1';
}
return [
'valid' => empty($errors),
'errors' => $errors
];
}
private function clearDirectory(string $dir, array $exclude = []): void
{
if (!is_dir($dir)) {
return;
}
$files = scandir($dir);
foreach ($files as $file) {
if ($file === '.' || $file === '..' || in_array($file, $exclude)) {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $file;
if (is_dir($path)) {
$this->clearDirectory($path);
rmdir($path);
} else {
unlink($path);
}
}
}
}