瀏覽代碼

feat: admin system overview

M1Screw 2 年之前
父節點
當前提交
a04b10c7f7

+ 9 - 6
app/routes.php

@@ -189,11 +189,11 @@ return static function (Slim\App $app): void {
         $group->delete('/coupon/{id}', App\Controllers\Admin\CouponController::class . ':delete');
         $group->post('/coupon/{id}/disable', App\Controllers\Admin\CouponController::class . ':disable');
         // 登录日志
-        $group->get('/login', App\Controllers\Admin\IpController::class . ':login');
-        $group->post('/login/ajax', App\Controllers\Admin\IpController::class . ':ajaxLogin');
+        $group->get('/login', App\Controllers\Admin\LoginLogController::class . ':index');
+        $group->post('/login/ajax', App\Controllers\Admin\LoginLogController::class . ':ajax');
         // 在线IP
-        $group->get('/online', App\Controllers\Admin\IpController::class . ':online');
-        $group->post('/online/ajax', App\Controllers\Admin\IpController::class . ':ajaxOnline');
+        $group->get('/online', App\Controllers\Admin\OnlineIpController::class . ':index');
+        $group->post('/online/ajax', App\Controllers\Admin\OnlineIpController::class . ':ajax');
         // 订阅日志
         $group->get('/subscribe', App\Controllers\Admin\SubscribeLogController::class . ':index');
         $group->post('/subscribe/ajax', App\Controllers\Admin\SubscribeLogController::class . ':ajax');
@@ -206,11 +206,14 @@ return static function (Slim\App $app): void {
         $group->get('/trafficlog', App\Controllers\Admin\TrafficLogController::class . ':index');
         $group->post('/trafficlog/ajax', App\Controllers\Admin\TrafficLogController::class . ':ajax');
         // 用户余额日志
-        $group->get('/moneylog', App\Controllers\Admin\MoneyLogController::class . ':log');
+        $group->get('/moneylog', App\Controllers\Admin\MoneyLogController::class . ':index');
         $group->post('/moneylog/ajax', App\Controllers\Admin\MoneyLogController::class . ':ajax');
         // 支付网关日志
-        $group->get('/gateway', App\Controllers\Admin\PaylistController::class . ':gateway');
+        $group->get('/gateway', App\Controllers\Admin\PaylistController::class . ':index');
         $group->post('/gateway/ajax', App\Controllers\Admin\PaylistController::class . ':ajax');
+        // 系统状态
+        $group->get('/system', App\Controllers\Admin\SystemController::class . ':index');
+        $group->post('/system/check_update', App\Controllers\Admin\SystemController::class . ':checkUpdate');
         // 设置中心
         $group->get('/setting/billing', App\Controllers\Admin\Setting\BillingController::class . ':billing');
         $group->post('/setting/billing', App\Controllers\Admin\Setting\BillingController::class . ':saveBilling');

+ 4 - 0
resources/views/tabler/admin/header.tpl

@@ -135,6 +135,10 @@
                                                 <i class="ti ti-server-2"></i>&nbsp;
                                                 节点
                                             </a>
+                                            <a class="dropdown-item" href="/admin/system">
+                                                <i class="ti ti-tool"></i>&nbsp;
+                                                系统
+                                            </a>
                                         </div>
                                     </div>
                                 </div>

+ 5 - 14
resources/views/tabler/admin/index.tpl

@@ -6,10 +6,10 @@
             <div class="row align-items-center">
                 <div class="col">
                     <h2 class="page-title">
-                        <span class="home-title">系统概况</span>
+                        <span class="home-title">站点概况</span>
                     </h2>
                     <div class="page-pretitle my-3">
-                        <span class="home-subtitle">在这里查看系统的的各项运营指标</span>
+                        <span class="home-subtitle">站点运营状态总览</span>
                     </div>
                 </div>
             </div>
@@ -191,10 +191,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('node-online'), {
                 chart: {
                     type: "donut",
@@ -234,10 +231,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('user-inactive'), {
                 chart: {
                     type: "donut",
@@ -277,10 +271,7 @@
                     fillSeriesColor: false
                 },
             })).render();
-        });
-    </script>
-    <script>
-        document.addEventListener("DOMContentLoaded", function() {
+
             window.ApexCharts && (new ApexCharts(document.getElementById('traffic-usage'), {
                 chart: {
                     type: "donut",

+ 64 - 0
resources/views/tabler/admin/system.tpl

@@ -0,0 +1,64 @@
+{include file='admin/header.tpl'}
+
+<div class="page-wrapper">
+    <div class="container-xl">
+        <div class="page-header d-print-none text-white">
+            <div class="row align-items-center">
+                <div class="col">
+                    <h2 class="page-title">
+                        <span class="home-title">系统状态</span>
+                    </h2>
+                    <div class="page-pretitle my-3">
+                        <span class="home-subtitle">查看系统的运行状态</span>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+    <div class="page-body">
+        <div class="container-xl">
+            <div class="row row-deck row-cards">
+                 <div class="col-sm-12 col-lg-12">
+                     <div class="card">
+                         <div class="card-body">
+                             <table class="table table-transparent table-responsive">
+                                 <tr>
+                                     <td>SSPanel-UIM 版本</td>
+                                     <td class="text-end" id="version"><a href="#" id="version_check">{$version} </a></td>
+                                 </tr>
+                                 <tr>
+                                     <td>数据库版本</td>
+                                     <td class="text-end">{$db_version}</td>
+                                 </tr>
+                                 <tr>
+                                     <td>最后一次每日任务执行时间</td>
+                                     <td class="text-end">{$last_daily_job_time}</td>
+                                 </tr>
+                             </table>
+                         </div>
+                     </div>
+                 </div>
+            </div>
+        </div>
+    </div>
+
+    <script>
+        $('#version_check').click(function() {
+            $.ajax({
+                url: '/admin/system/check_update',
+                type: 'POST',
+                dataType: "json",
+                success: function(data) {
+                    if (data.is_upto_date === true) {
+                        $('.badge').remove();
+                        $('#version').append('<span class="badge bg-green">已是最新版本</span>');
+                    } else {
+                        $('.badge').remove();
+                        $('#version').append('<span class="badge bg-red">有新版本 ' + data.latest_version + ' 可用</span>');
+                    }
+                }
+            })
+        });
+    </script>
+
+{include file='admin/footer.tpl'}

+ 73 - 0
src/Controllers/Admin/LoginLogController.php

@@ -0,0 +1,73 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\Admin;
+
+use App\Controllers\BaseController;
+use App\Models\LoginIp;
+use App\Utils\Tools;
+use Exception;
+use MaxMind\Db\Reader\InvalidDatabaseException;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Http\Response;
+use Slim\Http\ServerRequest;
+
+final class LoginLogController extends BaseController
+{
+    public static array $details =
+        [
+            'field' => [
+                'id' => '事件ID',
+                'userid' => '用户ID',
+                'user_name' => '用户名',
+                'ip' => 'IP',
+                'location' => 'IP归属地',
+                'datetime' => '时间',
+                'type' => '类型',
+            ],
+        ];
+
+    /**
+     * 后台登录记录页面
+     *
+     * @throws Exception
+     */
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        return $response->write(
+            $this->view()
+                ->assign('details', self::$details)
+                ->fetch('admin/log/login.tpl')
+        );
+    }
+
+    /**
+     * 后台登录记录页面 AJAX
+     *
+     * @throws InvalidDatabaseException
+     */
+    public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    {
+        $length = $request->getParam('length');
+        $page = $request->getParam('start') / $length + 1;
+        $draw = $request->getParam('draw');
+
+        $logins = LoginIp::orderBy('id', 'desc')->paginate($length, '*', '', $page);
+        $total = LoginIp::count();
+
+        foreach ($logins as $login) {
+            $login->user_name = $login->userName();
+            $login->location = Tools::getIpLocation($login->ip);
+            $login->datetime = Tools::toDateTime((int) $login->datetime);
+            $login->type = $login->type();
+        }
+
+        return $response->withJson([
+            'draw' => $draw,
+            'recordsTotal' => $total,
+            'recordsFiltered' => $total,
+            'logins' => $logins,
+        ]);
+    }
+}

+ 1 - 1
src/Controllers/Admin/MoneyLogController.php

@@ -32,7 +32,7 @@ final class MoneyLogController extends BaseController
      *
      * @throws Exception
      */
-    public function log(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()

+ 5 - 62
src/Controllers/Admin/IpController.php → src/Controllers/Admin/OnlineIpController.php

@@ -5,7 +5,6 @@ declare(strict_types=1);
 namespace App\Controllers\Admin;
 
 use App\Controllers\BaseController;
-use App\Models\LoginIp;
 use App\Services\DB;
 use App\Utils\Tools;
 use Exception;
@@ -18,22 +17,9 @@ use function array_slice;
 use function count;
 use function str_replace;
 
-final class IpController extends BaseController
+final class OnlineIpController extends BaseController
 {
-    public static array $login_details =
-        [
-            'field' => [
-                'id' => '事件ID',
-                'userid' => '用户ID',
-                'user_name' => '用户名',
-                'ip' => 'IP',
-                'location' => 'IP归属地',
-                'datetime' => '时间',
-                'type' => '类型',
-            ],
-        ];
-
-    public static array $ip_details =
+    public static array $details =
         [
             'field' => [
                 'id' => '事件ID',
@@ -48,59 +34,16 @@ final class IpController extends BaseController
             ],
         ];
 
-    /**
-     * 后台登录记录页面
-     *
-     * @throws Exception
-     */
-    public function login(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
-        return $response->write(
-            $this->view()
-                ->assign('details', self::$login_details)
-                ->fetch('admin/log/login.tpl')
-        );
-    }
-
-    /**
-     * 后台登录记录页面 AJAX
-     *
-     * @throws InvalidDatabaseException
-     */
-    public function ajaxLogin(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
-    {
-        $length = $request->getParam('length');
-        $page = $request->getParam('start') / $length + 1;
-        $draw = $request->getParam('draw');
-
-        $logins = LoginIp::orderBy('id', 'desc')->paginate($length, '*', '', $page);
-        $total = LoginIp::count();
-
-        foreach ($logins as $login) {
-            $login->user_name = $login->userName();
-            $login->location = Tools::getIpLocation($login->ip);
-            $login->datetime = Tools::toDateTime((int) $login->datetime);
-            $login->type = $login->type();
-        }
-
-        return $response->withJson([
-            'draw' => $draw,
-            'recordsTotal' => $total,
-            'recordsFiltered' => $total,
-            'logins' => $logins,
-        ]);
-    }
-
     /**
      * 后台在线 IP 页面
      *
      * @throws Exception
      */
-    public function online(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()
-                ->assign('details', self::$ip_details)
+                ->assign('details', self::$details)
                 ->fetch('admin/log/online.tpl')
         );
     }
@@ -110,7 +53,7 @@ final class IpController extends BaseController
      *
      * @throws InvalidDatabaseException
      */
-    public function ajaxOnline(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function ajax(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         $data = $request->getParsedBody();
         $length = (int) ($data['length'] ?? 0);

+ 1 - 1
src/Controllers/Admin/PaylistController.php

@@ -33,7 +33,7 @@ final class PaylistController extends BaseController
      *
      * @throws Exception
      */
-    public function gateway(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
+    public function index(ServerRequest $request, Response $response, array $args): Response|ResponseInterface
     {
         return $response->write(
             $this->view()

+ 54 - 0
src/Controllers/Admin/SystemController.php

@@ -0,0 +1,54 @@
+<?php
+
+declare(strict_types=1);
+
+namespace App\Controllers\Admin;
+
+use App\Controllers\BaseController;
+use App\Models\Setting;
+use App\Utils\Tools;
+use Exception;
+use Psr\Http\Message\ResponseInterface;
+use Slim\Http\Response;
+use Slim\Http\ServerRequest;
+use const VERSION;
+
+final class SystemController extends BaseController
+{
+    /**
+     * 后台系统状态页面
+     *
+     * @throws Exception
+     */
+    public function index(ServerRequest $request, Response $response, array $args): ResponseInterface
+    {
+        $last_daily_job_time = Tools::toDateTime(Setting::obtain('last_daily_job_time'));
+        $db_version = Setting::obtain('db_version');
+
+        return $response->write(
+            $this->view()
+                ->assign('version', VERSION)
+                ->assign('last_daily_job_time', $last_daily_job_time)
+                ->assign('db_version', $db_version)
+                ->fetch('admin/system.tpl')
+        );
+    }
+
+    /**
+     * 检查版本更新
+     */
+    public function checkUpdate(ServerRequest $request, Response $response, array $args): ResponseInterface
+    {
+        $latest_version = file_get_contents('https://ota.sspanel.org/get-latest-version', false, stream_context_create([
+            'http' => [
+                'timeout' => 3,
+            ],
+        ]));
+        $is_upto_date = version_compare($latest_version, VERSION, '<=');
+
+        return $response->withJson([
+            'is_upto_date' => $is_upto_date,
+            'latest_version' => $latest_version,
+        ]);
+    }
+}

+ 19 - 11
src/Services/Captcha.php

@@ -40,18 +40,22 @@ final class Captcha
                             'response' => $turnstile,
                         ]
                     );
-                    $opts = ['http' => [
-                        'method' => 'POST',
-                        'header' => 'Content-Type: application/x-www-form-urlencoded',
-                        'content' => $postdata,
-                    ],
+
+                    $opts = [
+                        'http' => [
+                            'method' => 'POST',
+                            'header' => 'Content-Type: application/x-www-form-urlencoded',
+                            'content' => $postdata,
+                        ],
                     ];
-                    $json = file_get_contents(
+
+                    $json = json_decode(file_get_contents(
                         'https://challenges.cloudflare.com/turnstile/v0/siteverify',
                         false,
                         stream_context_create($opts)
-                    );
-                    $result = json_decode($json)->success;
+                    ));
+
+                    $result = $json->success;
                 }
                 break;
             case 'geetest':
@@ -64,6 +68,7 @@ final class Captcha
                     $pass_token = $geetest['pass_token'];
                     $gen_time = $geetest['gen_time'];
                     $sign_token = hash_hmac('sha256', $lot_number, $captcha_key);
+
                     $postdata = http_build_query(
                         [
                             'lot_number' => $lot_number,
@@ -73,6 +78,7 @@ final class Captcha
                             'sign_token' => $sign_token,
                         ]
                     );
+
                     $opts = [
                         'http' => [
                             'method' => 'POST',
@@ -81,12 +87,14 @@ final class Captcha
                             'timeout' => 5,
                         ],
                     ];
-                    $json = file_get_contents(
+
+                    $json = json_decode(file_get_contents(
                         'https://gcaptcha4.geetest.com/validate?captcha_id=' . $captcha_id,
                         false,
                         stream_context_create($opts)
-                    );
-                    if (json_decode($json)->result === 'success') {
+                    ));
+
+                    if ($json->result === 'success') {
                         $result = true;
                     }
                 }