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

This commit is contained in:
兔姬桑
2020-12-31 03:22:49 +08:00
parent 330158f2fb
commit b1c25ed8f0
13 changed files with 143 additions and 180 deletions

View File

@@ -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;
}
}

View File

@@ -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()]);

View File

@@ -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,
];
$etag = self::abortIfNotModified($data);
$data = ['status' => $status, 'code' => $code, 'data' => $data, 'message' => $message];
if ($addition) {
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();
$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;
}
// 上报节点在线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 ($inputArray as $input) {
if (! Arr::has($input, ['ip', 'uid'])) {
return $this->returnData('上报节点在线用户IP信息失败请检查字段');
}
$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();
if (! $obj->id) {
return $this->returnData('生成节点在线用户IP信息失败');
}
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;
$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();
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();
}
if ($validator->fails()) {
return $this->returnData('上报用户流量日志失败,请检查字段');
}
return $this->returnData('上报用户流量日志成功', 'success', 200);
foreach ($validator->validated() as $input) { // 处理用户流量数据
$rate = $node->traffic_rate;
$u = $input['upload'] * $rate;
$d = $input['download'] * $rate;
$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('生成用户流量日志失败');
}
// 获取节点的审计规则
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');
$validator = Validator::make($request->all(), ['uid' => 'required|numeric|exists:user,id', 'rule_id' => 'required|numeric|exists:rule,id', 'reason' => 'required']);
if ($obj->save()) {
return $this->returnData('上报用户触发审计规则日志成功', 'success', 200);
}
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('上报用户触发审计规则日志失败');

View File

@@ -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()]);
}
}

View File

@@ -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]);
}

View File

@@ -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) {

View File

@@ -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);

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
}

View File

@@ -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'); // 上报用户触发的审计规则记录
});
});