diff --git a/app/Http/Controllers/SakaiController.php b/app/Http/Controllers/SakaiController.php index cbfc8b34..073b2b13 100755 --- a/app/Http/Controllers/SakaiController.php +++ b/app/Http/Controllers/SakaiController.php @@ -12,7 +12,8 @@ use App\Services\CountrySpeedService; use App\Services\ServiceFnPoolV2rayTo; use App\Models\FreenodePool as ModelFreenodePool; use App\Services\Cron\ReportTraffic; - +use App\Services\Cron\ReportUmami; +use App\Services\TomTool\Telegram\Slave as TeleSlave; class SakaiController { @@ -26,16 +27,25 @@ class SakaiController public function test1() { - $s = "vmess://eyJ2IjoiMiIsInBzIjoi8J+HuvCfh7hVU18yNHwxMDNLQi9zfEIwMDctMjYwNjE4IiwiYWRkIjoiMTI5LjE0Ni43Ny4yNDgiLCJwb3J0IjoiMzk0OTUiLCJpZCI6ImY4MzZjNzM2LTg3ZmMtNGZkZS1hYWJjLTAwODU3ZWNkZmYzZSIsImFpZCI6IjAiLCJzY3kiOiJhdXRvIiwibmV0Ijoid3MiLCJ0eXBlIjoiIiwidGxzIjoiIiwicGF0aCI6Ii9jY3R2MTMubTN1OCJ9"; +// TeleSlave::warn()->send("⚠️ [Umami 报表失败]: 无法获取 Auth Token,请检查账号密码。"); +// echo 111; +// exit; - $s = explode("://", $s); + $oReportUmami = new ReportUmami(); + $oReportUmami->handle(); - $a = $s[0]; - $b = $s[1]; + exit; - $s = base64_decode($b); - - tt($s); +// $s = "vmess://eyJ2IjoiMiIsInBzIjoi8J+HuvCfh7hVU18yNHwxMDNLQi9zfEIwMDctMjYwNjE4IiwiYWRkIjoiMTI5LjE0Ni43Ny4yNDgiLCJwb3J0IjoiMzk0OTUiLCJpZCI6ImY4MzZjNzM2LTg3ZmMtNGZkZS1hYWJjLTAwODU3ZWNkZmYzZSIsImFpZCI6IjAiLCJzY3kiOiJhdXRvIiwibmV0Ijoid3MiLCJ0eXBlIjoiIiwidGxzIjoiIiwicGF0aCI6Ii9jY3R2MTMubTN1OCJ9"; +// +// $s = explode("://", $s); +// +// $a = $s[0]; +// $b = $s[1]; +// +// $s = base64_decode($b); +// +// tt($s); // $oReportTraffic = new ReportTraffic(); // diff --git a/app/Services/Cron/ReportUmami.php b/app/Services/Cron/ReportUmami.php new file mode 100755 index 00000000..b707f4ec --- /dev/null +++ b/app/Services/Cron/ReportUmami.php @@ -0,0 +1,149 @@ +baseUrl = (string) config('path.url_umami_base'); + } + + /** + * 定时任务入口方法 + */ + public function handle(): void + { + try { + // 1. 获取 API 认证 Token + $token = $this->getAuthToken(); + if (!$token) { + TeleSlave::log()->send("⚠️ [Umami 报表失败]: 无法获取 Auth Token,请检查账号密码。"); + return; + } + + // 2. 获取网站列表 + $websites = $this->getWebsites($token); + if (empty($websites)) { + TeleSlave::log()->send("ℹ️ [Umami 报表]: 数据库中未找到任何网站。"); + return; + } + + // 3. 统计过去 24 小时的数据 + $endAt = microtime(true) * 1000; // 当前时间戳 (毫秒) + $startAt = $endAt - (24 * 3600 * 1000); // 24小时前的时间戳 (毫秒) + + $msg = "📊 Umami 每日站点数据日报\n"; + $msg .= "----------------------------\n"; + + foreach ($websites as $site) { + $siteId = $site['id']; + $siteName = $site['name']; + $domain = $site['domain'] ?? ''; + + // 获取单个网站的统计指标 + $stats = $this->getWebsiteStats($token, $siteId, (int)$startAt, (int)$endAt); + + if ($stats) { + $pageviews = $stats['pageviews']['value'] ?? 0; + $visitors = $stats['visitors']['value'] ?? 0; + $visits = $stats['visits']['value'] ?? 0; + $bounces = $stats['bounces']['value'] ?? 0; + + // 计算跳出率 + $bounceRate = $visits > 0 ? round(($bounces / $visits) * 100, 1) : 0; + + $msg .= "🌐 {$siteName} ({$domain})\n"; + $msg .= "├ 👁️ 页面浏览 (PV): {$pageviews}\n"; + $msg .= "├ 👤 独立访客 (UV): {$visitors}\n"; + $msg .= "├ 🔄 会话次数: {$visits}\n"; + $msg .= "└ 📉 跳出率: {$bounceRate}%\n\n"; + } + } + + // 4. 发送日志至 Telegram + TeleSlave::log()->send($msg); + + } catch (\Throwable $e) { + TeleSlave::log()->send("❌ [Umami 报表异常]: " . $e->getMessage()); + } + } + + /** + * 1. 登录 Umami 获取 JWT Token + */ + private function getAuthToken(): ?string + { + $response = $this->curlRequest('POST', '/api/auth/login', [ + 'username' => $this->username, + 'password' => $this->password, + ]); + + return $response['token'] ?? null; + } + + /** + * 2. 获取网站列表 + */ + private function getWebsites(string $token): array + { + $response = $this->curlRequest('GET', '/api/websites', [], $token); + return $response['data'] ?? $response ?? []; + } + + /** + * 3. 获取指定网站的统计数据 + */ + private function getWebsiteStats(string $token, string $siteId, int $startAt, int $endAt): ?array + { + $endpoint = sprintf('/api/websites/%s/stats?startAt=%d&endAt=%d', $siteId, $startAt, $endAt); + return $this->curlRequest('GET', $endpoint, [], $token); + } + + /** + * 通用 cURL 请求工具方法 + */ + private function curlRequest(string $method, string $path, array $data = [], ?string $token = null): ?array + { organize: + $ch = curl_init($this->baseUrl . $path); + + $headers = [ + 'Content-Type: application/json', + 'Accept: application/json', + ]; + + if ($token) { + $headers[] = 'Authorization: Bearer ' . $token; + } + + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_TIMEOUT, 10); + + if ($method === 'POST') { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); + } + + $result = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode >= 200 && $httpCode < 300 && $result) { + return json_decode($result, true); + } + + return null; + } +} diff --git a/config/path.php b/config/path.php index b9155ef8..71d2d22b 100755 --- a/config/path.php +++ b/config/path.php @@ -12,4 +12,5 @@ return [ 'cvf' => env('url_fn_cvf', 'clashv2rayfree.com'), 'vcf' => env('url_fn_vcf', 'v2rayclashfree.com') ], + 'url_umami_base' => env("url_umami_base"), ];