mirror of
https://github.com/ProxyPanel/ProxyPanel.git
synced 2026-04-06 20:50:01 +00:00
完善工单/节点/系统设置的请求合规检测
This commit is contained in:
@@ -14,6 +14,7 @@ use App\Models\NodeCertificate;
|
||||
use App\Models\NodePing;
|
||||
use App\Models\RuleGroup;
|
||||
use App\Services\NodeService;
|
||||
use Arr;
|
||||
use Exception;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -68,7 +69,9 @@ class NodeController extends Controller
|
||||
public function store(NodeRequest $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$node = Node::create($request->except('_token', 'labels'));
|
||||
$array = $request->validated();
|
||||
Arr::forget($array, ['labels']);
|
||||
$node = Node::create($array);
|
||||
|
||||
if ($node) {
|
||||
// 生成节点标签
|
||||
@@ -104,10 +107,12 @@ class NodeController extends Controller
|
||||
public function update(NodeRequest $request, Node $node): JsonResponse
|
||||
{
|
||||
try {
|
||||
// 更新节点标签
|
||||
$node->labels()->sync($request->input('labels'));
|
||||
$array = $request->validated();
|
||||
Arr::forget($array, ['labels']);
|
||||
if ($node->update($array)) {
|
||||
// 更新节点标签
|
||||
$node->labels()->sync($request->input('labels'));
|
||||
|
||||
if ($node->update($request->except('_token', 'labels'))) {
|
||||
return Response::json(['status' => 'success', 'message' => '编辑成功']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -182,9 +187,7 @@ class NodeController extends Controller
|
||||
// Ping节点延迟
|
||||
public function pingNode(Node $node): JsonResponse
|
||||
{
|
||||
$result = NetworkDetection::ping($node->is_ddns ? $node->server : $node->ip);
|
||||
|
||||
if ($result) {
|
||||
if ($result = NetworkDetection::ping($node->is_ddns ? $node->server : $node->ip)) {
|
||||
return Response::json([
|
||||
'status' => 'success',
|
||||
'message' => [
|
||||
|
||||
@@ -4,11 +4,11 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Components\PushNotification;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\SystemRequest;
|
||||
use App\Models\Config;
|
||||
use App\Models\Label;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Response;
|
||||
|
||||
class SystemController extends Controller
|
||||
@@ -20,7 +20,7 @@ class SystemController extends Controller
|
||||
}
|
||||
|
||||
// 设置系统扩展信息,例如客服、统计代码
|
||||
public function setExtend(Request $request): RedirectResponse
|
||||
public function setExtend(SystemRequest $request): RedirectResponse
|
||||
{
|
||||
if ($request->hasFile('website_home_logo')) {
|
||||
$validator = validator()->make($request->all(), ['website_home_logo' => 'image|mimes:jpeg,png,jpg,gif,svg|max:2048']);
|
||||
@@ -53,29 +53,18 @@ class SystemController extends Controller
|
||||
}
|
||||
|
||||
// 设置某个配置项
|
||||
public function setConfig(Request $request): JsonResponse
|
||||
public function setConfig(SystemRequest $request): JsonResponse
|
||||
{
|
||||
$name = $request->input('name');
|
||||
$value = $request->input('value');
|
||||
|
||||
if (! $name) {
|
||||
return Response::json(['status' => 'fail', 'message' => '设置失败:请求参数异常']);
|
||||
}
|
||||
|
||||
// 屏蔽异常配置
|
||||
if (! in_array($name, Config::pluck('name')->toArray(), true)) {
|
||||
return Response::json(['status' => 'fail', 'message' => '设置失败:配置不存在']);
|
||||
}
|
||||
|
||||
// 如果开启用户邮件重置密码,则先设置网站名称和网址
|
||||
if ($value !== '0' && in_array($name, ['is_reset_password', 'is_activate_account', 'expire_warning', 'traffic_warning'], true)) {
|
||||
$config = Config::find('website_name');
|
||||
if (! $config->value) {
|
||||
if (! Config::find('website_url')->value) {
|
||||
return Response::json(['status' => 'fail', 'message' => '设置失败:启用该配置需要先设置【网站名称】']);
|
||||
}
|
||||
|
||||
$config = Config::find('website_url');
|
||||
if (! $config->value) {
|
||||
if (! Config::find('website_url')->value) {
|
||||
return Response::json(['status' => 'fail', 'message' => '设置失败:启用该配置需要先设置【网站地址】']);
|
||||
}
|
||||
}
|
||||
@@ -146,9 +135,11 @@ class SystemController extends Controller
|
||||
}
|
||||
|
||||
// 更新配置
|
||||
Config::find($name)->update(['value' => $value]);
|
||||
if (Config::findOrFail($name)->update(['value' => $value])) {
|
||||
return Response::json(['status' => 'success', 'message' => '修改成功']);
|
||||
}
|
||||
|
||||
return Response::json(['status' => 'success', 'message' => '操作成功']);
|
||||
return Response::json(['status' => 'fail', 'message' => '修改失败']);
|
||||
}
|
||||
|
||||
// 推送通知测试
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Components\Helpers;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\TicketRequest;
|
||||
use App\Mail\closeTicket;
|
||||
use App\Mail\replyTicket;
|
||||
use App\Models\Ticket;
|
||||
@@ -37,34 +38,16 @@ class TicketController extends Controller
|
||||
}
|
||||
|
||||
// 创建工单
|
||||
public function store(Request $request)
|
||||
public function store(TicketRequest $request)
|
||||
{
|
||||
$id = $request->input('id');
|
||||
$email = $request->input('email');
|
||||
$title = $request->input('title');
|
||||
$content = $request->input('content');
|
||||
|
||||
$user = User::find($id) ?: User::whereEmail($email)->first();
|
||||
|
||||
if (! $user) {
|
||||
return Response::json(['status' => 'fail', 'message' => '用户不存在']);
|
||||
}
|
||||
$data = $request->validated();
|
||||
$user = User::find($data['id']) ?: User::whereEmail($data['email'])->first();
|
||||
|
||||
if ($user === Auth::user()) {
|
||||
return Response::json(['status' => 'fail', 'message' => '不能对自己发起工单']);
|
||||
}
|
||||
|
||||
if (empty($title) || empty($content)) {
|
||||
return Response::json(['status' => 'fail', 'message' => '请输入标题和内容']);
|
||||
}
|
||||
|
||||
$obj = new Ticket();
|
||||
$obj->user_id = $user->id;
|
||||
$obj->admin_id = Auth::id();
|
||||
$obj->title = $title;
|
||||
$obj->content = $content;
|
||||
|
||||
if ($obj->save()) {
|
||||
if (Ticket::create(['user_id' => $user->id, 'admin_id' => auth()->id(), 'title' => $data['title'], 'content' => $data['content']])) {
|
||||
return Response::json(['status' => 'success', 'message' => '工单创建成功']);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,38 +9,47 @@ class NodeRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'type' => 'required|between:1,3',
|
||||
'name' => 'required',
|
||||
'country_code' => 'required',
|
||||
'is_ddns' => 'required|boolean',
|
||||
'name' => 'required|string',
|
||||
'server' => 'required_if:is_ddns,1|nullable|ends_with:'.implode(',', config('domains')),
|
||||
'push_port' => 'numeric|between:0,65535',
|
||||
'traffic_rate' => 'required|numeric|min:0',
|
||||
'level' => 'required|numeric|between:0,255',
|
||||
'speed_limit' => 'required|numeric|min:0',
|
||||
'client_limit' => 'required|numeric|min:0',
|
||||
'port' => 'nullable|numeric|between:0,65535',
|
||||
'ip' => 'ipv4|required_if:is_ddns,0|nullable',
|
||||
'ipv6' => 'nullable|ipv6',
|
||||
'relay_server' => 'required_if:is_relay,1',
|
||||
'relay_port' => 'required_if:is_relay,1|numeric|between:0,65535',
|
||||
'method' => 'required_if:type,1',
|
||||
'protocol' => 'required_if:type,1',
|
||||
'obfs' => 'required_if:type,1',
|
||||
'is_subscribe' => 'boolean',
|
||||
'is_ddns' => 'boolean',
|
||||
'is_relay' => 'boolean',
|
||||
'is_udp' => 'boolean',
|
||||
'detection_type' => 'between:0,3',
|
||||
'compatible' => 'boolean',
|
||||
'single' => 'boolean',
|
||||
'push_port' => 'numeric|between:0,65535',
|
||||
'traffic_rate' => 'required|numeric|min:0',
|
||||
'level' => 'required|numeric|exists:level,level',
|
||||
'rule_group_id' => 'nullable|exists:rule_group,id',
|
||||
'speed_limit' => 'required|numeric|min:0',
|
||||
'client_limit' => 'required|numeric|min:0',
|
||||
'labels' => 'nullable|exists:label,id',
|
||||
'country_code' => 'required|exists:country,code',
|
||||
'description' => 'nullable|string',
|
||||
'sort' => 'required|numeric|between:0,255',
|
||||
'status' => 'boolean',
|
||||
'is_udp' => 'required|boolean',
|
||||
'status' => 'required|boolean',
|
||||
'type' => 'required|numeric|between:1,4',
|
||||
'method' => 'required_if:type,1,4|exists:ss_config,name',
|
||||
'protocol' => 'required_if:type,1,4|exists:ss_config,name',
|
||||
'protocol_param' => 'nullable|string',
|
||||
'obfs' => 'required_if:type,1,4|exists:ss_config,name',
|
||||
'obfs_param' => 'nullable|string',
|
||||
'compatible' => 'required|boolean',
|
||||
'is_subscribe' => 'required|boolean',
|
||||
'detection_type' => 'required|numeric|between:0,3',
|
||||
'single' => 'required|boolean',
|
||||
'port' => 'required_if:single,1|numeric|between:1,65535|nullable',
|
||||
'passwd' => 'required_if:single,1|string|nullable',
|
||||
'v2_alter_id' => 'required_if:type,2|numeric|between:0,65535',
|
||||
'v2_port' => 'required_if:type,2|numeric|between:0,65535',
|
||||
'v2_method' => 'required_if:type,2',
|
||||
'v2_net' => 'required_if:type,2',
|
||||
'v2_type' => 'required_if:type,2',
|
||||
'v2_tls' => 'boolean',
|
||||
'v2_host' => 'string|nullable',
|
||||
'v2_path' => 'string|nullable',
|
||||
'v2_tls' => 'required_if:type,2|boolean',
|
||||
'tls_provider' => 'json|nullable',
|
||||
'is_relay' => 'required|boolean',
|
||||
'relay_server' => 'required_if:is_relay,1',
|
||||
'relay_port' => 'required_if:is_relay,1|numeric|between:1,65535',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
23
app/Http/Requests/Admin/SystemRequest.php
Normal file
23
app/Http/Requests/Admin/SystemRequest.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class SystemRequest extends FormRequest
|
||||
{
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|exists:config,name',
|
||||
'value' => 'nullable',
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'name.exists' => '设置项目不存在于数据库',
|
||||
];
|
||||
}
|
||||
}
|
||||
18
app/Http/Requests/Admin/TicketRequest.php
Normal file
18
app/Http/Requests/Admin/TicketRequest.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class TicketRequest extends FormRequest
|
||||
{
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'id' => 'required_without:email|exists:user,id|numeric|nullable',
|
||||
'email' => 'required_without:id|exists:user,email||nullable',
|
||||
'title' => 'required|string',
|
||||
'content' => 'required|string',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -107,7 +107,6 @@
|
||||
<label for="country_code" class="col-md-3 col-form-label"> 国家/地区 </label>
|
||||
<select data-plugin="selectpicker" data-style="btn-outline btn-primary"
|
||||
class="col-md-5 form-control" name="country_code" id="country_code">
|
||||
<option value="un" selected hidden>请选择</option>
|
||||
@foreach($countries as $country)
|
||||
<option value="{{$country->code}}">{{$country->code}} - {{$country->name}}</option>
|
||||
@endforeach
|
||||
|
||||
@@ -150,14 +150,6 @@
|
||||
@can('admin.rule.store')
|
||||
// 添加规则
|
||||
function addRule() {
|
||||
$.post("{{route('admin.rule.store')}}", {}, function(ret) {
|
||||
$('#add').modal('hide');
|
||||
if (ret.status === 'success') {
|
||||
swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
|
||||
} else {
|
||||
swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
|
||||
}
|
||||
});
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: "{{route('admin.rule.store')}}",
|
||||
|
||||
@@ -177,18 +177,36 @@
|
||||
confirmButtonText: '{{trans('home.ticket_confirm')}}',
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
$.post('{{route('admin.ticket.store')}}', {
|
||||
_token: '{{csrf_token()}}',
|
||||
id: id,
|
||||
email: email,
|
||||
title: title,
|
||||
content: content,
|
||||
}, function(ret) {
|
||||
if (ret.status === 'success') {
|
||||
swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
|
||||
} else {
|
||||
swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
|
||||
}
|
||||
$.ajax({
|
||||
method: 'POST',
|
||||
url: "{{route('admin.ticket.store')}}",
|
||||
data: {
|
||||
_token: '{{csrf_token()}}',
|
||||
id: id,
|
||||
email: email,
|
||||
title: title,
|
||||
content: content,
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(ret) {
|
||||
$('#add_ticket_modal').modal('hide');
|
||||
if (ret.status === 'success') {
|
||||
swal.fire({title: ret.message, icon: 'success', timer: 1000, showConfirmButton: false}).then(() => window.location.reload());
|
||||
} else {
|
||||
swal.fire({title: ret.message, icon: 'error'}).then(() => window.location.reload());
|
||||
}
|
||||
},
|
||||
error: function(data) {
|
||||
$('#add_ticket_modal').modal('hide');
|
||||
let str = '';
|
||||
const errors = data.responseJSON;
|
||||
if ($.isEmptyObject(errors) === false) {
|
||||
$.each(errors.errors, function(index, value) {
|
||||
str += '<li>' + value + '</li>';
|
||||
});
|
||||
swal.fire({title: '提示', html: str, icon: 'error', confirmButtonText: '{{trans('home.ticket_confirm')}}'});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
<div class="chat @if(($ticket->admin_id && $ticket->admin_id !== $user->id) ||($ticket->user_id && $ticket->user_id !== $user->id)) chat-left @endif">
|
||||
<div class="chat
|
||||
@if (isset($ticket->admin_id) && $ticket->admin_id !== $user->id)
|
||||
chat-left
|
||||
@elseif(isset($ticket->user_id) && !isset($ticket->admin_id) && $ticket->user_id !== $user->id)
|
||||
chat-left
|
||||
@endif">
|
||||
<div class="chat-avatar">
|
||||
<p class="avatar" data-toggle="tooltip" href="#" data-placement="right" title="" data-original-title="{{($ticket->admin ?? $ticket->user)->email}}">
|
||||
<x-avatar :user="$ticket->admin ?? $ticket->user"/>
|
||||
|
||||
@@ -175,7 +175,7 @@
|
||||
<footer class="site-footer">
|
||||
<div class="site-footer-legal">
|
||||
Copyright ©️2017 - 2020 <a href="https://github.com/ProxyPanel/ProxyPanel" target="_blank">{{config('version.name')}}</a>
|
||||
🚀 版本: {{config('version.number')}}
|
||||
🚀 版本: <code> {{config('version.number')}} </code>
|
||||
</div>
|
||||
<div class="site-footer-right">
|
||||
由 <a href="{{sysConfig('website_url')}}" target="_blank">{{sysConfig('website_name')}}</a> 🈺运营
|
||||
|
||||
Reference in New Issue
Block a user