Browse Source

优化webApi的读取/写表处理方式

兔姬桑 4 years ago
parent
commit
b1c25ed8f0

+ 0 - 16
app/Components/Helpers.php

@@ -252,20 +252,4 @@ class Helpers
 
         return $log->save();
     }
-
-    public static function abortIfNotModified($data): string
-    {
-        $req = request();
-        // Only for "GET" method
-        if (! $req->isMethod('GET')) {
-            return '';
-        }
-
-        $etag = sha1(json_encode($data));
-        if ($etag == $req->header('IF-NONE-MATCH')) {
-            abort(304);
-        }
-
-        return $etag;
-    }
 }

+ 14 - 20
app/Exceptions/Handler.php

@@ -78,23 +78,23 @@ class Handler extends ExceptionHandler
                 case $exception instanceof NotFoundHttpException: // 捕获访问异常
                     Log::info('异常请求:'.$request->fullUrl().',IP:'.IP::getClientIp());
 
-                    if ($request->ajax()) {
-                        return Response::json(['status' => 'fail', 'message' => trans('error.MissingPage')]);
+                    if ($request->ajax() || $request->wantsJson()) {
+                        return Response::json(['status' => 'fail', 'message' => trans('error.MissingPage')], 404);
                     }
 
                     return Response::view('auth.error', ['message' => trans('error.MissingPage')], 404);
                 case $exception instanceof AuthenticationException:  // 捕获身份校验异常
-                    if ($request->ajax()) {
-                        return Response::json(['status' => 'fail', 'message' => trans('error.Unauthorized')]);
+                    if ($request->ajax() || $request->wantsJson()) {
+                        return Response::json(['status' => 'fail', 'message' => trans('error.Unauthorized')], 401);
                     }
 
                     return Response::view('auth.error', ['message' => trans('error.Unauthorized')], 401);
                 case $exception instanceof TokenMismatchException: // 捕获CSRF异常
-                    if ($request->ajax()) {
+                    if ($request->ajax() || $request->wantsJson()) {
                         return Response::json([
                             'status' => 'fail',
                             'message' => trans('error.RefreshPage').'<a href="'.route('login').'" target="_blank">'.trans('error.Refresh').'</a>',
-                        ]);
+                        ], 419);
                     }
 
                     return Response::view(
@@ -103,17 +103,17 @@ class Handler extends ExceptionHandler
                         419
                     );
                 case $exception instanceof ReflectionException:
-                    if ($request->ajax()) {
-                        return Response::json(['status' => 'fail', 'message' => trans('error.SystemError')]);
+                    if ($request->ajax() || $request->wantsJson()) {
+                        return Response::json(['status' => 'fail', 'message' => trans('error.SystemError')], 500);
                     }
 
                     return Response::view('auth.error', ['message' => trans('error.SystemError')], 500);
                 case $exception instanceof ErrorException: // 捕获系统错误异常
-                    if ($request->ajax()) {
+                    if ($request->ajax() || $request->wantsJson()) {
                         return Response::json([
                             'status' => 'fail',
                             'message' => trans('error.SystemError').', '.trans('error.Visit').'<a href="'.route('admin.log.viewer').'" target="_blank">'.trans('error.log').'</a>',
-                        ]);
+                        ], 500);
                     }
 
                     return Response::view(
@@ -122,20 +122,14 @@ class Handler extends ExceptionHandler
                         500
                     );
                 case $exception instanceof ConnectionException:
-                    if ($request->ajax()) {
-                        return Response::json([
-                            'status' => 'fail',
-                            'message' => $exception->getMessage(),
-                        ]);
+                    if ($request->ajax() || $request->wantsJson()) {
+                        return Response::json(['status' => 'fail', 'message' => $exception->getMessage()], 408);
                     }
 
                     return Response::view('auth.error', ['message' => $exception->getMessage()], 408);
                 default:
-                    if ($request->ajax()) {
-                        return Response::json([
-                            'status' => 'fail',
-                            'message' => $exception->getMessage(),
-                        ]);
+                    if ($request->ajax() || $request->wantsJson()) {
+                        return Response::json(['status' => 'fail', 'message' => $exception->getMessage()]);
                     }
 
                     return Response::view('auth.error', ['message' => $exception->getMessage()]);

+ 82 - 102
app/Http/Controllers/Api/WebApi/BaseController.php

@@ -2,96 +2,86 @@
 
 namespace App\Http\Controllers\Api\WebApi;
 
-use App\Components\Helpers;
 use App\Models\Node;
-use App\Models\NodeHeartBeat;
-use App\Models\NodeOnlineIp;
-use App\Models\NodeOnlineLog;
-use App\Models\RuleLog;
-use App\Models\User;
-use App\Models\UserDataFlowLog;
-use Arr;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Response;
+use Validator;
 
 class BaseController
 {
     // 上报节点心跳信息
-    public function setNodeStatus(Request $request, $id): JsonResponse
+    public function setNodeStatus(Request $request, Node $node): JsonResponse
     {
-        $cpu = (int) $request->input('cpu') / 100;
-        $mem = (int) $request->input('mem') / 100;
-        $disk = (int) $request->input('disk') / 100;
+        $validator = Validator::make($request->all(), ['cpu' => 'required', 'mem' => 'required', 'disk' => 'required', 'uptime' => 'required|numeric']);
 
-        if (is_null($request->input('uptime'))) {
+        if ($validator->fails()) {
             return $this->returnData('上报节点心跳信息失败,请检查字段');
         }
 
-        $obj = new NodeHeartBeat();
-        $obj->node_id = $id;
-        $obj->uptime = (int) $request->input('uptime');
-        //$obj->load = $request->input('load');
-        $obj->load = implode(' ', [$cpu, $mem, $disk]);
-        $obj->log_time = time();
-        $obj->save();
+        $data = array_map('intval', $validator->validated());
 
-        if (! $obj->id) {
-            return $this->returnData('生成节点心跳信息失败');
+        if ($node->heartBeats()->create([
+            'uptime' => $data['uptime'],
+            'load' => implode(' ', [$data['cpu'] / 100, $data['mem'] / 100, $data['disk'] / 100]),
+            'log_time' => time(),
+        ])) {
+            return $this->returnData('上报节点心跳信息成功', 'success', 200);
         }
 
-        return $this->returnData('上报节点心跳信息成功', 'success', 200);
+        return $this->returnData('生成节点心跳信息失败');
     }
 
     // 返回数据
-    public function returnData($message, $status = 'fail', $code = 400, $data = [], $addition = []): JsonResponse
+    public function returnData(string $message, string $status = 'fail', int $code = 400, array $data = [], array $addition = null): JsonResponse
     {
-        $etag = Helpers::abortIfNotModified($data);
-        $data = [
-            'status' => $status,
-            'code' => $code,
-            'data' => $data,
-            'message' => $message,
-        ];
-
-        if ($addition) {
+        $etag = self::abortIfNotModified($data);
+        $data = ['status' => $status, 'code' => $code, 'data' => $data, 'message' => $message];
+
+        if (isset($addition)) {
             $data = array_merge($data, $addition);
         }
 
-        return Response::json($data)->header('ETAG', $etag);
+        return Response::json($data)->header('ETAG', $etag)->setStatusCode($code);
     }
 
-    // 上报节点在线人数
-    public function setNodeOnline(Request $request, $id): JsonResponse
+    // 检查数据是否有变动
+    private static function abortIfNotModified($data): string
     {
-        $inputArray = $request->all();
-        $onlineCount = 0;
-        foreach ($inputArray as $input) {
-            if (! Arr::has($input, ['ip', 'uid'])) {
-                return $this->returnData('上报节点在线用户IP信息失败,请检查字段');
-            }
+        $req = request();
+        // Only for "GET" method
+        if (! $req->isMethod('GET')) {
+            return '';
+        }
 
-            $obj = new NodeOnlineIp();
-            $obj->node_id = $id;
-            $obj->user_id = $input['uid'];
-            $obj->ip = $input['ip'];
-            $obj->port = User::find($input['uid'])->port;
-            $obj->created_at = time();
-            $obj->save();
+        $etag = sha1(json_encode($data));
+        if ($etag === $req->header('IF-NONE-MATCH')) {
+            abort(304);
+        }
 
-            if (! $obj->id) {
-                return $this->returnData('生成节点在线用户IP信息失败');
-            }
+        return $etag;
+    }
+
+    // 上报节点在线IP
+    public function setNodeOnline(Request $request, Node $node): JsonResponse
+    {
+        $validator = Validator::make($request->all(), ['*.uid' => 'required|numeric|exists:user,id', '*.ip' => 'required|string']);
+
+        if ($validator->fails()) {
+            return $this->returnData('上报节点在线用户IP信息失败,请检查字段');
+        }
+
+        $onlineCount = 0;
+        foreach ($validator->validated() as $input) { // 处理节点在线IP数据
+            $formattedData[] = ['user_id' => $input['uid'], 'ip' => $input['ip'], 'created_at' => time()];
             $onlineCount++;
         }
 
-        $obj = new NodeOnlineLog();
-        $obj->node_id = $id;
-        $obj->online_user = $onlineCount;
-        $obj->log_time = time();
-        $obj->save();
+        if (isset($formattedData) && ! $node->onlineIps()->createMany($formattedData)) {  // 生成节点在线IP数据
+            return $this->returnData('生成节点在线用户IP信息失败');
+        }
 
-        if ($obj->id) {
+        if ($node->onlineLogs()->create(['online_user' => $onlineCount, 'log_time' => time()])) { // 生成节点在线人数数据
             return $this->returnData('上报节点在线情况成功', 'success', 200);
         }
 
@@ -99,47 +89,39 @@ class BaseController
     }
 
     // 上报用户流量日志
-    public function setUserTraffic(Request $request, $id): JsonResponse
+    public function setUserTraffic(Request $request, Node $node): JsonResponse
     {
-        foreach ($request->all() as $input) {
-            if (! Arr::exists($input, 'uid')) {
-                return $this->returnData('上报用户流量日志失败,请检查字段');
-            }
+        $validator = Validator::make($request->all(), ['*.uid' => 'required|numeric|exists:user,id', '*.upload' => 'required|numeric', '*.download' => 'required|numeric']);
 
-            $rate = Node::find($id)->traffic_rate;
+        if ($validator->fails()) {
+            return $this->returnData('上报用户流量日志失败,请检查字段');
+        }
 
-            $log = new UserDataFlowLog();
-            $log->user_id = (int) $input['uid'];
-            $log->u = (int) $input['upload'] * $rate;
-            $log->d = (int) $input['download'] * $rate;
-            $log->node_id = $id;
-            $log->rate = $rate;
-            $log->traffic = flowAutoShow($log->u + $log->d);
-            $log->log_time = time();
-            $log->save();
+        foreach ($validator->validated() as $input) { // 处理用户流量数据
+            $rate = $node->traffic_rate;
+            $u = $input['upload'] * $rate;
+            $d = $input['download'] * $rate;
 
-            if (! $log->id) {
-                return $this->returnData('生成用户流量日志失败');
-            }
-            $user = User::find($log->user_id);
-            if ($user) {
-                $user->u += $log->u;
-                $user->d += $log->d;
-                $user->t = time();
-                $user->save();
+            $formattedData[] = ['user_id' => $input['uid'], 'u' => $u, 'd' => $d, 'rate' => $rate, 'traffic' => flowAutoShow($u + $d), 'log_time' => time()];
+        }
+
+        if (isset($formattedData) && $logs = $node->userDataFlowLogs()->createMany($formattedData)) { // 生成用户流量数据
+            foreach ($logs as $log) { // 更新用户流量数据
+                $user = $log->user;
+                $user->update(['u' => $user->u + $log->u, 'd' => $user->d + $log->d, 't' => time()]);
             }
+
+            return $this->returnData('上报用户流量日志成功', 'success', 200);
         }
 
-        return $this->returnData('上报用户流量日志成功', 'success', 200);
+        return $this->returnData('生成用户流量日志失败');
     }
 
     // 获取节点的审计规则
     public function getNodeRule(Node $node): JsonResponse
     {
-        $data = [];
-        //节点未设置任何审计规则
-        if ($node->rule_group_id) {
-            $ruleGroup = $node->ruleGroup;
+        // 节点未设置任何审计规则
+        if ($ruleGroup = $node->ruleGroup) {
             foreach ($ruleGroup->rules as $rule) {
                 $data[] = [
                     'id' => $rule->id,
@@ -148,26 +130,24 @@ class BaseController
                 ];
             }
 
-            return $this->returnData('获取节点审计规则成功', 'success', 200, ['mode' => $ruleGroup->type ? 'reject' : 'allow', 'rules' => $data]);
+            return $this->returnData('获取节点审计规则成功', 'success', 200, ['mode' => $ruleGroup->type ? 'reject' : 'allow', 'rules' => $data ?? []]);
         }
 
-        //放行
-        return $this->returnData('获取节点审计规则成功', 'success', 200, ['mode' => 'all', 'rules' => $data]);
+        // 放行
+        return $this->returnData('获取节点审计规则成功', 'success', 200, ['mode' => 'all', 'rules' => $data ?? []]);
     }
 
-    // 上报用户触发审计规则记录
-    public function addRuleLog(Request $request, $id): JsonResponse
+    // 上报用户触发审计规则记录
+    public function addRuleLog(Request $request, Node $node): JsonResponse
     {
-        if ($request->has(['uid', 'rule_id', 'reason'])) {
-            $obj = new RuleLog();
-            $obj->user_id = $request->input('uid');
-            $obj->node_id = $id;
-            $obj->rule_id = $request->input('rule_id');
-            $obj->reason = $request->input('reason');
-
-            if ($obj->save()) {
-                return $this->returnData('上报用户触发审计规则日志成功', 'success', 200);
-            }
+        $validator = Validator::make($request->all(), ['uid' => 'required|numeric|exists:user,id', 'rule_id' => 'required|numeric|exists:rule,id', 'reason' => 'required']);
+
+        if ($validator->fails()) {
+            return $this->returnData('上报用户触发审计规则日志失败,请检查字段');
+        }
+        $data = $validator->validated();
+        if ($node->ruleLogs()->create(['user_id' => $data['uid'], 'rule_id' => $data['rule_id'], 'reason' => $data['reason']])) {
+            return $this->returnData('上报用户触发审计规则日志成功', 'success', 200);
         }
 
         return $this->returnData('上报用户触发审计规则日志失败');

+ 1 - 5
app/Http/Controllers/Api/WebApi/VNetController.php

@@ -45,10 +45,6 @@ class VNetController extends BaseController
             ];
         }
 
-        if (isset($data)) {
-            return $this->returnData('获取用户列表成功', 'success', 200, $data, ['updateTime' => time()]);
-        }
-
-        return $this->returnData('获取用户列表失败');
+        return $this->returnData('获取用户列表成功', 'success', 200, $data ?? [], ['updateTime' => time()]);
     }
 }

+ 4 - 15
app/Http/Middleware/WebApi.php

@@ -2,8 +2,6 @@
 
 namespace App\Http\Middleware;
 
-use App\Models\Node;
-use App\Models\NodeAuth;
 use Closure;
 use Illuminate\Http\JsonResponse;
 use Response;
@@ -20,7 +18,7 @@ class WebApi
      */
     public function handle($request, Closure $next)
     {
-        $id = $request->id;
+        $node = $request->node;
         $key = $request->header('key');
         $time = $request->header('timestamp');
 
@@ -28,17 +26,8 @@ class WebApi
             return $this->returnData('Your key is null!');
         }
 
-        if (! isset($id)) {// 未提供 node
-            return $this->returnData('Your Node Id is null!');
-        }
-
-        $node = Node::find($id);
-        if (! $node) {// node不存在
-            return $this->returnData('Unknown Node!');
-        }
-
-        $nodeAuth = NodeAuth::whereNodeId($id)->first();
-        if (! $nodeAuth || $key !== $nodeAuth->key) {// key不存在/不匹配
+        $nodeAuth = $node->auth ?? null;
+        if (! isset($nodeAuth) || $key !== $nodeAuth->key) {// key不存在/不匹配
             return $this->returnData('Token is invalid!');
         }
 
@@ -50,7 +39,7 @@ class WebApi
     }
 
     // 返回数据
-    public function returnData($message): JsonResponse
+    public function returnData(string $message): JsonResponse
     {
         return Response::json(['status' => 'fail', 'code' => 404, 'message' => $message]);
     }

+ 2 - 2
app/Jobs/VNet/reloadNode.php

@@ -42,10 +42,10 @@ class reloadNode implements ShouldQueue
                 'push_port' => $node->push_port,
                 'single' => $node->single,
                 'secret' => $node->auth->secret,
-                'is_udp' => $node->is_udp,
                 'speed_limit' => $node->getRawOriginal('speed_limit'),
+                'is_udp' => $node->is_udp,
                 'client_limit' => $node->client_limit,
-                'redirect_url' => (string) sysConfig('redirect_url'),
+                // 'redirect_url' => (string) sysConfig('redirect_url'),
             ]);
 
             if (! $ret) {

+ 15 - 0
app/Models/Node.php

@@ -26,11 +26,26 @@ class Node extends Model
         return $this->hasMany(NodeHeartBeat::class);
     }
 
+    public function onlineIps(): HasMany
+    {
+        return $this->hasMany(NodeOnlineIp::class);
+    }
+
     public function onlineLogs(): HasMany
     {
         return $this->hasMany(NodeOnlineLog::class);
     }
 
+    public function userDataFlowLogs(): HasMany
+    {
+        return $this->hasMany(UserDataFlowLog::class);
+    }
+
+    public function ruleLogs(): HasMany
+    {
+        return $this->hasMany(RuleLog::class);
+    }
+
     public function pingLogs(): HasMany
     {
         return $this->hasMany(NodePing::class);

+ 1 - 0
app/Models/NodeHeartBeat.php

@@ -11,6 +11,7 @@ class NodeHeartbeat extends Model
 {
     public $timestamps = false;
     protected $table = 'node_heartbeat';
+    protected $guarded = [];
 
     public function scopeRecently($query)
     {

+ 1 - 0
app/Models/NodeOnlineIp.php

@@ -12,6 +12,7 @@ class NodeOnlineIp extends Model
 {
     public $timestamps = false;
     protected $table = 'node_online_ip';
+    protected $guarded = [];
 
     public function node(): BelongsTo
     {

+ 1 - 0
app/Models/NodeOnlineLog.php

@@ -11,4 +11,5 @@ class NodeOnlineLog extends Model
 {
     public $timestamps = false;
     protected $table = 'node_online_log';
+    protected $guarded = [];
 }

+ 1 - 0
app/Models/RuleLog.php

@@ -12,6 +12,7 @@ class RuleLog extends Model
 {
     public const UPDATED_AT = null;
     protected $table = 'rule_log';
+    protected $guarded = [];
 
     public function user(): BelongsTo
     {

+ 3 - 2
app/Models/UserDataFlowLog.php

@@ -12,16 +12,17 @@ class UserDataFlowLog extends Model
 {
     public $timestamps = false;
     protected $table = 'user_traffic_log';
+    protected $guarded = [];
 
     // 关联账号
     public function user(): BelongsTo
     {
-        return $this->belongsTo(User::class, 'user_id', 'id');
+        return $this->belongsTo(User::class);
     }
 
     // 关联节点
     public function node(): BelongsTo
     {
-        return $this->belongsTo(Node::class, 'node_id', 'id');
+        return $this->belongsTo(Node::class);
     }
 }

+ 16 - 16
routes/api.php

@@ -5,46 +5,46 @@ Route::group(['namespace' => 'Api\WebApi', 'middleware' => 'webApi'], function (
     // VNet后端WEBAPI V1版
     Route::group(['prefix' => 'web/v1'], function () {
         Route::get('node/{node}', 'VNetController@getNodeInfo'); // 获取节点信息
-        Route::post('nodeStatus/{id}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
-        Route::post('nodeOnline/{id}', 'BaseController@setNodeOnline'); // 上报节点在线人数
+        Route::post('nodeStatus/{node}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
+        Route::post('nodeOnline/{node}', 'BaseController@setNodeOnline'); // 上报节点在线人数
         Route::get('userList/{node}', 'VNetController@getUserList'); // 获取节点可用的用户列表
-        Route::post('userTraffic/{id}', 'BaseController@setUserTraffic'); // 上报用户流量日志
+        Route::post('userTraffic/{node}', 'BaseController@setUserTraffic'); // 上报用户流量日志
         Route::get('nodeRule/{node}', 'BaseController@getNodeRule'); // 获取节点的审计规则
-        Route::post('trigger/{id}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
+        Route::post('trigger/{node}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
     });
 
     // VNet后端WEBAPI V2版
     Route::group(['prefix' => 'vnet/v2'], function () {
         Route::get('node/{node}', 'VNetController@getNodeInfo'); // 获取节点信息
-        Route::post('nodeStatus/{id}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
-        Route::post('nodeOnline/{id}', 'BaseController@setNodeOnline'); // 上报节点在线人数
+        Route::post('nodeStatus/{node}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
+        Route::post('nodeOnline/{node}', 'BaseController@setNodeOnline'); // 上报节点在线人数
         Route::get('userList/{node}', 'VNetController@getUserList'); // 获取节点可用的用户列表
-        Route::post('userTraffic/{id}', 'BaseController@setUserTraffic'); // 上报用户流量日志
+        Route::post('userTraffic/{node}', 'BaseController@setUserTraffic'); // 上报用户流量日志
         Route::get('nodeRule/{node}', 'BaseController@getNodeRule'); // 获取节点的审计规则
-        Route::post('trigger/{id}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
+        Route::post('trigger/{node}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
     });
 
     // V2Ray后端WEBAPI V1版
     Route::group(['prefix' => 'v2ray/v1'], function () {
         Route::get('node/{node}', 'V2RayController@getNodeInfo'); // 获取节点信息
-        Route::post('nodeStatus/{id}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
-        Route::post('nodeOnline/{id}', 'BaseController@setNodeOnline'); // 上报节点在线人数
+        Route::post('nodeStatus/{node}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
+        Route::post('nodeOnline/{node}', 'BaseController@setNodeOnline'); // 上报节点在线人数
         Route::get('userList/{node}', 'V2RayController@getUserList'); // 获取节点可用的用户列表
-        Route::post('userTraffic/{id}', 'BaseController@setUserTraffic'); // 上报用户流量日志
+        Route::post('userTraffic/{node}', 'BaseController@setUserTraffic'); // 上报用户流量日志
         Route::get('nodeRule/{node}', 'BaseController@getNodeRule'); // 获取节点的审计规则
-        Route::post('trigger/{id}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
+        Route::post('trigger/{node}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
         Route::post('certificate/{node}', 'V2RayController@addCertificate'); // 上报节点伪装域名证书信息
     });
 
     // Trojan后端WEBAPI V1版
     Route::group(['prefix' => 'trojan/v1'], function () {
         Route::get('node/{node}', 'TrojanController@getNodeInfo'); // 获取节点信息
-        Route::post('nodeStatus/{id}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
-        Route::post('nodeOnline/{id}', 'BaseController@setNodeOnline'); // 上报节点在线人数
+        Route::post('nodeStatus/{node}', 'BaseController@setNodeStatus'); // 上报节点心跳信息
+        Route::post('nodeOnline/{node}', 'BaseController@setNodeOnline'); // 上报节点在线人数
         Route::get('userList/{node}', 'TrojanController@getUserList'); // 获取节点可用的用户列表
-        Route::post('userTraffic/{id}', 'BaseController@setUserTraffic'); // 上报用户流量日志
+        Route::post('userTraffic/{node}', 'BaseController@setUserTraffic'); // 上报用户流量日志
         Route::get('nodeRule/{node}', 'BaseController@getNodeRule'); // 获取节点的审计规则
-        Route::post('trigger/{id}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
+        Route::post('trigger/{node}', 'BaseController@addRuleLog'); // 上报用户触发的审计规则记录
     });
 });