mirror of
https://devops.lemonos.cn/lawson/FendxPHP.git
synced 2026-06-15 23:12:49 +08:00
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);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|