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"),
];