mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
- 创建用户表(users)包含基本信息和认证字段 - 创建角色表(roles)用于权限控制 - 创建权限表(permissions)定义系统权限 - 创建用户角色关联表(user_roles)建立用户与角色关系 - 创建角色权限关联表(role_permissions)建立角色与权限关系 - 创建迁移记录表(migrations)追踪数据库变更 - 添加AdminController提供管理员面板功能 - 实现系统监控、配置管理、缓存清理等功能 - 添加AOP切面编程支持的各种通知类型 - 实现告警管理AlertManager支持多渠道告警 - 添加文档注解接口规范
462 lines
14 KiB
PHP
462 lines
14 KiB
PHP
<?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);
|
||
}
|
||
}
|
||
}
|
||
}
|