mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-24 03:57:27 +08:00
feat: new xboard
This commit is contained in:
@@ -1,141 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\CouponGenerate;
|
||||
use App\Http\Requests\Admin\CouponSave;
|
||||
use App\Models\Coupon;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CouponController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
|
||||
$sort = $request->input('sort') ? $request->input('sort') : 'id';
|
||||
$builder = Coupon::orderBy($sort, $sortType);
|
||||
$total = $builder->count();
|
||||
$coupons = $builder->forPage($current, $pageSize)
|
||||
->get();
|
||||
return response([
|
||||
'data' => $coupons,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
],[
|
||||
'id.required' => '优惠券ID不能为空',
|
||||
'id.numeric' => '优惠券ID必须为数字'
|
||||
]);
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
return $this->fail([400202,'优惠券不存在']);
|
||||
}
|
||||
$coupon->show = !$coupon->show;
|
||||
if (!$coupon->save()) {
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function generate(CouponGenerate $request)
|
||||
{
|
||||
if ($request->input('generate_count')) {
|
||||
$this->multiGenerate($request);
|
||||
return;
|
||||
}
|
||||
|
||||
$params = $request->validated();
|
||||
if (!$request->input('id')) {
|
||||
if (!isset($params['code'])) {
|
||||
$params['code'] = Helper::randomChar(8);
|
||||
}
|
||||
if (!Coupon::create($params)) {
|
||||
return $this->fail([500,'创建失败']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Coupon::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
private function multiGenerate(CouponGenerate $request)
|
||||
{
|
||||
$coupons = [];
|
||||
$coupon = $request->validated();
|
||||
$coupon['created_at'] = $coupon['updated_at'] = time();
|
||||
$coupon['show'] = 1;
|
||||
unset($coupon['generate_count']);
|
||||
for ($i = 0;$i < $request->input('generate_count');$i++) {
|
||||
$coupon['code'] = Helper::randomChar(8);
|
||||
array_push($coupons, $coupon);
|
||||
}
|
||||
try{
|
||||
DB::beginTransaction();
|
||||
if (!Coupon::insert(array_map(function ($item) use ($coupon) {
|
||||
// format data
|
||||
if (isset($item['limit_plan_ids']) && is_array($item['limit_plan_ids'])) {
|
||||
$item['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
|
||||
}
|
||||
if (isset($item['limit_period']) && is_array($item['limit_period'])) {
|
||||
$item['limit_period'] = json_encode($coupon['limit_period']);
|
||||
}
|
||||
return $item;
|
||||
}, $coupons))) {
|
||||
throw new \Exception();
|
||||
}
|
||||
DB::commit();
|
||||
}catch(\Exception $e){
|
||||
DB::rollBack();
|
||||
return $this->fail([500, '生成失败']);
|
||||
}
|
||||
|
||||
$data = "名称,类型,金额或比例,开始时间,结束时间,可用次数,可用于订阅,券码,生成时间\r\n";
|
||||
foreach($coupons as $coupon) {
|
||||
$type = ['', '金额', '比例'][$coupon['type']];
|
||||
$value = ['', ($coupon['value'] / 100),$coupon['value']][$coupon['type']];
|
||||
$startTime = date('Y-m-d H:i:s', $coupon['started_at']);
|
||||
$endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
|
||||
$limitUse = $coupon['limit_use'] ?? '不限制';
|
||||
$createTime = date('Y-m-d H:i:s', $coupon['created_at']);
|
||||
$limitPlanIds = isset($coupon['limit_plan_ids']) ? implode("/", $coupon['limit_plan_ids']) : '不限制';
|
||||
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
|
||||
}
|
||||
echo $data;
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
],[
|
||||
'id.required' => '优惠券ID不能为空',
|
||||
'id.numeric' => '优惠券ID必须为数字'
|
||||
]);
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
return $this->fail([400202,'优惠券不存在']);
|
||||
}
|
||||
if (!$coupon->delete()) {
|
||||
return $this->fail([500,'删除失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\NoticeSave;
|
||||
use App\Models\Notice;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class NoticeController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return $this->success(Notice::orderBy('id', 'DESC')->get());
|
||||
}
|
||||
|
||||
public function save(NoticeSave $request)
|
||||
{
|
||||
$data = $request->only([
|
||||
'title',
|
||||
'content',
|
||||
'img_url',
|
||||
'tags'
|
||||
]);
|
||||
if (!$request->input('id')) {
|
||||
if (!Notice::create($data)) {
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Notice::find($request->input('id'))->update($data);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
}
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
return $this->fail([500 ,'公告ID不能为空']);
|
||||
}
|
||||
$notice = Notice::find($request->input('id'));
|
||||
if (!$notice) {
|
||||
return $this->fail([400202 ,'公告不存在']);
|
||||
}
|
||||
$notice->show = $notice->show ? 0 : 1;
|
||||
if (!$notice->save()) {
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
return $this->fail([422 ,'公告ID不能为空']);
|
||||
}
|
||||
$notice = Notice::find($request->input('id'));
|
||||
if (!$notice) {
|
||||
return $this->fail([400202 ,'公告不存在']);
|
||||
}
|
||||
if (!$notice->delete()) {
|
||||
return $this->fail([500 ,'删除失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderFetch;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Models\CommissionLog;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
private function filter(Request $request, &$builder)
|
||||
{
|
||||
if ($request->input('filter')) {
|
||||
foreach ($request->input('filter') as $filter) {
|
||||
if ($filter['key'] === 'email') {
|
||||
$user = User::where('email', "%{$filter['value']}%")->first();
|
||||
if (!$user) continue;
|
||||
$builder->where('user_id', $user->id);
|
||||
continue;
|
||||
}
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
}
|
||||
$builder->where($filter['key'], $filter['condition'], $filter['value']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function detail(Request $request)
|
||||
{
|
||||
$order = Order::find($request->input('id'));
|
||||
if (!$order) return $this->fail([400202 ,'订单不存在']);
|
||||
$order['commission_log'] = CommissionLog::where('trade_no', $order->trade_no)->get();
|
||||
if ($order->surplus_order_ids) {
|
||||
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
|
||||
}
|
||||
return $this->success($order);
|
||||
}
|
||||
|
||||
public function fetch(OrderFetch $request)
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$orderModel = Order::orderBy('created_at', 'DESC');
|
||||
if ($request->input('is_commission')) {
|
||||
$orderModel->where('invite_user_id', '!=', NULL);
|
||||
$orderModel->whereNotIn('status', [0, 2]);
|
||||
$orderModel->where('commission_balance', '>', 0);
|
||||
}
|
||||
$this->filter($request, $orderModel);
|
||||
$total = $orderModel->count();
|
||||
$res = $orderModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
$plan = Plan::get();
|
||||
for ($i = 0; $i < count($res); $i++) {
|
||||
for ($k = 0; $k < count($plan); $k++) {
|
||||
if ($plan[$k]['id'] == $res[$i]['plan_id']) {
|
||||
$res[$i]['plan_name'] = $plan[$k]['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return response([
|
||||
'data' => $res,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
public function paid(Request $request)
|
||||
{
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202 ,'订单不存在']);
|
||||
}
|
||||
if ($order->status !== 0) return $this->fail([400 ,'只能对待支付的订单进行操作']);
|
||||
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->paid('manual_operation')) {
|
||||
return $this->fail([500 ,'更新失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202 ,'订单不存在']);
|
||||
}
|
||||
if ($order->status !== 0) return $this->fail([400 ,'只能对待支付的订单进行操作']);
|
||||
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
return $this->fail([400 ,'更新失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function update(OrderUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'commission_status'
|
||||
]);
|
||||
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202 ,'订单不存在']);
|
||||
}
|
||||
|
||||
try {
|
||||
$order->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500 ,'更新失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function assign(OrderAssign $request)
|
||||
{
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
|
||||
if (!$user) {
|
||||
return $this->fail([400202 ,'该用户不存在']);
|
||||
}
|
||||
|
||||
if (!$plan) {
|
||||
return $this->fail([400202 ,'该订阅不存在']);
|
||||
}
|
||||
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($user->id)) {
|
||||
return $this->fail([400 ,'该用户还有待支付的订单,无法分配']);
|
||||
}
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $user->id;
|
||||
$order->plan_id = $plan->id;
|
||||
$order->period = $request->input('period');
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->total_amount = $request->input('total_amount');
|
||||
|
||||
if ($order->period === 'reset_price') {
|
||||
$order->type = Order::TYPE_RESET_TRAFFIC;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
$order->type = Order::TYPE_UPGRADE;
|
||||
} else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
|
||||
$order->type = Order::TYPE_RENEWAL;
|
||||
} else {
|
||||
$order->type = Order::TYPE_NEW_PURCHASE;
|
||||
}
|
||||
|
||||
$orderService->setInvite($user);
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return $this->fail([500 ,'订单创建失败']);
|
||||
}
|
||||
DB::commit();
|
||||
}catch(\Exception $e){
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->success($order->trade_no);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plan;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\ServerVmess;
|
||||
use App\Models\User;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class GroupController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('group_id')) {
|
||||
return $this->success([ServerGroup::find($request->input('group_id'))]);
|
||||
}
|
||||
$serverGroups = ServerGroup::get();
|
||||
$servers = ServerService::getAllServers();
|
||||
foreach ($serverGroups as $k => $v) {
|
||||
$serverGroups[$k]['user_count'] = User::where('group_id', $v['id'])->count();
|
||||
$serverGroups[$k]['server_count'] = 0;
|
||||
foreach ($servers as $server) {
|
||||
if (in_array($v['id'], $server['group_id'])) {
|
||||
$serverGroups[$k]['server_count'] = $serverGroups[$k]['server_count']+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->success($serverGroups);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (empty($request->input('name'))) {
|
||||
return $this->fail([422,'组名不能为空']);
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$serverGroup = ServerGroup::find($request->input('id'));
|
||||
} else {
|
||||
$serverGroup = new ServerGroup();
|
||||
}
|
||||
|
||||
$serverGroup->name = $request->input('name');
|
||||
return $this->success($serverGroup->save());
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$serverGroup = ServerGroup::find($request->input('id'));
|
||||
if (!$serverGroup) {
|
||||
return $this->fail([400202,'组不存在']);
|
||||
}
|
||||
}
|
||||
|
||||
$servers = ServerVmess::all();
|
||||
foreach ($servers as $server) {
|
||||
if (in_array($request->input('id'), $server->group_id)) {
|
||||
return $this->fail([400,'该组已被节点所使用,无法删除']);
|
||||
}
|
||||
}
|
||||
|
||||
if (Plan::where('group_id', $request->input('id'))->first()) {
|
||||
return $this->fail([400, '该组已被订阅所使用,无法删除']);
|
||||
}
|
||||
if (User::where('group_id', $request->input('id'))->first()) {
|
||||
return $this->fail([400, '该组已被用户所使用,无法删除']);
|
||||
}
|
||||
return $this->success($serverGroup->delete());
|
||||
}
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerHysteria;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class HysteriaController extends Controller
|
||||
{
|
||||
public function save(Request $request)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'show' => '',
|
||||
'name' => 'required',
|
||||
'group_id' => 'required|array',
|
||||
'route_id' => 'nullable|array',
|
||||
'parent_id' => 'nullable|integer',
|
||||
'host' => 'required',
|
||||
'port' => 'required',
|
||||
'server_port' => 'required',
|
||||
'tags' => 'nullable|array',
|
||||
'excludes' => 'nullable|array',
|
||||
'ips' => 'nullable|array',
|
||||
'rate' => 'required|numeric',
|
||||
'up_mbps' => 'required|numeric|min:1',
|
||||
'down_mbps' => 'required|numeric|min:1',
|
||||
'server_name' => 'nullable',
|
||||
'insecure' => 'required|in:0,1',
|
||||
'alpn' => 'nullable|in:0,1,2,3',
|
||||
'version' => 'nullable|in:1,2',
|
||||
'is_obfs' => 'nullable'
|
||||
],[
|
||||
'name.required' => '节点名称不能为空',
|
||||
'group_id.required' => '权限组不能为空',
|
||||
'host.required' => '节点地址不能为空',
|
||||
'port.required' => '连接端口不能为空',
|
||||
'server_port' => '服务端口不能为空',
|
||||
'rate.required' => '倍率不能为空',
|
||||
'up_mbps.required' => '上行带宽不能为空',
|
||||
'down_mbps.required' => '下行带宽不能为空',
|
||||
]);
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = ServerHysteria::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
if (!ServerHysteria::create($params)) {
|
||||
return $this->fail([500,'创建失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = ServerHysteria::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'节点ID不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($server->delete());
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'show' => 'in:0,1'
|
||||
], [
|
||||
'show.in' => '显示状态格式不正确'
|
||||
]);
|
||||
$params = $request->only([
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = ServerHysteria::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'该服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = ServerHysteria::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'服务器不存在']);
|
||||
}
|
||||
ServerHysteria::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ManageController extends Controller
|
||||
{
|
||||
public function getNodes(Request $request)
|
||||
{
|
||||
return $this->success(ServerService::getAllServers());
|
||||
}
|
||||
|
||||
public function sort(Request $request)
|
||||
{
|
||||
ini_set('post_max_size', '1m');
|
||||
$params = $request->only(
|
||||
'shadowsocks',
|
||||
'vmess',
|
||||
'trojan',
|
||||
'hysteria',
|
||||
'vless'
|
||||
) ?? [];
|
||||
try{
|
||||
DB::beginTransaction();
|
||||
foreach ($params as $k => $v) {
|
||||
$model = 'App\\Models\\Server' . ucfirst($k);
|
||||
foreach($v as $id => $sort) {
|
||||
$model::where('id', $id)->update(['sort' => $sort]);
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
}catch (\Exception $e){
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksSave;
|
||||
use App\Http\Requests\Admin\ServerShadowsocksUpdate;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class ShadowsocksController extends Controller
|
||||
{
|
||||
public function save(ServerShadowsocksSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
if ($request->input('id')) {
|
||||
$server = ServerShadowsocks::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
return $this->success(true);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
}
|
||||
|
||||
try{
|
||||
ServerShadowsocks::create($params);
|
||||
return $this->success(true);
|
||||
}catch(\Exception $e){
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'创建失败']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = ServerShadowsocks::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '节点不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($server->delete());
|
||||
}
|
||||
|
||||
public function update(ServerShadowsocksUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = ServerShadowsocks::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '该服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = ServerShadowsocks::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'服务器不存在']);
|
||||
}
|
||||
ServerShadowsocks::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerTrojanSave;
|
||||
use App\Http\Requests\Admin\ServerTrojanUpdate;
|
||||
use App\Models\ServerTrojan;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TrojanController extends Controller
|
||||
{
|
||||
public function save(ServerTrojanSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
if ($request->input('id')) {
|
||||
$server = ServerTrojan::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
ServerTrojan::create($params);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = ServerTrojan::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'节点ID不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($server->delete());
|
||||
}
|
||||
|
||||
public function update(ServerTrojanUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = ServerTrojan::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'该服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = ServerTrojan::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'服务器不存在']);
|
||||
}
|
||||
ServerTrojan::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerVless;
|
||||
use Illuminate\Http\Request;
|
||||
use ParagonIE_Sodium_Compat as SodiumCompat;
|
||||
use App\Utils\Helper;
|
||||
|
||||
class VlessController extends Controller
|
||||
{
|
||||
public function save(Request $request)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'group_id' => 'required',
|
||||
'route_id' => 'nullable|array',
|
||||
'name' => 'required',
|
||||
'parent_id' => 'nullable|integer',
|
||||
'host' => 'required',
|
||||
'port' => 'required',
|
||||
'server_port' => 'required',
|
||||
'tls' => 'required|in:0,1,2',
|
||||
'tls_settings' => 'nullable|array',
|
||||
'flow' => 'nullable|in:xtls-rprx-vision',
|
||||
'network' => 'required',
|
||||
'network_settings' => 'nullable|array',
|
||||
'tags' => 'nullable|array',
|
||||
'excludes' => 'nullable|array',
|
||||
'ips' => 'nullable|array',
|
||||
'rate' => 'required',
|
||||
'show' => 'nullable|in:0,1',
|
||||
'sort' => 'nullable'
|
||||
],[
|
||||
'name.required' => '节点名称不能为空',
|
||||
'group_id.required' => '权限组不能为空',
|
||||
'host.required' => '节点地址不能为空',
|
||||
'port.required' => '连接端口不能为空',
|
||||
'server_port' => '服务端口不能为空',
|
||||
'rate.required' => '倍率不能为空',
|
||||
'network.required' => '协议不能为空',
|
||||
]);
|
||||
|
||||
if (isset($params['tls']) && (int)$params['tls'] === 2) {
|
||||
$keyPair = SodiumCompat::crypto_box_keypair();
|
||||
$params['tls_settings'] = $params['tls_settings'] ?? [];
|
||||
if (!isset($params['tls_settings']['public_key'])) {
|
||||
$params['tls_settings']['public_key'] = Helper::base64EncodeUrlSafe(SodiumCompat::crypto_box_publickey($keyPair));
|
||||
}
|
||||
if (!isset($params['tls_settings']['private_key'])) {
|
||||
$params['tls_settings']['private_key'] = Helper::base64EncodeUrlSafe(SodiumCompat::crypto_box_secretkey($keyPair));
|
||||
}
|
||||
if (!isset($params['tls_settings']['short_id'])) {
|
||||
$params['tls_settings']['short_id'] = substr(sha1($params['tls_settings']['private_key']), 0, 8);
|
||||
}
|
||||
if (!isset($params['tls_settings']['server_port'])) {
|
||||
$params['tls_settings']['server_port'] = "443";
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = ServerVless::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
ServerVless::create($params);
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = ServerVless::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202,'节点不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($server->delete());
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'show' => 'nullable|in:0,1',
|
||||
]);
|
||||
|
||||
$server = ServerVless::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '该服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = ServerVless::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '该服务器不存在']);
|
||||
}
|
||||
ServerVless::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerVmessSave;
|
||||
use App\Http\Requests\Admin\ServerVmessUpdate;
|
||||
use App\Models\ServerVmess;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class VmessController extends Controller
|
||||
{
|
||||
public function save(ServerVmessSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = ServerVmess::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
ServerVmess::create($params);
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = ServerVmess::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '节点不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($server->delete());
|
||||
}
|
||||
|
||||
public function update(ServerVmessUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = ServerVmess::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '该服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = ServerVmess::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '该服务器不存在']);
|
||||
}
|
||||
ServerVmess::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CommissionLog;
|
||||
use App\Models\Order;
|
||||
use App\Models\ServerHysteria;
|
||||
use App\Models\ServerVless;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\ServerVmess;
|
||||
use App\Models\Stat;
|
||||
use App\Models\StatServer;
|
||||
use App\Models\StatUser;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Services\StatisticalService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StatController extends Controller
|
||||
{
|
||||
public function getOverride(Request $request)
|
||||
{
|
||||
return [
|
||||
'data' => [
|
||||
'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->count(),
|
||||
'ticket_pending_total' => Ticket::where('status', 0)
|
||||
->count(),
|
||||
'commission_pending_total' => Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('commission_balance', '>', 0)
|
||||
->count(),
|
||||
'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'commission_month_payout' => CommissionLog::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->sum('get_amount'),
|
||||
'commission_last_month_payout' => CommissionLog::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->sum('get_amount'),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function getOrder(Request $request)
|
||||
{
|
||||
$statistics = Stat::where('record_type', 'd')
|
||||
->limit(31)
|
||||
->orderBy('record_at', 'DESC')
|
||||
->get()
|
||||
->toArray();
|
||||
$result = [];
|
||||
foreach ($statistics as $statistic) {
|
||||
$date = date('m-d', $statistic['record_at']);
|
||||
$result[] = [
|
||||
'type' => '收款金额',
|
||||
'date' => $date,
|
||||
'value' => $statistic['paid_total'] / 100
|
||||
];
|
||||
$result[] = [
|
||||
'type' => '收款笔数',
|
||||
'date' => $date,
|
||||
'value' => $statistic['paid_count']
|
||||
];
|
||||
$result[] = [
|
||||
'type' => '佣金金额(已发放)',
|
||||
'date' => $date,
|
||||
'value' => $statistic['commission_total'] / 100
|
||||
];
|
||||
$result[] = [
|
||||
'type' => '佣金笔数(已发放)',
|
||||
'date' => $date,
|
||||
'value' => $statistic['commission_count']
|
||||
];
|
||||
}
|
||||
$result = array_reverse($result);
|
||||
return [
|
||||
'data' => $result
|
||||
];
|
||||
}
|
||||
|
||||
// 获取当日实时流量排行
|
||||
public function getServerLastRank()
|
||||
{
|
||||
$servers = [
|
||||
'shadowsocks' => ServerShadowsocks::with(['parent'])->get()->toArray(),
|
||||
'v2ray' => ServerVmess::with(['parent'])->get()->toArray(),
|
||||
'trojan' => ServerTrojan::with(['parent'])->get()->toArray(),
|
||||
'vmess' => ServerVmess::with(['parent'])->get()->toArray(),
|
||||
'hysteria' => ServerHysteria::with(['parent'])->get()->toArray(),
|
||||
'vless' => ServerVless::with(['parent'])->get()->toArray(),
|
||||
];
|
||||
|
||||
$recordAt = strtotime(date('Y-m-d'));
|
||||
$statService = new StatisticalService();
|
||||
$statService->setStartAt($recordAt);
|
||||
$stats = $statService->getStatServer();
|
||||
$statistics = collect($stats)->map(function ($item){
|
||||
$item['total'] = $item['u'] + $item['d'];
|
||||
return $item;
|
||||
})->sortByDesc('total')->values()->all();
|
||||
foreach ($statistics as $k => $v) {
|
||||
foreach ($servers[$v['server_type']] as $server) {
|
||||
if ($server['id'] === $v['server_id']) {
|
||||
$statistics[$k]['server_name'] = $server['name'];
|
||||
if($server['parent']) $statistics[$k]['server_name'] .= "({$server['parent']['name']})";
|
||||
}
|
||||
}
|
||||
$statistics[$k]['total'] = $statistics[$k]['total'] / 1073741824;
|
||||
}
|
||||
array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
|
||||
return [
|
||||
'data' => collect($statistics)->take(15)->all()
|
||||
];
|
||||
}
|
||||
// 获取昨日节点流量排行
|
||||
public function getServerYesterdayRank()
|
||||
{
|
||||
$servers = [
|
||||
'shadowsocks' => ServerShadowsocks::with(['parent'])->get()->toArray(),
|
||||
'v2ray' => ServerVmess::with(['parent'])->get()->toArray(),
|
||||
'trojan' => ServerTrojan::with(['parent'])->get()->toArray(),
|
||||
'vmess' => ServerVmess::with(['parent'])->get()->toArray(),
|
||||
'hysteria' => ServerHysteria::with(['parent'])->get()->toArray(),
|
||||
'vless' => ServerVless::with(['parent'])->get()->toArray(),
|
||||
];
|
||||
$startAt = strtotime('-1 day', strtotime(date('Y-m-d')));
|
||||
$endAt = strtotime(date('Y-m-d'));
|
||||
$statistics = StatServer::select([
|
||||
'server_id',
|
||||
'server_type',
|
||||
'u',
|
||||
'd',
|
||||
DB::raw('(u+d) as total')
|
||||
])
|
||||
->where('record_at', '>=', $startAt)
|
||||
->where('record_at', '<', $endAt)
|
||||
->where('record_type', 'd')
|
||||
->limit(15)
|
||||
->orderBy('total', 'DESC')
|
||||
->get()
|
||||
->toArray();
|
||||
foreach ($statistics as $k => $v) {
|
||||
foreach ($servers[$v['server_type']] as $server) {
|
||||
if ($server['id'] === $v['server_id']) {
|
||||
$statistics[$k]['server_name'] = $server['name'];
|
||||
if($server['parent']) $statistics[$k]['server_name'] .= "({$server['parent']['name']})";
|
||||
}
|
||||
}
|
||||
$statistics[$k]['total'] = $statistics[$k]['total'] / 1073741824;
|
||||
}
|
||||
array_multisort(array_column($statistics, 'total'), SORT_DESC, $statistics);
|
||||
return [
|
||||
'data' => $statistics
|
||||
];
|
||||
}
|
||||
|
||||
public function getStatUser(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|integer'
|
||||
]);
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$builder = StatUser::orderBy('record_at', 'DESC')->where('user_id', $request->input('user_id'));
|
||||
|
||||
$total = $builder->count();
|
||||
$records = $builder->forPage($current, $pageSize)
|
||||
->get();
|
||||
|
||||
// 追加当天流量
|
||||
$recordAt = strtotime(date('Y-m-d'));
|
||||
$statService = new StatisticalService();
|
||||
$statService->setStartAt($recordAt);
|
||||
$todayTraffics = $statService->getStatUserByUserID($request->input('user_id'));
|
||||
if (($current == 1) && count($todayTraffics) > 0) {
|
||||
foreach ($todayTraffics as $todayTraffic){
|
||||
$todayTraffic['server_rate'] = number_format($todayTraffic['server_rate'], 2);
|
||||
$records->prepend($todayTraffic);
|
||||
}
|
||||
};
|
||||
|
||||
return [
|
||||
'data' => $records,
|
||||
'total' => $total + count($todayTraffics),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ThemeService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class ThemeController extends Controller
|
||||
{
|
||||
private $themes;
|
||||
private $path;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->path = $path = public_path('theme/');
|
||||
$this->themes = array_map(function ($item) use ($path) {
|
||||
return str_replace($path, '', $item);
|
||||
}, glob($path . '*'));
|
||||
}
|
||||
|
||||
public function getThemes()
|
||||
{
|
||||
$themeConfigs = [];
|
||||
foreach ($this->themes as $theme) {
|
||||
$themeConfigFile = $this->path . "{$theme}/config.json";
|
||||
if (!File::exists($themeConfigFile)) continue;
|
||||
$themeConfig = json_decode(File::get($themeConfigFile), true);
|
||||
if (!isset($themeConfig['configs']) || !is_array($themeConfig)) continue;
|
||||
$themeConfigs[$theme] = $themeConfig;
|
||||
if (admin_setting("theme_{$theme}")) continue;
|
||||
$themeService = new ThemeService($theme);
|
||||
$themeService->init();
|
||||
}
|
||||
$data = [
|
||||
'themes' => $themeConfigs,
|
||||
'active' => admin_setting('frontend_theme', 'Xboard')
|
||||
];
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
public function getThemeConfig(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required|in:' . join(',', $this->themes)
|
||||
]);
|
||||
return $this->success(admin_setting("theme_{$payload['name']}") ?? config("theme.{$payload['name']}"));
|
||||
}
|
||||
|
||||
public function saveThemeConfig(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required|in:' . join(',', $this->themes),
|
||||
'config' => 'required'
|
||||
]);
|
||||
$payload['config'] = json_decode(base64_decode($payload['config']), true);
|
||||
if (!$payload['config'] || !is_array($payload['config'])) return $this->fail([422,'参数不正确']);
|
||||
$themeConfigFile = public_path("theme/{$payload['name']}/config.json");
|
||||
if (!File::exists($themeConfigFile)) return $this->fail([400202,'主题不存在']);
|
||||
$themeConfig = json_decode(File::get($themeConfigFile), true);
|
||||
if (!isset($themeConfig['configs']) || !is_array($themeConfig)) return $this->fail([422,'主题配置文件有误']);
|
||||
$validateFields = array_column($themeConfig['configs'], 'field_name');
|
||||
$config = [];
|
||||
foreach ($validateFields as $validateField) {
|
||||
$config[$validateField] = isset($payload['config'][$validateField]) ? $payload['config'][$validateField] : '';
|
||||
}
|
||||
|
||||
File::ensureDirectoryExists(base_path() . '/config/theme/');
|
||||
// $data = var_export($config, 1);
|
||||
|
||||
try {
|
||||
admin_setting(["theme_{$payload['name']}" => $config]);
|
||||
// sleep(2);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail([200002, '保存失败']);
|
||||
}
|
||||
return $this->success($config);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\TicketMessage;
|
||||
use App\Models\User;
|
||||
use App\Services\TicketService;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
return $this->fail([400202,'工单不存在']);
|
||||
}
|
||||
$ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
|
||||
for ($i = 0; $i < count($ticket['message']); $i++) {
|
||||
if ($ticket['message'][$i]['user_id'] !== $ticket->user_id) {
|
||||
$ticket['message'][$i]['is_me'] = true;
|
||||
} else {
|
||||
$ticket['message'][$i]['is_me'] = false;
|
||||
}
|
||||
}
|
||||
return $this->success($ticket);
|
||||
}
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$model = Ticket::orderBy('updated_at', 'DESC');
|
||||
if ($request->input('status') !== NULL) {
|
||||
$model->where('status', $request->input('status'));
|
||||
}
|
||||
if ($request->input('reply_status') !== NULL) {
|
||||
$model->whereIn('reply_status', $request->input('reply_status'));
|
||||
}
|
||||
if ($request->input('email') !== NULL) {
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
if ($user) $model->where('user_id', $user->id);
|
||||
}
|
||||
$total = $model->count();
|
||||
$res = $model->forPage($current, $pageSize)
|
||||
->get();
|
||||
return response([
|
||||
'data' => $res,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
public function reply(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric',
|
||||
'message' => 'required|string'
|
||||
],[
|
||||
'id.required' => '工单ID不能为空',
|
||||
'message.required' => '消息不能为空'
|
||||
]);
|
||||
$ticketService = new TicketService();
|
||||
$ticketService->replyByAdmin(
|
||||
$request->input('id'),
|
||||
$request->input('message'),
|
||||
$request->user['id']
|
||||
);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function close(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
],[
|
||||
'id.required' => '工单ID不能为空'
|
||||
]);
|
||||
try {
|
||||
$ticket = Ticket::findOrFail($request->input('id'));
|
||||
$ticket->status = Ticket::STATUS_CLOSED;
|
||||
$ticket->save();
|
||||
return $this->success(true);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->fail([400202, '工单不存在']);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail([500101, '关闭失败']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ class AppController extends Controller
|
||||
public function getConfig(Request $request)
|
||||
{
|
||||
$servers = [];
|
||||
$user = $request->user;
|
||||
$user = $request->user();
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
$servers = ServerService::getAvailableServers($user);
|
||||
@@ -30,8 +30,9 @@ class AppController extends Controller
|
||||
$proxies = [];
|
||||
|
||||
foreach ($servers as $item) {
|
||||
$protocol_settings = $item['protocol_settings'];
|
||||
if ($item['type'] === 'shadowsocks'
|
||||
&& in_array($item['cipher'], [
|
||||
&& in_array(data_get($protocol_settings, 'cipher'), [
|
||||
'aes-128-gcm',
|
||||
'aes-192-gcm',
|
||||
'aes-256-gcm',
|
||||
|
||||
@@ -8,12 +8,27 @@ use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class ClientController extends Controller
|
||||
{
|
||||
/**
|
||||
* Protocol prefix mapping for server names
|
||||
*/
|
||||
private const PROTOCOL_PREFIXES = [
|
||||
'hysteria' => [
|
||||
1 => '[Hy]',
|
||||
2 => '[Hy2]'
|
||||
],
|
||||
'vless' => '[vless]',
|
||||
'shadowsocks' => '[ss]',
|
||||
'vmess' => '[vmess]',
|
||||
'trojan' => '[trojan]',
|
||||
];
|
||||
|
||||
// 支持hy2 的客户端版本列表
|
||||
const SupportedHy2ClientVersions = [
|
||||
private const CLIENT_VERSIONS = [
|
||||
'NekoBox' => '1.2.7',
|
||||
'sing-box' => '1.5.0',
|
||||
'stash' => '2.5.0',
|
||||
@@ -26,121 +41,191 @@ class ClientController extends Controller
|
||||
'loon' => '637',
|
||||
'v2rayng' => '1.9.5',
|
||||
'v2rayN' => '6.31',
|
||||
'surge' => '2398'
|
||||
'surge' => '2398',
|
||||
'flclash' => '0.8.0'
|
||||
];
|
||||
// allowed types
|
||||
const AllowedTypes = ['vmess', 'vless', 'trojan', 'hysteria', 'shadowsocks', 'hysteria2'];
|
||||
|
||||
private const ALLOWED_TYPES = ['vmess', 'vless', 'trojan', 'hysteria', 'shadowsocks', 'hysteria2'];
|
||||
|
||||
/**
|
||||
* 处理浏览器访问订阅的情况
|
||||
*/
|
||||
private function handleBrowserSubscribe($user, UserService $userService)
|
||||
{
|
||||
$useTraffic = $user['u'] + $user['d'];
|
||||
$totalTraffic = $user['transfer_enable'];
|
||||
$remainingTraffic = Helper::trafficConvert($totalTraffic - $useTraffic);
|
||||
$expiredDate = $user['expired_at'] ? date('Y-m-d', $user['expired_at']) : __('Unlimited');
|
||||
$resetDay = $userService->getResetDay($user);
|
||||
|
||||
// 获取通用订阅地址
|
||||
$subscriptionUrl = Helper::getSubscribeUrl($user->token);
|
||||
|
||||
// 生成二维码
|
||||
$writer = new \BaconQrCode\Writer(
|
||||
new \BaconQrCode\Renderer\ImageRenderer(
|
||||
new \BaconQrCode\Renderer\RendererStyle\RendererStyle(200),
|
||||
new \BaconQrCode\Renderer\Image\SvgImageBackEnd()
|
||||
)
|
||||
);
|
||||
$qrCode = base64_encode($writer->writeString($subscriptionUrl));
|
||||
|
||||
$data = [
|
||||
'username' => $user->email,
|
||||
'status' => $userService->isAvailable($user) ? 'active' : 'inactive',
|
||||
'data_limit' => $totalTraffic ? Helper::trafficConvert($totalTraffic) : '∞',
|
||||
'data_used' => Helper::trafficConvert($useTraffic),
|
||||
'expired_date' => $expiredDate,
|
||||
'reset_day' => $resetDay,
|
||||
'subscription_url' => $subscriptionUrl,
|
||||
'qr_code' => $qrCode
|
||||
];
|
||||
|
||||
// 只有当 device_limit 不为 null 时才添加到返回数据中
|
||||
if ($user->device_limit !== null) {
|
||||
$data['device_limit'] = $user->device_limit;
|
||||
}
|
||||
|
||||
return response()->view('client.subscribe', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否是浏览器访问
|
||||
*/
|
||||
private function isBrowserAccess(Request $request): bool
|
||||
{
|
||||
$userAgent = strtolower($request->input('flag', $request->header('User-Agent')));
|
||||
return str_contains($userAgent, 'mozilla')
|
||||
|| str_contains($userAgent, 'chrome')
|
||||
|| str_contains($userAgent, 'safari')
|
||||
|| str_contains($userAgent, 'edge');
|
||||
}
|
||||
|
||||
public function subscribe(Request $request)
|
||||
{
|
||||
// filter types
|
||||
$types = $request->input('types', 'all');
|
||||
$typesArr = $types === 'all' ? self::AllowedTypes : array_values(array_intersect(explode('|', str_replace(['|', '|', ','], "|", $types)), self::AllowedTypes));
|
||||
// filter keyword
|
||||
$filterArr = mb_strlen($filter = $request->input('filter')) > 20 ? null : explode("|", str_replace(['|', '|', ','], "|", $filter));
|
||||
$flag = strtolower($request->input('flag') ?? $request->header('User-Agent', ''));
|
||||
$ip = $request->input('ip', $request->ip());
|
||||
// get client version
|
||||
$version = preg_match('/\/v?(\d+(\.\d+){0,2})/', $flag, $matches) ? $matches[1] : null;
|
||||
$supportHy2 = $version ? collect(self::SupportedHy2ClientVersions)
|
||||
->contains(fn($minVersion, $client) => stripos($flag, $client) !== false && $this->versionCompare($version, $minVersion)) : true;
|
||||
$user = $request->user;
|
||||
// account not expired and is not banned.
|
||||
$request->validate([
|
||||
'types' => ['nullable', 'string'],
|
||||
'filter' => ['nullable', 'string'],
|
||||
'flag' => ['nullable', 'string'],
|
||||
]);
|
||||
|
||||
$user = $request->user();
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
// get ip location
|
||||
$ip2region = new \Ip2Region();
|
||||
$region = filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) ? ($ip2region->memorySearch($ip)['region'] ?? null) : null;
|
||||
// get available servers
|
||||
$servers = ServerService::getAvailableServers($user);
|
||||
// filter servers
|
||||
$serversFiltered = $this->serverFilter($servers, $typesArr, $filterArr, $region, $supportHy2);
|
||||
$this->setSubscribeInfoToServers($serversFiltered, $user, count($servers) - count($serversFiltered));
|
||||
$servers = $serversFiltered;
|
||||
$this->addPrefixToServerName($servers);
|
||||
if ($flag) {
|
||||
foreach (array_reverse(glob(app_path('Protocols') . '/*.php')) as $file) {
|
||||
$file = 'App\\Protocols\\' . basename($file, '.php');
|
||||
$class = new $file($user, $servers);
|
||||
$classFlags = explode(',', $class->flag);
|
||||
foreach ($classFlags as $classFlag) {
|
||||
if (stripos($flag, $classFlag) !== false) {
|
||||
return $class->handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$class = new General($user, $servers);
|
||||
return $class->handle();
|
||||
|
||||
if (!$userService->isAvailable($user)) {
|
||||
return response()->json(['message' => 'Account unavailable'], 403);
|
||||
}
|
||||
|
||||
// 检测是否是浏览器访问
|
||||
if ($this->isBrowserAccess($request)) {
|
||||
return $this->handleBrowserSubscribe($user, $userService);
|
||||
}
|
||||
$clientInfo = $this->getClientInfo($request);
|
||||
$types = $this->getFilteredTypes($request->input('types'), $clientInfo['supportHy2']);
|
||||
$filterArr = $this->getFilterArray($request->input('filter'));
|
||||
// Get available servers and apply filters
|
||||
$servers = ServerService::getAvailableServers($user);
|
||||
$serversFiltered = $this->filterServers(
|
||||
servers: $servers,
|
||||
types: $types,
|
||||
filters: $filterArr,
|
||||
);
|
||||
|
||||
$this->setSubscribeInfoToServers($serversFiltered, $user, count($servers) - count($serversFiltered));
|
||||
$serversFiltered = $this->addPrefixToServerName($serversFiltered);
|
||||
|
||||
// Handle protocol response
|
||||
if ($clientInfo['flag']) {
|
||||
foreach (array_reverse(glob(app_path('Protocols') . '/*.php')) as $file) {
|
||||
$className = 'App\\Protocols\\' . basename($file, '.php');
|
||||
$protocol = new $className($user, $serversFiltered);
|
||||
if (
|
||||
collect($protocol->getFlags())
|
||||
->contains(fn($f) => stripos($clientInfo['flag'], $f) !== false)
|
||||
) {
|
||||
return $protocol->handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (new General($user, $serversFiltered))->handle();
|
||||
}
|
||||
/**
|
||||
* Summary of serverFilter
|
||||
* @param mixed $typesArr
|
||||
* @param mixed $filterArr
|
||||
* @param mixed $region
|
||||
* @param mixed $supportHy2
|
||||
* @return array
|
||||
*/
|
||||
private function serverFilter($servers, $typesArr, $filterArr, $region, $supportHy2)
|
||||
|
||||
private function getFilteredTypes(string|null $types, bool $supportHy2): array
|
||||
{
|
||||
return collect($servers)->reject(function ($server) use ($typesArr, $filterArr, $region, $supportHy2) {
|
||||
if ($server['type'] == "hysteria" && $server['version'] == 2) {
|
||||
if(!in_array('hysteria2', $typesArr)){
|
||||
if ($types === 'all') {
|
||||
return self::ALLOWED_TYPES;
|
||||
}
|
||||
|
||||
$allowedTypes = $supportHy2
|
||||
? self::ALLOWED_TYPES
|
||||
: array_diff(self::ALLOWED_TYPES, ['hysteria2']);
|
||||
if (!$types) {
|
||||
return array_values($allowedTypes);
|
||||
}
|
||||
|
||||
$userTypes = explode('|', str_replace(['|', '|', ','], '|', $types));
|
||||
return array_values(array_intersect($userTypes, $allowedTypes));
|
||||
}
|
||||
|
||||
private function getFilterArray(?string $filter): ?array
|
||||
{
|
||||
if ($filter === null) {
|
||||
return null;
|
||||
}
|
||||
return mb_strlen($filter) > 20 ? null :
|
||||
explode('|', str_replace(['|', '|', ','], '|', $filter));
|
||||
}
|
||||
|
||||
private function getClientInfo(Request $request): array
|
||||
{
|
||||
$flag = strtolower($request->input('flag') ?? $request->header('User-Agent', ''));
|
||||
preg_match('/\/v?(\d+(\.\d+){0,2})/', $flag, $matches);
|
||||
$version = $matches[1] ?? null;
|
||||
|
||||
$supportHy2 = $version ? $this->checkHy2Support($flag, $version) : true;
|
||||
|
||||
return [
|
||||
'flag' => $flag,
|
||||
'version' => $version,
|
||||
'supportHy2' => $supportHy2
|
||||
];
|
||||
}
|
||||
|
||||
private function checkHy2Support(string $flag, string $version): bool
|
||||
{
|
||||
$result = false;
|
||||
foreach (self::CLIENT_VERSIONS as $client => $minVersion) {
|
||||
if (stripos($flag, $client) !== false) {
|
||||
$result = $result || version_compare($version, $minVersion, '>=');
|
||||
}
|
||||
}
|
||||
return $result || !count(self::CLIENT_VERSIONS);
|
||||
}
|
||||
|
||||
private function filterServers(array $servers, array $types, ?array $filters): array
|
||||
{
|
||||
return collect($servers)->reject(function ($server) use ($types, $filters) {
|
||||
// Check Hysteria2 compatibility
|
||||
if ($server['type'] === 'hysteria' && optional($server['protocol_settings'])['version'] === 2) {
|
||||
if (!in_array('hysteria2', $types)) {
|
||||
return true;
|
||||
}elseif(false == $supportHy2){
|
||||
}
|
||||
} else {
|
||||
if (!in_array($server['type'], $types)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($filterArr) {
|
||||
foreach ($filterArr as $filter) {
|
||||
if (stripos($server['name'], $filter) !== false || in_array($filter, $server['tags'] ?? [])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strpos($region, '中国') !== false) {
|
||||
$excludes = $server['excludes'] ?? [];
|
||||
if (empty($excludes)) {
|
||||
return false;
|
||||
}
|
||||
foreach ($excludes as $v) {
|
||||
$excludeList = explode("|", str_replace(["|", ",", " ", ","], "|", $v));
|
||||
foreach ($excludeList as $needle) {
|
||||
if (stripos($region, $needle) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Apply custom filters
|
||||
if ($filters) {
|
||||
return !collect($filters)->contains(function ($filter) use ($server) {
|
||||
return stripos($server['name'], $filter) !== false
|
||||
|| in_array($filter, $server['tags'] ?? []);
|
||||
});
|
||||
}
|
||||
return false;
|
||||
})->values()->all();
|
||||
}
|
||||
/*
|
||||
* add prefix to server name
|
||||
*/
|
||||
private function addPrefixToServerName(&$servers)
|
||||
{
|
||||
// 线路名称增加协议类型
|
||||
if (admin_setting('show_protocol_to_server_enable')) {
|
||||
$typePrefixes = [
|
||||
'hysteria' => [1 => '[Hy]', 2 => '[Hy2]'],
|
||||
'vless' => '[vless]',
|
||||
'shadowsocks' => '[ss]',
|
||||
'vmess' => '[vmess]',
|
||||
'trojan' => '[trojan]',
|
||||
];
|
||||
$servers = collect($servers)->map(function ($server) use ($typePrefixes) {
|
||||
if (isset($typePrefixes[$server['type']])) {
|
||||
$prefix = is_array($typePrefixes[$server['type']]) ? $typePrefixes[$server['type']][$server['version']] : $typePrefixes[$server['type']];
|
||||
$server['name'] = $prefix . $server['name'];
|
||||
}
|
||||
return $server;
|
||||
})->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of setSubscribeInfoToServers
|
||||
@@ -163,7 +248,7 @@ class ClientController extends Controller
|
||||
$useTraffic = $user['u'] + $user['d'];
|
||||
$totalTraffic = $user['transfer_enable'];
|
||||
$remainingTraffic = Helper::trafficConvert($totalTraffic - $useTraffic);
|
||||
$expiredDate = $user['expired_at'] ? date('Y-m-d', $user['expired_at']) : '长期有效';
|
||||
$expiredDate = $user['expired_at'] ? date('Y-m-d', $user['expired_at']) : __('长期有效');
|
||||
$userService = new UserService();
|
||||
$resetDay = $userService->getResetDay($user);
|
||||
array_unshift($servers, array_merge($servers[0], [
|
||||
@@ -179,33 +264,41 @@ class ClientController extends Controller
|
||||
]));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断版本号
|
||||
* Add protocol prefix to server names if enabled in admin settings
|
||||
*
|
||||
* @param array<int, array<string, mixed>> $servers
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
|
||||
function versionCompare($version1, $version2)
|
||||
private function addPrefixToServerName(array $servers): array
|
||||
{
|
||||
if (!preg_match('/^\d+(\.\d+){0,2}/', $version1) || !preg_match('/^\d+(\.\d+){0,2}/', $version2)) {
|
||||
return false;
|
||||
}
|
||||
$v1Parts = explode('.', $version1);
|
||||
$v2Parts = explode('.', $version2);
|
||||
|
||||
$maxParts = max(count($v1Parts), count($v2Parts));
|
||||
|
||||
for ($i = 0; $i < $maxParts; $i++) {
|
||||
$part1 = isset($v1Parts[$i]) ? (int) $v1Parts[$i] : 0;
|
||||
$part2 = isset($v2Parts[$i]) ? (int) $v2Parts[$i] : 0;
|
||||
|
||||
if ($part1 < $part2) {
|
||||
return false;
|
||||
} elseif ($part1 > $part2) {
|
||||
return true;
|
||||
}
|
||||
if (!admin_setting('show_protocol_to_server_enable', false)) {
|
||||
return $servers;
|
||||
}
|
||||
|
||||
// 版本号相等
|
||||
return true;
|
||||
return collect($servers)
|
||||
->map(function (array $server): array {
|
||||
$server['name'] = $this->getPrefixedServerName($server);
|
||||
return $server;
|
||||
})
|
||||
->all();
|
||||
}
|
||||
/**
|
||||
* Get server name with protocol prefix
|
||||
*
|
||||
* @param array<string, mixed> $server
|
||||
*/
|
||||
private function getPrefixedServerName(array $server): string
|
||||
{
|
||||
$type = $server['type'] ?? '';
|
||||
if (!isset(self::PROTOCOL_PREFIXES[$type])) {
|
||||
return $server['name'] ?? '';
|
||||
}
|
||||
|
||||
$prefix = is_array(self::PROTOCOL_PREFIXES[$type])
|
||||
? self::PROTOCOL_PREFIXES[$type][$server['protocol_settings']['version'] ?? 1] ?? ''
|
||||
: self::PROTOCOL_PREFIXES[$type];
|
||||
|
||||
return $prefix . ($server['name'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,23 @@
|
||||
namespace App\Http\Controllers\V1\Guest;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PlanResource;
|
||||
use App\Models\Plan;
|
||||
use App\Services\PlanService;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
|
||||
protected $planService;
|
||||
public function __construct(PlanService $planService)
|
||||
{
|
||||
$this->planService = $planService;
|
||||
}
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$plan = Plan::where('show', 1)->get();
|
||||
return $this->success($plan);
|
||||
$plan = $this->planService->getAvailablePlans();
|
||||
return $this->success(PlanResource::collection($plan));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class TelegramController extends Controller
|
||||
if ($request->input('access_token') !== md5(admin_setting('telegram_bot_token'))) {
|
||||
throw new ApiException('access_token is error', 401);
|
||||
}
|
||||
$data = json_decode(get_request_content(),true);
|
||||
$data = json_decode(request()->getContent(),true);
|
||||
$this->formatMessage($data);
|
||||
$this->formatChatJoinRequest($data);
|
||||
$this->handle();
|
||||
|
||||
@@ -180,7 +180,7 @@ class AuthController extends Controller
|
||||
|
||||
$authService = new AuthService($user);
|
||||
|
||||
$data = $authService->generateAuthData($request);
|
||||
$data = $authService->generateAuthData();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
@@ -223,48 +223,70 @@ class AuthController extends Controller
|
||||
}
|
||||
|
||||
$authService = new AuthService($user);
|
||||
return $this->success($authService->generateAuthData($request));
|
||||
return $this->success($authService->generateAuthData());
|
||||
}
|
||||
|
||||
public function token2Login(Request $request)
|
||||
{
|
||||
if ($request->input('token')) {
|
||||
$redirect = '/#/login?verify=' . $request->input('token') . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
|
||||
if (admin_setting('app_url')) {
|
||||
$location = admin_setting('app_url') . $redirect;
|
||||
} else {
|
||||
$location = url($redirect);
|
||||
}
|
||||
return redirect()->to($location)->send();
|
||||
if ($token = $request->input('token')) {
|
||||
$redirect = '/#/login?verify=' . $token . '&redirect=' . ($request->input('redirect', 'dashboard'));
|
||||
|
||||
return redirect()->to(
|
||||
admin_setting('app_url')
|
||||
? admin_setting('app_url') . $redirect
|
||||
: url($redirect)
|
||||
);
|
||||
}
|
||||
|
||||
if ($request->input('verify')) {
|
||||
$key = CacheKey::get('TEMP_TOKEN', $request->input('verify'));
|
||||
if ($verify = $request->input('verify')) {
|
||||
$key = CacheKey::get('TEMP_TOKEN', $verify);
|
||||
$userId = Cache::get($key);
|
||||
|
||||
if (!$userId) {
|
||||
return $this->fail([400,__('Token error')]);
|
||||
}
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
return $this->fail([400,__('The user does not ')]);
|
||||
return response()->json([
|
||||
'message' => __('Token error')
|
||||
], 400);
|
||||
}
|
||||
|
||||
$user = User::findOrFail($userId);
|
||||
|
||||
if ($user->banned) {
|
||||
return $this->fail([400,__('Your account has been suspended')]);
|
||||
return response()->json([
|
||||
'message' => __('Your account has been suspended')
|
||||
], 400);
|
||||
}
|
||||
|
||||
Cache::forget($key);
|
||||
$authService = new AuthService($user);
|
||||
return $this->success($authService->generateAuthData($request));
|
||||
|
||||
return response()->json([
|
||||
'data' => $authService->generateAuthData()
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'message' => __('Invalid request')
|
||||
], 400);
|
||||
}
|
||||
|
||||
public function getQuickLoginUrl(Request $request)
|
||||
{
|
||||
$authorization = $request->input('auth_data') ?? $request->header('authorization');
|
||||
if (!$authorization) return $this->fail(ResponseEnum::CLIENT_HTTP_UNAUTHORIZED);
|
||||
|
||||
$user = AuthService::decryptAuthData($authorization);
|
||||
if (!$user) return $this->fail(ResponseEnum::CLIENT_HTTP_UNAUTHORIZED_EXPIRED);
|
||||
|
||||
if (!$authorization) {
|
||||
return response()->json([
|
||||
'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED
|
||||
], 401);
|
||||
}
|
||||
|
||||
$user = AuthService::findUserByBearerToken($authorization);
|
||||
|
||||
if (!$user) {
|
||||
return response()->json([
|
||||
'message' => ResponseEnum::CLIENT_HTTP_UNAUTHORIZED_EXPIRED
|
||||
], 401);
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
$key = CacheKey::get('TEMP_TOKEN', $code);
|
||||
Cache::put($key, $user['id'], 60);
|
||||
|
||||
@@ -1,222 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerVmess;
|
||||
use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/*
|
||||
* V2ray Aurora
|
||||
* Github: https://github.com/tokumeikoi/aurora
|
||||
*/
|
||||
class DeepbworkController extends Controller
|
||||
{
|
||||
CONST V2RAY_CONFIG = '{"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"api":{"services":["HandlerService","StatsService"],"tag":"api"},"dns":{},"stats":{},"inbounds":[{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled":true,"destOverride":["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},{"listen":"127.0.0.1","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"outbounds":[{"protocol":"freedom","settings":{}},{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"type":"field","inboundTag":"api","outboundTag":"api"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
|
||||
|
||||
// 后端获取用户
|
||||
public function user(Request $request)
|
||||
{
|
||||
ini_set('memory_limit', -1);
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = ServerVmess::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->fail([400,'节点不存在']);
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_VMESS_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$users = ServerService::getAvailableUsers($server->group_id);
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->v2ray_user = [
|
||||
"uuid" => $user->uuid,
|
||||
"email" => sprintf("%s@v2board.user", $user->uuid),
|
||||
"alter_id" => 0,
|
||||
"level" => 0,
|
||||
];
|
||||
unset($user->uuid);
|
||||
array_push($result, $user);
|
||||
}
|
||||
$eTag = sha1(json_encode($result));
|
||||
if (strpos($request->header('If-None-Match'), $eTag) !== false ) {
|
||||
return response(null,304);
|
||||
}
|
||||
return response([
|
||||
'msg' => 'ok',
|
||||
'data' => $result,
|
||||
])->header('ETag', "\"{$eTag}\"");
|
||||
}
|
||||
|
||||
// 后端提交数据
|
||||
public function submit(Request $request)
|
||||
{
|
||||
$server = ServerVmess::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'server is not found'
|
||||
]);
|
||||
}
|
||||
$data = get_request_content();
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_VMESS_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_VMESS_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
$formatData = [];
|
||||
|
||||
foreach ($data as $item) {
|
||||
$formatData[$item['user_id']] = [$item['u'], $item['d']];
|
||||
}
|
||||
$userService->trafficFetch($server->toArray(), 'vmess', $formatData);
|
||||
|
||||
return response([
|
||||
'ret' => 1,
|
||||
'msg' => 'ok'
|
||||
]);
|
||||
}
|
||||
|
||||
// 后端获取配置
|
||||
public function config(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'node_id' => 'required',
|
||||
'local_port' => 'required'
|
||||
],[
|
||||
'node_id.required' => '节点ID不能为空',
|
||||
'local_port.required' => '本地端口不能为空'
|
||||
]);
|
||||
try {
|
||||
$json = $this->getV2RayConfig($request->input('node_id'), $request->input('local_port'));
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
throw new ApiException($e->getMessage());
|
||||
}
|
||||
|
||||
return(json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
private function getV2RayConfig(int $nodeId, int $localPort)
|
||||
{
|
||||
$server = ServerVmess::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->fail([400,'节点不存在']);
|
||||
}
|
||||
$json = json_decode(self::V2RAY_CONFIG);
|
||||
$json->log->loglevel = (int)admin_setting('server_log_enable') ? 'debug' : 'none';
|
||||
$json->inbounds[1]->port = (int)$localPort;
|
||||
$json->inbounds[0]->port = (int)$server->server_port;
|
||||
$json->inbounds[0]->streamSettings->network = $server->network;
|
||||
$this->setDns($server, $json);
|
||||
$this->setNetwork($server, $json);
|
||||
$this->setRule($server, $json);
|
||||
$this->setTls($server, $json);
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
private function setDns(ServerVmess $server, object $json)
|
||||
{
|
||||
if ($server->dnsSettings) {
|
||||
$dns = $server->dnsSettings;
|
||||
if (isset($dns->servers)) {
|
||||
array_push($dns->servers, '1.1.1.1');
|
||||
array_push($dns->servers, 'localhost');
|
||||
}
|
||||
$json->dns = $dns;
|
||||
$json->outbounds[0]->settings->domainStrategy = 'UseIP';
|
||||
}
|
||||
}
|
||||
|
||||
private function setNetwork(ServerVmess $server, object $json)
|
||||
{
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp':
|
||||
$json->inbounds[0]->streamSettings->tcpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'kcp':
|
||||
$json->inbounds[0]->streamSettings->kcpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'ws':
|
||||
$json->inbounds[0]->streamSettings->wsSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'http':
|
||||
$json->inbounds[0]->streamSettings->httpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'domainsocket':
|
||||
$json->inbounds[0]->streamSettings->dsSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'quic':
|
||||
$json->inbounds[0]->streamSettings->quicSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'grpc':
|
||||
$json->inbounds[0]->streamSettings->grpcSettings = $server->networkSettings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setRule(ServerVmess $server, object $json)
|
||||
{
|
||||
$domainRules = array_filter(explode(PHP_EOL, admin_setting('server_v2ray_domain')));
|
||||
$protocolRules = array_filter(explode(PHP_EOL, admin_setting('server_v2ray_protocol')));
|
||||
if ($server->ruleSettings) {
|
||||
$ruleSettings = $server->ruleSettings;
|
||||
// domain
|
||||
if (isset($ruleSettings->domain)) {
|
||||
$ruleSettings->domain = array_filter($ruleSettings->domain);
|
||||
if (!empty($ruleSettings->domain)) {
|
||||
$domainRules = array_merge($domainRules, $ruleSettings->domain);
|
||||
}
|
||||
}
|
||||
// protocol
|
||||
if (isset($ruleSettings->protocol)) {
|
||||
$ruleSettings->protocol = array_filter($ruleSettings->protocol);
|
||||
if (!empty($ruleSettings->protocol)) {
|
||||
$protocolRules = array_merge($protocolRules, $ruleSettings->protocol);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!empty($domainRules)) {
|
||||
$domainObj = new \StdClass();
|
||||
$domainObj->type = 'field';
|
||||
$domainObj->domain = $domainRules;
|
||||
$domainObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $domainObj);
|
||||
}
|
||||
if (!empty($protocolRules)) {
|
||||
$protocolObj = new \StdClass();
|
||||
$protocolObj->type = 'field';
|
||||
$protocolObj->protocol = $protocolRules;
|
||||
$protocolObj->outboundTag = 'block';
|
||||
array_push($json->routing->rules, $protocolObj);
|
||||
}
|
||||
if (empty($domainRules) && empty($protocolRules)) {
|
||||
$json->inbounds[0]->sniffing->enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function setTls(ServerVMess $server, object $json)
|
||||
{
|
||||
if ((int)$server->tls) {
|
||||
$tlsSettings = $server->tlsSettings;
|
||||
$json->inbounds[0]->streamSettings->security = 'tls';
|
||||
$tls = (object)[
|
||||
'certificateFile' => '/root/.cert/server.crt',
|
||||
'keyFile' => '/root/.cert/server.key'
|
||||
];
|
||||
$json->inbounds[0]->streamSettings->tlsSettings = new \StdClass();
|
||||
if (isset($tlsSettings->serverName)) {
|
||||
$json->inbounds[0]->streamSettings->tlsSettings->serverName = (string)$tlsSettings->serverName;
|
||||
}
|
||||
if (isset($tlsSettings->allowInsecure)) {
|
||||
$json->inbounds[0]->streamSettings->tlsSettings->allowInsecure = (int)$tlsSettings->allowInsecure ? true : false;
|
||||
}
|
||||
$json->inbounds[0]->streamSettings->tlsSettings->certificates[0] = $tls;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,9 @@ class ShadowsocksTidalabController extends Controller
|
||||
public function user(Request $request)
|
||||
{
|
||||
ini_set('memory_limit', -1);
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = ServerShadowsocks::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->fail([400,'节点不存在']);
|
||||
}
|
||||
$server = $request->input('node_info');
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$users = ServerService::getAvailableUsers($server->group_id);
|
||||
$users = ServerService::getAvailableUsers($server->group_ids);
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
array_push($result, [
|
||||
@@ -49,15 +45,8 @@ class ShadowsocksTidalabController extends Controller
|
||||
// 后端提交数据
|
||||
public function submit(Request $request)
|
||||
{
|
||||
$server = ServerShadowsocks::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'server is not found'
|
||||
]);
|
||||
}
|
||||
$data = get_request_content();
|
||||
$data = json_decode($data, true);
|
||||
$server = $request->input('node_info');
|
||||
$data = json_decode(request()->getContent(), true);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
@@ -73,4 +62,4 @@ class ShadowsocksTidalabController extends Controller
|
||||
'msg' => 'ok'
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,9 +23,8 @@ class TrojanTidalabController extends Controller
|
||||
public function user(Request $request)
|
||||
{
|
||||
ini_set('memory_limit', -1);
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = ServerTrojan::find($nodeId);
|
||||
if (!$server) {
|
||||
$server = $request->input('node_info');
|
||||
if ($server->type !== 'trojan') {
|
||||
return $this->fail([400, '节点不存在']);
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
@@ -51,15 +50,11 @@ class TrojanTidalabController extends Controller
|
||||
// 后端提交数据
|
||||
public function submit(Request $request)
|
||||
{
|
||||
$server = ServerTrojan::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'server is not found'
|
||||
]);
|
||||
$server = $request->input('node_info');
|
||||
if ($server->type !== 'trojan') {
|
||||
return $this->fail([400, '节点不存在']);
|
||||
}
|
||||
$data = get_request_content();
|
||||
$data = json_decode($data, true);
|
||||
$data = json_decode(request()->getContent(), true);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
@@ -78,6 +73,10 @@ class TrojanTidalabController extends Controller
|
||||
// 后端获取配置
|
||||
public function config(Request $request)
|
||||
{
|
||||
$server = $request->input('node_info');
|
||||
if ($server->type !== 'trojan') {
|
||||
return $this->fail([400, '节点不存在']);
|
||||
}
|
||||
$request->validate([
|
||||
'node_id' => 'required',
|
||||
'local_port' => 'required'
|
||||
@@ -86,7 +85,7 @@ class TrojanTidalabController extends Controller
|
||||
'local_port.required' => '本地端口不能为空'
|
||||
]);
|
||||
try {
|
||||
$json = $this->getTrojanConfig($request->input('node_id'), $request->input('local_port'));
|
||||
$json = $this->getTrojanConfig($server, $request->input('local_port'));
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '配置获取失败']);
|
||||
@@ -95,19 +94,15 @@ class TrojanTidalabController extends Controller
|
||||
return (json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
|
||||
private function getTrojanConfig(int $nodeId, int $localPort)
|
||||
private function getTrojanConfig($server, int $localPort)
|
||||
{
|
||||
$server = ServerTrojan::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->fail([400, '节点不存在']);
|
||||
}
|
||||
|
||||
$protocolSettings = $server->protocol_settings;
|
||||
$json = json_decode(self::TROJAN_CONFIG);
|
||||
$json->local_port = $server->server_port;
|
||||
$json->ssl->sni = $server->server_name ? $server->server_name : $server->host;
|
||||
$json->ssl->sni = data_get($protocolSettings, 'server_name', $server->host);
|
||||
$json->ssl->cert = "/root/.cert/server.crt";
|
||||
$json->ssl->key = "/root/.cert/server.key";
|
||||
$json->api->api_port = $localPort;
|
||||
return $json;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,22 +9,30 @@ use App\Utils\CacheKey;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\Services\UserOnlineService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class UniProxyController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
private readonly UserOnlineService $userOnlineService
|
||||
) {
|
||||
}
|
||||
|
||||
// 后端获取用户
|
||||
public function user(Request $request)
|
||||
{
|
||||
ini_set('memory_limit', -1);
|
||||
Cache::put(CacheKey::get('SERVER_' . strtoupper($request->input('node_type')) . '_LAST_CHECK_AT', $request->input('node_id')), time(), 3600);
|
||||
$users = ServerService::getAvailableUsers($request->input('node_info')->group_id)->toArray();
|
||||
$node = $request->input('node_info');
|
||||
$nodeType = $node->type;
|
||||
$nodeId = $node->id;
|
||||
Cache::put(CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_CHECK_AT', $nodeId), time(), 3600);
|
||||
$users = ServerService::getAvailableUsers($node->group_ids);
|
||||
|
||||
$response['users'] = $users;
|
||||
|
||||
$eTag = sha1(json_encode($response));
|
||||
if (strpos($request->header('If-None-Match'), $eTag) !== false) {
|
||||
if (strpos($request->header('If-None-Match', ''), $eTag) !== false) {
|
||||
return response(null, 304);
|
||||
}
|
||||
|
||||
@@ -34,131 +42,142 @@ class UniProxyController extends Controller
|
||||
// 后端提交数据
|
||||
public function push(Request $request)
|
||||
{
|
||||
$res = json_decode(get_request_content(), true);
|
||||
$data = array_filter($res, function ($item) {
|
||||
return is_array($item) && count($item) === 2 && is_numeric($item[0]) && is_numeric($item[1]);
|
||||
});
|
||||
$nodeType = $request->input('node_type');
|
||||
$nodeId = $request->input('node_id');
|
||||
// 增加单节点多服务器统计在线人数
|
||||
$ip = $request->ip();
|
||||
$id = $request->input("id");
|
||||
$time = time();
|
||||
$cacheKey = CacheKey::get('MULTI_SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId);
|
||||
|
||||
// 1、获取节点节点在线人数缓存
|
||||
$onlineUsers = Cache::get($cacheKey) ?? [];
|
||||
$onlineCollection = collect($onlineUsers);
|
||||
// 过滤掉超过600秒的记录
|
||||
$onlineCollection = $onlineCollection->reject(function ($item) use ($time) {
|
||||
return $item['time'] < ($time - 600);
|
||||
});
|
||||
// 定义数据
|
||||
$updatedItem = [
|
||||
'id' => $id ?? $ip,
|
||||
'ip' => $ip,
|
||||
'online_user' => count($data),
|
||||
'time' => $time
|
||||
];
|
||||
|
||||
$existingItemIndex = $onlineCollection->search(function ($item) use ($updatedItem) {
|
||||
return ($item['id'] ?? '') === $updatedItem['id'];
|
||||
});
|
||||
if ($existingItemIndex !== false) {
|
||||
$onlineCollection[$existingItemIndex] = $updatedItem;
|
||||
} else {
|
||||
$onlineCollection->push($updatedItem);
|
||||
$res = json_decode(request()->getContent(), true);
|
||||
if (!is_array($res)) {
|
||||
return $this->fail([422, 'Invalid data format']);
|
||||
}
|
||||
$onlineUsers = $onlineCollection->all();
|
||||
Cache::put($cacheKey, $onlineUsers, 3600);
|
||||
$data = array_filter($res, function ($item) {
|
||||
return is_array($item)
|
||||
&& count($item) === 2
|
||||
&& is_numeric($item[0])
|
||||
&& is_numeric($item[1]);
|
||||
});
|
||||
if (empty($data)) {
|
||||
return $this->success(true);
|
||||
}
|
||||
$node = $request->input('node_info');
|
||||
$nodeType = $node->type;
|
||||
$nodeId = $node->id;
|
||||
|
||||
Cache::put(
|
||||
CacheKey::get('SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId),
|
||||
count($data),
|
||||
3600
|
||||
);
|
||||
Cache::put(
|
||||
CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_PUSH_AT', $nodeId),
|
||||
time(),
|
||||
3600
|
||||
);
|
||||
|
||||
$online_user = $onlineCollection->sum('online_user');
|
||||
Cache::put(CacheKey::get('SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId), $online_user, 3600);
|
||||
Cache::put(CacheKey::get('SERVER_' . strtoupper($nodeType) . '_LAST_PUSH_AT', $nodeId), time(), 3600);
|
||||
$userService = new UserService();
|
||||
$userService->trafficFetch($request->input('node_info')->toArray(), $nodeType, $data, $ip);
|
||||
$userService->trafficFetch($node->toArray(), $nodeType, $data);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
// 后端获取配置
|
||||
public function config(Request $request)
|
||||
{
|
||||
$nodeType = $request->input('node_type');
|
||||
$nodeInfo = $request->input('node_info');
|
||||
switch ($nodeType) {
|
||||
case 'shadowsocks':
|
||||
$response = [
|
||||
'server_port' => $nodeInfo->server_port,
|
||||
'cipher' => $nodeInfo->cipher,
|
||||
'obfs' => $nodeInfo->obfs,
|
||||
'obfs_settings' => $nodeInfo->obfs_settings
|
||||
];
|
||||
$node = $request->input('node_info');
|
||||
$nodeType = $node->type;
|
||||
$protocolSettings = $node->protocol_settings;
|
||||
|
||||
$serverPort = $node->server_port;
|
||||
$host = $node->host;
|
||||
|
||||
$baseConfig = [
|
||||
'server_port' => (int) $serverPort,
|
||||
'network' => data_get($protocolSettings, 'network'),
|
||||
'networkSettings' => data_get($protocolSettings, 'network_settings') ?: null,
|
||||
];
|
||||
|
||||
$response = match ($nodeType) {
|
||||
'shadowsocks' => [
|
||||
...$baseConfig,
|
||||
'cipher' => $protocolSettings['cipher'],
|
||||
'obfs' => $protocolSettings['obfs'],
|
||||
'obfs_settings' => $protocolSettings['obfs_settings'],
|
||||
'server_key' => match ($protocolSettings['cipher']) {
|
||||
'2022-blake3-aes-128-gcm' => Helper::getServerKey($node->created_at, 16),
|
||||
'2022-blake3-aes-256-gcm' => Helper::getServerKey($node->created_at, 32),
|
||||
default => null
|
||||
}
|
||||
],
|
||||
'vmess' => [
|
||||
...$baseConfig,
|
||||
'tls' => (int) $protocolSettings['tls']
|
||||
],
|
||||
'trojan' => [
|
||||
...$baseConfig,
|
||||
'host' => $host,
|
||||
'server_name' => $protocolSettings['server_name'],
|
||||
],
|
||||
'vless' => [
|
||||
...$baseConfig,
|
||||
'tls' => (int) $protocolSettings['tls'],
|
||||
'flow' => $protocolSettings['flow'],
|
||||
'tls_settings' =>
|
||||
match ((int) $protocolSettings['tls']) {
|
||||
2 => $protocolSettings['reality_settings'],
|
||||
default => $protocolSettings['tls_settings']
|
||||
}
|
||||
],
|
||||
'hysteria' => [
|
||||
'server_port' => (int) $serverPort,
|
||||
'version' => (int) $protocolSettings['version'],
|
||||
'host' => $host,
|
||||
'server_name' => $protocolSettings['tls']['server_name'],
|
||||
'up_mbps' => (int) $protocolSettings['bandwidth']['up'],
|
||||
'down_mbps' => (int) $protocolSettings['bandwidth']['down'],
|
||||
...match ((int) $protocolSettings['version']) {
|
||||
1 => ['obfs' => $protocolSettings['obfs']['password'] ?? null],
|
||||
2 => [
|
||||
'obfs' => $protocolSettings['obfs']['open'] ? $protocolSettings['obfs']['type'] : null,
|
||||
'obfs-password' => $protocolSettings['obfs']['password'] ?? null
|
||||
],
|
||||
default => []
|
||||
}
|
||||
],
|
||||
default => []
|
||||
};
|
||||
|
||||
if ($nodeInfo->cipher === '2022-blake3-aes-128-gcm') {
|
||||
$response['server_key'] = Helper::getServerKey($nodeInfo->created_at, 16);
|
||||
}
|
||||
if ($nodeInfo->cipher === '2022-blake3-aes-256-gcm') {
|
||||
$response['server_key'] = Helper::getServerKey($nodeInfo->created_at, 32);
|
||||
}
|
||||
break;
|
||||
case 'vmess':
|
||||
$response = [
|
||||
'server_port' => $nodeInfo->server_port,
|
||||
'network' => $nodeInfo->network,
|
||||
'networkSettings' => $nodeInfo->networkSettings,
|
||||
'tls' => $nodeInfo->tls
|
||||
];
|
||||
break;
|
||||
case 'trojan':
|
||||
$response = [
|
||||
'host' => $nodeInfo->host,
|
||||
'server_port' => $nodeInfo->server_port,
|
||||
'server_name' => $nodeInfo->server_name,
|
||||
'network' => $nodeInfo->network,
|
||||
'networkSettings' => $nodeInfo->networkSettings,
|
||||
];
|
||||
break;
|
||||
case 'hysteria':
|
||||
$response = [
|
||||
'version' => $nodeInfo->version,
|
||||
'host' => $nodeInfo->host,
|
||||
'server_port' => $nodeInfo->server_port,
|
||||
'server_name' => $nodeInfo->server_name,
|
||||
'up_mbps' => $nodeInfo->up_mbps,
|
||||
'down_mbps' => $nodeInfo->down_mbps,
|
||||
'obfs' => $nodeInfo->is_obfs ? Helper::getServerKey($nodeInfo->created_at, 16) : null
|
||||
];
|
||||
break;
|
||||
case "vless":
|
||||
$response = [
|
||||
'server_port' => $nodeInfo->server_port,
|
||||
'network' => $nodeInfo->network,
|
||||
'network_settings' => $nodeInfo->network_settings,
|
||||
'networkSettings' => $nodeInfo->network_settings,
|
||||
'tls' => $nodeInfo->tls,
|
||||
'flow' => $nodeInfo->flow,
|
||||
'tls_settings' => $nodeInfo->tls_settings
|
||||
];
|
||||
break;
|
||||
}
|
||||
$response['base_config'] = [
|
||||
'push_interval' => (int) admin_setting('server_push_interval', 60),
|
||||
'pull_interval' => (int) admin_setting('server_pull_interval', 60)
|
||||
];
|
||||
if ($nodeInfo['route_id']) {
|
||||
$response['routes'] = ServerService::getRoutes($nodeInfo['route_id']);
|
||||
}
|
||||
$eTag = sha1(json_encode($response));
|
||||
if (strpos($request->header('If-None-Match'), $eTag) !== false) {
|
||||
return response(null, 304);
|
||||
|
||||
if (!empty($node['route_ids'])) {
|
||||
$response['routes'] = ServerService::getRoutes($node['route_ids']);
|
||||
}
|
||||
|
||||
$eTag = sha1(json_encode($response));
|
||||
if (strpos($request->header('If-None-Match', '') ?? '', $eTag) !== false) {
|
||||
return response(null, 304);
|
||||
}
|
||||
return response($response)->header('ETag', "\"{$eTag}\"");
|
||||
}
|
||||
|
||||
// 后端提交在线数据
|
||||
public function alive(Request $request)
|
||||
// 获取在线用户数据(wyx2685
|
||||
public function alivelist(Request $request): JsonResponse
|
||||
{
|
||||
return $this->success(true);
|
||||
$node = $request->input('node_info');
|
||||
$deviceLimitUsers = ServerService::getAvailableUsers($node->group_ids)
|
||||
->where('device_limit', '>', 0);
|
||||
$alive = $this->userOnlineService->getAliveList($deviceLimitUsers);
|
||||
return response()->json(['alive' => (object) $alive]);
|
||||
}
|
||||
|
||||
// 后端提交在线数据
|
||||
public function alive(Request $request): JsonResponse
|
||||
{
|
||||
$node = $request->input('node_info');
|
||||
$data = json_decode(request()->getContent(), true);
|
||||
if ($data === null) {
|
||||
return response()->json([
|
||||
'error' => 'Invalid online data'
|
||||
], 400);
|
||||
}
|
||||
$this->userOnlineService->updateAliveData($data, $node->type, $node->id);
|
||||
return response()->json(['data' => true]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class TicketController extends Controller
|
||||
$ticketService->replyByAdmin(
|
||||
$request->input('id'),
|
||||
$request->input('message'),
|
||||
$request->user['id']
|
||||
$request->user()->id
|
||||
);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class CouponController extends Controller
|
||||
}
|
||||
$couponService = new CouponService($request->input('code'));
|
||||
$couponService->setPlanId($request->input('plan_id'));
|
||||
$couponService->setUserId($request->user['id']);
|
||||
$couponService->setUserId($request->user()->id);
|
||||
$couponService->check();
|
||||
return $this->success($couponService->getCoupon());
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ class InviteController extends Controller
|
||||
{
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (InviteCode::where('user_id', $request->user['id'])->where('status', 0)->count() >= admin_setting('invite_gen_limit', 5)) {
|
||||
if (InviteCode::where('user_id', $request->user()->id)->where('status', 0)->count() >= admin_setting('invite_gen_limit', 5)) {
|
||||
return $this->fail([400,__('The maximum number of creations has been reached')]);
|
||||
}
|
||||
$inviteCode = new InviteCode();
|
||||
$inviteCode->user_id = $request->user['id'];
|
||||
$inviteCode->user_id = $request->user()->id;
|
||||
$inviteCode->code = Helper::randomChar(8);
|
||||
return $this->success($inviteCode->save());
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class InviteController extends Controller
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('page_size') >= 10 ? $request->input('page_size') : 10;
|
||||
$builder = CommissionLog::where('invite_user_id', $request->user['id'])
|
||||
$builder = CommissionLog::where('invite_user_id', $request->user()->id)
|
||||
->where('get_amount', '>', 0)
|
||||
->orderBy('created_at', 'DESC');
|
||||
$total = $builder->count();
|
||||
@@ -45,7 +45,7 @@ class InviteController extends Controller
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$commission_rate = admin_setting('invite_commission', 10);
|
||||
$user = User::find($request->user['id'])
|
||||
$user = User::find($request->user()->id)
|
||||
->load(['codes' => fn($query) => $query->where('status', 0)]);
|
||||
if ($user->commission_rate) {
|
||||
$commission_rate = $user->commission_rate;
|
||||
|
||||
@@ -20,7 +20,7 @@ class KnowledgeController extends Controller
|
||||
->first()
|
||||
->toArray();
|
||||
if (!$knowledge) return $this->fail([500, __('Article does not exist')]);
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
$userService = new UserService();
|
||||
if (!$userService->isAvailable($user)) {
|
||||
$this->formatAccessData($knowledge['body']);
|
||||
|
||||
@@ -12,8 +12,8 @@ class NoticeController extends Controller
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = 5;
|
||||
$model = Notice::orderBy('created_at', 'DESC')
|
||||
->where('show', 1);
|
||||
$model = Notice::orderBy('sort', 'ASC')
|
||||
->where('show', true);
|
||||
$total = $model->count();
|
||||
$res = $model->forPage($current, $pageSize)
|
||||
->get();
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace App\Http\Controllers\V1\User;
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\OrderSave;
|
||||
use App\Http\Resources\OrderResource;
|
||||
use App\Models\Order;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Plan;
|
||||
@@ -13,6 +14,7 @@ use App\Services\CouponService;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PaymentService;
|
||||
use App\Services\PlanService;
|
||||
use App\Services\Plugin\HookManager;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -22,138 +24,127 @@ class OrderController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$model = Order::where('user_id', $request->user['id'])
|
||||
->orderBy('created_at', 'DESC');
|
||||
if ($request->input('status') !== null) {
|
||||
$model->where('status', $request->input('status'));
|
||||
}
|
||||
$order = $model->get();
|
||||
$plan = Plan::get();
|
||||
for ($i = 0; $i < count($order); $i++) {
|
||||
for ($x = 0; $x < count($plan); $x++) {
|
||||
if ($order[$i]['plan_id'] === $plan[$x]['id']) {
|
||||
$order[$i]['plan'] = $plan[$x];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->success($order->makeHidden(['id', 'user_id']));
|
||||
$request->validate([
|
||||
'status' => 'nullable|integer|in:0,1,2,3',
|
||||
]);
|
||||
$orders = Order::with('plan')
|
||||
->where('user_id', $request->user()->id)
|
||||
->when($request->input('status') !== null, function ($query) use ($request) {
|
||||
$query->where('status', $request->input('status'));
|
||||
})
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get();
|
||||
|
||||
return $this->success(OrderResource::collection($orders));
|
||||
}
|
||||
|
||||
public function detail(Request $request)
|
||||
{
|
||||
$order = Order::where('user_id', $request->user['id'])
|
||||
$request->validate([
|
||||
'trade_no' => 'required|string',
|
||||
]);
|
||||
$order = Order::with('payment')
|
||||
->where('user_id', $request->user()->id)
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400, __('Order does not exist or has been paid')]);
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['try_out_plan_id'] = (int)admin_setting('try_out_plan_id');
|
||||
$order['try_out_plan_id'] = (int) admin_setting('try_out_plan_id');
|
||||
if (!$order['plan']) {
|
||||
return $this->fail([400, __('Subscription plan does not exist')]);
|
||||
}
|
||||
if ($order->surplus_order_ids) {
|
||||
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
|
||||
}
|
||||
return $this->success($order);
|
||||
return $this->success(OrderResource::make($order));
|
||||
}
|
||||
|
||||
public function save(OrderSave $request)
|
||||
{
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($request->user['id'])) {
|
||||
return $this->fail([400, __('You have an unpaid or pending order, please try again later or cancel it')]);
|
||||
$request->validate([
|
||||
'plan_id' => 'required|exists:App\Models\Plan,id',
|
||||
'period' => 'required|string'
|
||||
]);
|
||||
|
||||
$user = User::findOrFail($request->user()->id);
|
||||
$userService = app(UserService::class);
|
||||
|
||||
if ($userService->isNotCompleteOrderByUserId($user->id)) {
|
||||
throw new ApiException(__('You have an unpaid or pending order, please try again later or cancel it'));
|
||||
}
|
||||
|
||||
$planService = new PlanService($request->input('plan_id'));
|
||||
$plan = Plan::findOrFail($request->input('plan_id'));
|
||||
$planService = new PlanService($plan);
|
||||
|
||||
$plan = $planService->plan;
|
||||
$user = User::find($request->user['id']);
|
||||
// Validate plan purchase
|
||||
$planService->validatePurchase($user, $request->input('period'));
|
||||
|
||||
if (!$plan) {
|
||||
return $this->fail([400, __('Subscription plan does not exist')]);
|
||||
}
|
||||
return DB::transaction(function () use ($request, $plan, $user, $userService, $planService) {
|
||||
$period = $request->input('period');
|
||||
$newPeriod = PlanService::getPeriodKey($period);
|
||||
|
||||
if ($user->plan_id !== $plan->id && !$planService->haveCapacity() && $request->input('period') !== 'reset_price') {
|
||||
throw new ApiException(__('Current product is sold out'));
|
||||
}
|
||||
|
||||
if ($plan[$request->input('period')] === NULL) {
|
||||
return $this->fail([400, __('This payment period cannot be purchased, please choose another period')]);
|
||||
}
|
||||
|
||||
if ($request->input('period') === 'reset_price') {
|
||||
if (!$userService->isAvailable($user) || $plan->id !== $user->plan_id) {
|
||||
return $this->fail([400, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package')]);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if ($request->input('period') !== 'reset_price') {
|
||||
return $this->fail([400, __('This subscription has been sold out, please choose another subscription')]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('period') !== 'reset_price') {
|
||||
return $this->fail([400, __('This subscription cannot be renewed, please change to another subscription')]);
|
||||
}
|
||||
|
||||
|
||||
if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
|
||||
return $this->fail([400, __('This subscription has expired, please change to another subscription')]);
|
||||
}
|
||||
|
||||
try{
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $request->user['id'];
|
||||
$order->plan_id = $plan->id;
|
||||
$order->period = $request->input('period');
|
||||
$order->trade_no = Helper::generateOrderNo();
|
||||
$order->total_amount = $plan[$request->input('period')];
|
||||
// Create order
|
||||
$order = new Order([
|
||||
'user_id' => $user->id,
|
||||
'plan_id' => $plan->id,
|
||||
'period' => $newPeriod,
|
||||
'trade_no' => Helper::generateOrderNo(),
|
||||
'total_amount' => optional($plan->prices)[$newPeriod] * 100
|
||||
]);
|
||||
|
||||
// Apply coupon if provided
|
||||
if ($request->input('coupon_code')) {
|
||||
$couponService = new CouponService($request->input('coupon_code'));
|
||||
if (!$couponService->use($order)) {
|
||||
return $this->fail([400, __('Coupon failed')]);
|
||||
}
|
||||
$order->coupon_id = $couponService->getId();
|
||||
$this->applyCoupon($order, $request->input('coupon_code'));
|
||||
}
|
||||
|
||||
// Set order attributes
|
||||
$orderService = new OrderService($order);
|
||||
$orderService->setVipDiscount($user);
|
||||
$orderService->setOrderType($user);
|
||||
$orderService->setInvite($user);
|
||||
|
||||
// Handle user balance
|
||||
if ($user->balance && $order->total_amount > 0) {
|
||||
$remainingBalance = $user->balance - $order->total_amount;
|
||||
$userService = new UserService();
|
||||
if ($remainingBalance > 0) {
|
||||
if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
|
||||
return $this->fail([400, __('Insufficient balance')]);
|
||||
}
|
||||
$order->balance_amount = $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
if (!$userService->addBalance($order->user_id, - $user->balance)) {
|
||||
return $this->fail([400, __('Insufficient balance')]);
|
||||
}
|
||||
$order->balance_amount = $user->balance;
|
||||
$order->total_amount = $order->total_amount - $user->balance;
|
||||
}
|
||||
$this->handleUserBalance($order, $user, $userService);
|
||||
}
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return $this->fail([400, __('Failed to create order')]);
|
||||
throw new ApiException(__('Failed to create order'));
|
||||
}
|
||||
DB::commit();
|
||||
}catch (\Exception $e){
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
HookManager::call('order.after_create', $order);
|
||||
|
||||
return $this->success($order->trade_no);
|
||||
return $this->success($order->trade_no);
|
||||
});
|
||||
}
|
||||
|
||||
protected function applyCoupon(Order $order, string $couponCode): void
|
||||
{
|
||||
$couponService = new CouponService($couponCode);
|
||||
if (!$couponService->use($order)) {
|
||||
throw new ApiException(__('Coupon failed'));
|
||||
}
|
||||
$order->coupon_id = $couponService->getId();
|
||||
}
|
||||
|
||||
protected function handleUserBalance(Order $order, User $user, UserService $userService): void
|
||||
{
|
||||
$remainingBalance = $user->balance - $order->total_amount;
|
||||
|
||||
if ($remainingBalance > 0) {
|
||||
if (!$userService->addBalance($order->user_id, -$order->total_amount)) {
|
||||
throw new ApiException(__('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
if (!$userService->addBalance($order->user_id, -$user->balance)) {
|
||||
throw new ApiException(__('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $user->balance;
|
||||
$order->total_amount = $order->total_amount - $user->balance;
|
||||
}
|
||||
}
|
||||
|
||||
public function checkout(Request $request)
|
||||
@@ -161,7 +152,7 @@ class OrderController extends Controller
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$method = $request->input('method');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
@@ -170,21 +161,24 @@ class OrderController extends Controller
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->paid($order->trade_no)) return $this->fail([400, '支付失败']);
|
||||
if (!$orderService->paid($order->trade_no))
|
||||
return $this->fail([400, '支付失败']);
|
||||
return response([
|
||||
'type' => -1,
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
$payment = Payment::find($method);
|
||||
if (!$payment || $payment->enable !== 1) return $this->fail([400, __('Payment method is not available')]);
|
||||
if (!$payment || $payment->enable !== 1)
|
||||
return $this->fail([400, __('Payment method is not available')]);
|
||||
$paymentService = new PaymentService($payment->payment, $payment->id);
|
||||
$order->handling_amount = NULL;
|
||||
if ($payment->handling_fee_fixed || $payment->handling_fee_percent) {
|
||||
$order->handling_amount = round(($order->total_amount * ($payment->handling_fee_percent / 100)) + $payment->handling_fee_fixed);
|
||||
}
|
||||
$order->payment_id = $method;
|
||||
if (!$order->save()) return $this->fail([400, __('Request failed, please try again later')]);
|
||||
if (!$order->save())
|
||||
return $this->fail([400, __('Request failed, please try again later')]);
|
||||
$result = $paymentService->pay([
|
||||
'trade_no' => $tradeNo,
|
||||
'total_amount' => isset($order->handling_amount) ? ($order->total_amount + $order->handling_amount) : $order->total_amount,
|
||||
@@ -201,7 +195,7 @@ class OrderController extends Controller
|
||||
{
|
||||
$tradeNo = $request->input('trade_no');
|
||||
$order = Order::where('trade_no', $tradeNo)
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400, __('Order does not exist')]);
|
||||
@@ -232,7 +226,7 @@ class OrderController extends Controller
|
||||
return $this->fail([422, __('Invalid parameter')]);
|
||||
}
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400, __('Order does not exist')]);
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace App\Http\Controllers\V1\User;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Resources\PlanResource;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\PlanService;
|
||||
@@ -11,29 +12,27 @@ use Illuminate\Http\Request;
|
||||
|
||||
class PlanController extends Controller
|
||||
{
|
||||
protected PlanService $planService;
|
||||
|
||||
public function __construct(PlanService $planService)
|
||||
{
|
||||
$this->planService = $planService;
|
||||
}
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if ($request->input('id')) {
|
||||
$plan = Plan::where('id', $request->input('id'))->first();
|
||||
if (!$plan) {
|
||||
return $this->fail([400, __('Subscription plan does not exist')]);
|
||||
}
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if (!$this->planService->isPlanAvailableForUser($plan, $user)) {
|
||||
return $this->fail([400, __('Subscription plan does not exist')]);
|
||||
}
|
||||
return $this->success($plan);
|
||||
return $this->success(PlanResource::make($plan));
|
||||
}
|
||||
|
||||
$counts = PlanService::countActiveUsers();
|
||||
$plans = Plan::where('show', 1)
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
foreach ($plans as $k => $v) {
|
||||
if ($plans[$k]->capacity_limit === NULL) continue;
|
||||
if (!isset($counts[$plans[$k]->id])) continue;
|
||||
$plans[$k]->capacity_limit = $plans[$k]->capacity_limit - $counts[$plans[$k]->id]->count;
|
||||
}
|
||||
return $this->success($plans);
|
||||
$plans = $this->planService->getAvailablePlans();
|
||||
return $this->success(PlanResource::collection($plans));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ class ServerController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
$servers = [];
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
$servers = ServerService::getAvailableServers($user);
|
||||
}
|
||||
$eTag = sha1(json_encode(array_column($servers, 'cache_key')));
|
||||
if (strpos($request->header('If-None-Match'), $eTag) !== false ) {
|
||||
if (strpos($request->header('If-None-Match', ''), $eTag) !== false ) {
|
||||
return response(null,304);
|
||||
}
|
||||
$data = NodeResource::collection($servers);
|
||||
|
||||
@@ -15,23 +15,11 @@ class StatController extends Controller
|
||||
{
|
||||
$startDate = now()->startOfMonth()->timestamp;
|
||||
$records = StatUser::query()
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->where('record_at', '>=', $startDate)
|
||||
->orderBy('record_at', 'DESC')
|
||||
->get();
|
||||
|
||||
// 追加当天流量
|
||||
$recordAt = strtotime(date('Y-m-d'));
|
||||
$statService = new StatisticalService();
|
||||
$statService->setStartAt($recordAt);
|
||||
$todayTraffics = $statService->getStatUserByUserID($request->user['id']);
|
||||
if (count($todayTraffics) > 0) {
|
||||
$todayTraffics = collect($todayTraffics)->map(function ($todayTraffic) {
|
||||
$todayTraffic['server_rate'] = number_format($todayTraffic['server_rate'], 2);
|
||||
return $todayTraffic;
|
||||
});
|
||||
$records = $todayTraffics->merge($records);
|
||||
}
|
||||
$data = TrafficLogResource::collection(collect($records));
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,6 @@ class TelegramController extends Controller
|
||||
|
||||
public function unbind(Request $request)
|
||||
{
|
||||
$user = User::where('user_id', $request->user['id'])->first();
|
||||
$user = User::where('user_id', $request->user()->id)->first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class TicketController extends Controller
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->first()
|
||||
->load('message');
|
||||
if (!$ticket) {
|
||||
@@ -33,7 +33,7 @@ class TicketController extends Controller
|
||||
});
|
||||
return $this->success(TicketResource::make($ticket)->additional(['message' => true]));
|
||||
}
|
||||
$ticket = Ticket::where('user_id', $request->user['id'])
|
||||
$ticket = Ticket::where('user_id', $request->user()->id)
|
||||
->orderBy('created_at', 'DESC')
|
||||
->get();
|
||||
return $this->success(TicketResource::collection($ticket));
|
||||
@@ -43,20 +43,20 @@ class TicketController extends Controller
|
||||
{
|
||||
try{
|
||||
DB::beginTransaction();
|
||||
if ((int)Ticket::where('status', 0)->where('user_id', $request->user['id'])->lockForUpdate()->count()) {
|
||||
if ((int)Ticket::where('status', 0)->where('user_id', $request->user()->id)->lockForUpdate()->count()) {
|
||||
throw new \Exception(__('There are other unresolved tickets'));
|
||||
}
|
||||
$ticket = Ticket::create(array_merge($request->only([
|
||||
'subject',
|
||||
'level'
|
||||
]), [
|
||||
'user_id' => $request->user['id']
|
||||
'user_id' => $request->user()->id
|
||||
]));
|
||||
if (!$ticket) {
|
||||
throw new \Exception(__('There are other unresolved tickets'));
|
||||
}
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->user['id'],
|
||||
'user_id' => $request->user()->id,
|
||||
'ticket_id' => $ticket->id,
|
||||
'message' => $request->input('message')
|
||||
]);
|
||||
@@ -64,7 +64,7 @@ class TicketController extends Controller
|
||||
throw new \Exception(__('Failed to open ticket'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $request->input('message'), $request->user['id']);
|
||||
$this->sendNotify($ticket, $request->input('message'), $request->user()->id);
|
||||
return $this->success(true);
|
||||
}catch(\Exception $e){
|
||||
DB::rollBack();
|
||||
@@ -83,7 +83,7 @@ class TicketController extends Controller
|
||||
return $this->fail([400, __('Message cannot be empty')]);
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
return $this->fail([400, __('Ticket does not exist')]);
|
||||
@@ -91,18 +91,18 @@ class TicketController extends Controller
|
||||
if ($ticket->status) {
|
||||
return $this->fail([400, __('The ticket is closed and cannot be replied')]);
|
||||
}
|
||||
if ($request->user['id'] == $this->getLastMessage($ticket->id)->user_id) {
|
||||
if ($request->user()->id == $this->getLastMessage($ticket->id)->user_id) {
|
||||
return $this->fail([400, __('Please wait for the technical enginneer to reply')]);
|
||||
}
|
||||
$ticketService = new TicketService();
|
||||
if (!$ticketService->reply(
|
||||
$ticket,
|
||||
$request->input('message'),
|
||||
$request->user['id']
|
||||
$request->user()->id
|
||||
)) {
|
||||
return $this->fail([400, __('Ticket reply failed')]);
|
||||
}
|
||||
$this->sendNotify($ticket, $request->input('message'), $request->user['id']);
|
||||
$this->sendNotify($ticket, $request->input('message'), $request->user()->id);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class TicketController extends Controller
|
||||
return $this->fail([422, __('Invalid parameter')]);
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
return $this->fail([400, __('Ticket does not exist')]);
|
||||
@@ -143,7 +143,7 @@ class TicketController extends Controller
|
||||
)) {
|
||||
return $this->fail([422, __('Unsupported withdrawal method')]);
|
||||
}
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
$limit = admin_setting('commission_withdraw_limit', 100);
|
||||
if ($limit > ($user->commission_balance / 100)) {
|
||||
return $this->fail([422, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit])]);
|
||||
@@ -154,7 +154,7 @@ class TicketController extends Controller
|
||||
$ticket = Ticket::create([
|
||||
'subject' => $subject,
|
||||
'level' => 2,
|
||||
'user_id' => $request->user['id']
|
||||
'user_id' => $request->user()->id
|
||||
]);
|
||||
if (!$ticket) {
|
||||
return $this->fail([400, __('Failed to open ticket')]);
|
||||
@@ -164,7 +164,7 @@ class TicketController extends Controller
|
||||
__('Withdrawal account') . ":" . $request->input('withdraw_account')
|
||||
);
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->user['id'],
|
||||
'user_id' => $request->user()->id,
|
||||
'ticket_id' => $ticket->id,
|
||||
'message' => $message
|
||||
]);
|
||||
@@ -177,7 +177,7 @@ class TicketController extends Controller
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
$this->sendNotify($ticket, $message, $request->user['id']);
|
||||
$this->sendNotify($ticket, $message, $request->user()->id);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ use App\Services\AuthService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Utils\Helper;
|
||||
use Auth;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
@@ -22,7 +23,7 @@ class UserController extends Controller
|
||||
{
|
||||
public function getActiveSession(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -32,7 +33,7 @@ class UserController extends Controller
|
||||
|
||||
public function removeActiveSession(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -43,9 +44,9 @@ class UserController extends Controller
|
||||
public function checkLogin(Request $request)
|
||||
{
|
||||
$data = [
|
||||
'is_login' => $request->user['id'] ? true : false
|
||||
'is_login' => $request->user()?->id ? true : false
|
||||
];
|
||||
if ($request->user['is_admin']) {
|
||||
if ($request->user()?->is_admin) {
|
||||
$data['is_admin'] = true;
|
||||
}
|
||||
return $this->success($data);
|
||||
@@ -53,7 +54,7 @@ class UserController extends Controller
|
||||
|
||||
public function changePassword(UserChangePassword $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -76,7 +77,7 @@ class UserController extends Controller
|
||||
|
||||
public function info(Request $request)
|
||||
{
|
||||
$user = User::where('id', $request->user['id'])
|
||||
$user = User::where('id', $request->user()->id)
|
||||
->select([
|
||||
'email',
|
||||
'transfer_enable',
|
||||
@@ -106,12 +107,12 @@ class UserController extends Controller
|
||||
{
|
||||
$stat = [
|
||||
Order::where('status', 0)
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->count(),
|
||||
Ticket::where('status', 0)
|
||||
->where('user_id', $request->user['id'])
|
||||
->where('user_id', $request->user()->id)
|
||||
->count(),
|
||||
User::where('invite_user_id', $request->user['id'])
|
||||
User::where('invite_user_id', $request->user()->id)
|
||||
->count()
|
||||
];
|
||||
return $this->success($stat);
|
||||
@@ -119,7 +120,7 @@ class UserController extends Controller
|
||||
|
||||
public function getSubscribe(Request $request)
|
||||
{
|
||||
$user = User::where('id', $request->user['id'])
|
||||
$user = User::where('id', $request->user()->id)
|
||||
->select([
|
||||
'plan_id',
|
||||
'token',
|
||||
@@ -148,7 +149,7 @@ class UserController extends Controller
|
||||
|
||||
public function resetSecurity(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -167,7 +168,7 @@ class UserController extends Controller
|
||||
'remind_traffic'
|
||||
]);
|
||||
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -182,7 +183,7 @@ class UserController extends Controller
|
||||
|
||||
public function transfer(UserTransfer $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
@@ -199,7 +200,7 @@ class UserController extends Controller
|
||||
|
||||
public function getQuickLoginUrl(Request $request)
|
||||
{
|
||||
$user = User::find($request->user['id']);
|
||||
$user = User::find($request->user()->id);
|
||||
if (!$user) {
|
||||
return $this->fail([400, __('The user does not exist')]);
|
||||
}
|
||||
|
||||
Executable → Regular
+54
-63
@@ -1,15 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ConfigSave;
|
||||
use App\Models\Setting;
|
||||
use App\Services\MailService;
|
||||
use App\Services\TelegramService;
|
||||
use App\Services\ThemeService;
|
||||
use App\Utils\Dict;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ConfigController extends Controller
|
||||
{
|
||||
@@ -34,7 +34,7 @@ class ConfigController extends Controller
|
||||
public function testSendMail(Request $request)
|
||||
{
|
||||
$mailLog = MailService::sendEmail([
|
||||
'email' => $request->user['email'],
|
||||
'email' => $request->user()->email,
|
||||
'subject' => 'This is xboard test email',
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
@@ -44,8 +44,7 @@ class ConfigController extends Controller
|
||||
]
|
||||
]);
|
||||
return response([
|
||||
'data' => true,
|
||||
'log' => $mailLog
|
||||
'data' => $mailLog,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -53,8 +52,9 @@ class ConfigController extends Controller
|
||||
{
|
||||
// 判断站点网址
|
||||
$app_url = admin_setting('app_url');
|
||||
if(blank($app_url)) return $this->fail([422, '请先设置站点网址']);
|
||||
$hookUrl = $app_url .'/api/v1/guest/telegram/webhook?' . http_build_query([
|
||||
if (blank($app_url))
|
||||
return $this->fail([422, '请先设置站点网址']);
|
||||
$hookUrl = $app_url . '/api/v1/guest/telegram/webhook?' . http_build_query([
|
||||
'access_token' => md5(admin_setting('telegram_bot_token', $request->input('telegram_bot_token')))
|
||||
]);
|
||||
$telegramService = new TelegramService($request->input('telegram_bot_token'));
|
||||
@@ -68,45 +68,46 @@ class ConfigController extends Controller
|
||||
$key = $request->input('key');
|
||||
$data = [
|
||||
'invite' => [
|
||||
'invite_force' => (int)admin_setting('invite_force', 0),
|
||||
'invite_force' => (bool) admin_setting('invite_force', 0),
|
||||
'invite_commission' => admin_setting('invite_commission', 10),
|
||||
'invite_gen_limit' => admin_setting('invite_gen_limit', 5),
|
||||
'invite_never_expire' => admin_setting('invite_never_expire', 0),
|
||||
'commission_first_time_enable' => admin_setting('commission_first_time_enable', 1),
|
||||
'commission_auto_check_enable' => admin_setting('commission_auto_check_enable', 1),
|
||||
'invite_never_expire' => (bool) admin_setting('invite_never_expire', 0),
|
||||
'commission_first_time_enable' => (bool) admin_setting('commission_first_time_enable', 1),
|
||||
'commission_auto_check_enable' => (bool) admin_setting('commission_auto_check_enable', 1),
|
||||
'commission_withdraw_limit' => admin_setting('commission_withdraw_limit', 100),
|
||||
'commission_withdraw_method' => admin_setting('commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
|
||||
'withdraw_close_enable' => admin_setting('withdraw_close_enable', 0),
|
||||
'commission_distribution_enable' => admin_setting('commission_distribution_enable', 0),
|
||||
'withdraw_close_enable' => (bool) admin_setting('withdraw_close_enable', 0),
|
||||
'commission_distribution_enable' => (bool) admin_setting('commission_distribution_enable', 0),
|
||||
'commission_distribution_l1' => admin_setting('commission_distribution_l1'),
|
||||
'commission_distribution_l2' => admin_setting('commission_distribution_l2'),
|
||||
'commission_distribution_l3' => admin_setting('commission_distribution_l3')
|
||||
],
|
||||
'site' => [
|
||||
'logo' => admin_setting('logo'),
|
||||
'force_https' => (int)admin_setting('force_https', 0),
|
||||
'stop_register' => (int)admin_setting('stop_register', 0),
|
||||
'force_https' => (int) admin_setting('force_https', 0),
|
||||
'stop_register' => (int) admin_setting('stop_register', 0),
|
||||
'app_name' => admin_setting('app_name', 'XBoard'),
|
||||
'app_description' => admin_setting('app_description', 'XBoard is best!'),
|
||||
'app_url' => admin_setting('app_url'),
|
||||
'subscribe_url' => admin_setting('subscribe_url'),
|
||||
'try_out_plan_id' => (int)admin_setting('try_out_plan_id', 0),
|
||||
'try_out_hour' => (int)admin_setting('try_out_hour', 1),
|
||||
'try_out_plan_id' => (int) admin_setting('try_out_plan_id', 0),
|
||||
'try_out_hour' => (int) admin_setting('try_out_hour', 1),
|
||||
'tos_url' => admin_setting('tos_url'),
|
||||
'currency' => admin_setting('currency', 'CNY'),
|
||||
'currency_symbol' => admin_setting('currency_symbol', '¥'),
|
||||
],
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)admin_setting('plan_change_enable', 1),
|
||||
'reset_traffic_method' => (int)admin_setting('reset_traffic_method', 0),
|
||||
'surplus_enable' => (int)admin_setting('surplus_enable', 1),
|
||||
'new_order_event_id' => (int)admin_setting('new_order_event_id', 0),
|
||||
'renew_order_event_id' => (int)admin_setting('renew_order_event_id', 0),
|
||||
'change_order_event_id' => (int)admin_setting('change_order_event_id', 0),
|
||||
'show_info_to_server_enable' => (int)admin_setting('show_info_to_server_enable', 0),
|
||||
'show_protocol_to_server_enable' => (int)admin_setting('show_protocol_to_server_enable', 0),
|
||||
'default_remind_expire' => (int)admin_setting('default_remind_expire',1),
|
||||
'default_remind_traffic' => (int)admin_setting('default_remind_traffic',1),
|
||||
'plan_change_enable' => (bool) admin_setting('plan_change_enable', 1),
|
||||
'reset_traffic_method' => (int) admin_setting('reset_traffic_method', 0),
|
||||
'surplus_enable' => (bool) admin_setting('surplus_enable', 1),
|
||||
'new_order_event_id' => (int) admin_setting('new_order_event_id', 0),
|
||||
'renew_order_event_id' => (int) admin_setting('renew_order_event_id', 0),
|
||||
'change_order_event_id' => (int) admin_setting('change_order_event_id', 0),
|
||||
'show_info_to_server_enable' => (bool) admin_setting('show_info_to_server_enable', 0),
|
||||
'show_protocol_to_server_enable' => (bool) admin_setting('show_protocol_to_server_enable', 0),
|
||||
'default_remind_expire' => (bool) admin_setting('default_remind_expire', 1),
|
||||
'default_remind_traffic' => (bool) admin_setting('default_remind_traffic', 1),
|
||||
'subscribe_path' => admin_setting('subscribe_path', 's'),
|
||||
|
||||
],
|
||||
'frontend' => [
|
||||
@@ -120,6 +121,7 @@ class ConfigController extends Controller
|
||||
'server_token' => admin_setting('server_token'),
|
||||
'server_pull_interval' => admin_setting('server_pull_interval', 60),
|
||||
'server_push_interval' => admin_setting('server_push_interval', 60),
|
||||
'device_limit_mode' => (int) admin_setting('device_limit_mode', 0),
|
||||
],
|
||||
'email' => [
|
||||
'email_template' => admin_setting('email_template', 'default'),
|
||||
@@ -128,35 +130,36 @@ class ConfigController extends Controller
|
||||
'email_username' => admin_setting('email_username'),
|
||||
'email_password' => admin_setting('email_password'),
|
||||
'email_encryption' => admin_setting('email_encryption'),
|
||||
'email_from_address' => admin_setting('email_from_address')
|
||||
'email_from_address' => admin_setting('email_from_address'),
|
||||
'remind_mail_enable' => (bool) admin_setting('remind_mail_enable', false),
|
||||
],
|
||||
'telegram' => [
|
||||
'telegram_bot_enable' => admin_setting('telegram_bot_enable', 0),
|
||||
'telegram_bot_enable' => (bool) admin_setting('telegram_bot_enable', 0),
|
||||
'telegram_bot_token' => admin_setting('telegram_bot_token'),
|
||||
'telegram_discuss_link' => admin_setting('telegram_discuss_link')
|
||||
],
|
||||
'app' => [
|
||||
'windows_version' => admin_setting('windows_version'),
|
||||
'windows_download_url' => admin_setting('windows_download_url'),
|
||||
'macos_version' => admin_setting('macos_version'),
|
||||
'macos_download_url' => admin_setting('macos_download_url'),
|
||||
'android_version' => admin_setting('android_version'),
|
||||
'android_download_url' => admin_setting('android_download_url')
|
||||
'windows_version' => admin_setting('windows_version', ''),
|
||||
'windows_download_url' => admin_setting('windows_download_url', ''),
|
||||
'macos_version' => admin_setting('macos_version', ''),
|
||||
'macos_download_url' => admin_setting('macos_download_url', ''),
|
||||
'android_version' => admin_setting('android_version', ''),
|
||||
'android_download_url' => admin_setting('android_download_url', '')
|
||||
],
|
||||
'safe' => [
|
||||
'email_verify' => (int)admin_setting('email_verify', 0),
|
||||
'safe_mode_enable' => (int)admin_setting('safe_mode_enable', 0),
|
||||
'email_verify' => (bool) admin_setting('email_verify', 0),
|
||||
'safe_mode_enable' => (bool) admin_setting('safe_mode_enable', 0),
|
||||
'secure_path' => admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))),
|
||||
'email_whitelist_enable' => (int)admin_setting('email_whitelist_enable', 0),
|
||||
'email_whitelist_enable' => (bool) admin_setting('email_whitelist_enable', 0),
|
||||
'email_whitelist_suffix' => admin_setting('email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT),
|
||||
'email_gmail_limit_enable' => admin_setting('email_gmail_limit_enable', 0),
|
||||
'recaptcha_enable' => (int)admin_setting('recaptcha_enable', 0),
|
||||
'recaptcha_key' => admin_setting('recaptcha_key'),
|
||||
'recaptcha_site_key' => admin_setting('recaptcha_site_key'),
|
||||
'register_limit_by_ip_enable' => (int)admin_setting('register_limit_by_ip_enable', 0),
|
||||
'email_gmail_limit_enable' => (bool) admin_setting('email_gmail_limit_enable', 0),
|
||||
'recaptcha_enable' => (bool) admin_setting('recaptcha_enable', 0),
|
||||
'recaptcha_key' => admin_setting('recaptcha_key', ''),
|
||||
'recaptcha_site_key' => admin_setting('recaptcha_site_key', ''),
|
||||
'register_limit_by_ip_enable' => (bool) admin_setting('register_limit_by_ip_enable', 0),
|
||||
'register_limit_count' => admin_setting('register_limit_count', 3),
|
||||
'register_limit_expire' => admin_setting('register_limit_expire', 60),
|
||||
'password_limit_enable' => (int)admin_setting('password_limit_enable', 1),
|
||||
'password_limit_enable' => (bool) admin_setting('password_limit_enable', 1),
|
||||
'password_limit_count' => admin_setting('password_limit_count', 5),
|
||||
'password_limit_expire' => admin_setting('password_limit_expire', 60)
|
||||
]
|
||||
@@ -165,7 +168,8 @@ class ConfigController extends Controller
|
||||
return $this->success([
|
||||
$key => $data[$key]
|
||||
]);
|
||||
};
|
||||
}
|
||||
;
|
||||
// TODO: default should be in Dict
|
||||
return $this->success($data);
|
||||
}
|
||||
@@ -173,26 +177,13 @@ class ConfigController extends Controller
|
||||
public function save(ConfigSave $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
$config = config('v2board');
|
||||
foreach (ConfigSave::RULES as $k => $v) {
|
||||
if (!in_array($k, array_keys(ConfigSave::RULES))) {
|
||||
unset($config[$k]);
|
||||
continue;
|
||||
}
|
||||
if (array_key_exists($k, $data)) {
|
||||
$value = $data[$k];
|
||||
if (is_array($value)) $value = json_encode($value);
|
||||
Setting::updateOrCreate(
|
||||
['name' => $k],
|
||||
['name' => $k, 'value' => $value]
|
||||
);
|
||||
foreach ($data as $k => $v) {
|
||||
if ($k == 'frontend_theme') {
|
||||
$themeService = new ThemeService();
|
||||
$themeService->switch($v);
|
||||
}
|
||||
admin_setting([$k => $v]);
|
||||
}
|
||||
// 如果是workerman环境,则触发reload
|
||||
if(isset(get_defined_constants(true)['user']['Workerman'])){
|
||||
posix_kill(posix_getppid(), SIGUSR1);
|
||||
}
|
||||
Cache::forget('admin_settings');
|
||||
// \Artisan::call('horizon:terminate'); //重启队列使配置生效
|
||||
return $this->success(true);
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\CouponGenerate;
|
||||
use App\Http\Requests\Admin\CouponSave;
|
||||
use App\Models\Coupon;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CouponController extends Controller
|
||||
{
|
||||
private function applyFiltersAndSorts(Request $request, $builder)
|
||||
{
|
||||
if ($request->has('filter')) {
|
||||
collect($request->input('filter'))->each(function ($filter) use ($builder) {
|
||||
$key = $filter['id'];
|
||||
$value = $filter['value'];
|
||||
$builder->where(function ($query) use ($key, $value) {
|
||||
if (is_array($value)) {
|
||||
$query->whereIn($key, $value);
|
||||
} else {
|
||||
$query->where($key, 'like', "%{$value}%");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->has('sort')) {
|
||||
collect($request->input('sort'))->each(function ($sort) use ($builder) {
|
||||
$key = $sort['id'];
|
||||
$value = $sort['desc'] ? 'DESC' : 'ASC';
|
||||
$builder->orderBy($key, $value);
|
||||
});
|
||||
}
|
||||
}
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current', 1);
|
||||
$pageSize = $request->input('pageSize', 10);
|
||||
$builder = Coupon::query();
|
||||
$this->applyFiltersAndSorts($request, $builder);
|
||||
$coupons = $builder
|
||||
->orderBy('created_at', 'desc')
|
||||
->paginate($pageSize, ["*"], 'page', $current);
|
||||
return response([
|
||||
'data' => $coupons->items(),
|
||||
'total' => $coupons->total()
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'id' => 'required|numeric',
|
||||
'show' => 'nullable|boolean'
|
||||
], [
|
||||
'id.required' => '优惠券ID不能为空',
|
||||
'id.numeric' => '优惠券ID必须为数字'
|
||||
]);
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
throw new ApiException(400201, '优惠券不存在');
|
||||
}
|
||||
$coupon->update($params);
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
], [
|
||||
'id.required' => '优惠券ID不能为空',
|
||||
'id.numeric' => '优惠券ID必须为数字'
|
||||
]);
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
return $this->fail([400202, '优惠券不存在']);
|
||||
}
|
||||
$coupon->show = !$coupon->show;
|
||||
if (!$coupon->save()) {
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function generate(CouponGenerate $request)
|
||||
{
|
||||
if ($request->input('generate_count')) {
|
||||
$this->multiGenerate($request);
|
||||
return;
|
||||
}
|
||||
|
||||
$params = $request->validated();
|
||||
if (!$request->input('id')) {
|
||||
if (!isset($params['code'])) {
|
||||
$params['code'] = Helper::randomChar(8);
|
||||
}
|
||||
if (!Coupon::create($params)) {
|
||||
return $this->fail([500, '创建失败']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Coupon::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
private function multiGenerate(CouponGenerate $request)
|
||||
{
|
||||
$coupons = [];
|
||||
$coupon = $request->validated();
|
||||
$coupon['created_at'] = $coupon['updated_at'] = time();
|
||||
$coupon['show'] = 1;
|
||||
unset($coupon['generate_count']);
|
||||
for ($i = 0; $i < $request->input('generate_count'); $i++) {
|
||||
$coupon['code'] = Helper::randomChar(8);
|
||||
array_push($coupons, $coupon);
|
||||
}
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
if (
|
||||
!Coupon::insert(array_map(function ($item) use ($coupon) {
|
||||
// format data
|
||||
if (isset($item['limit_plan_ids']) && is_array($item['limit_plan_ids'])) {
|
||||
$item['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
|
||||
}
|
||||
if (isset($item['limit_period']) && is_array($item['limit_period'])) {
|
||||
$item['limit_period'] = json_encode($coupon['limit_period']);
|
||||
}
|
||||
return $item;
|
||||
}, $coupons))
|
||||
) {
|
||||
throw new \Exception();
|
||||
}
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return $this->fail([500, '生成失败']);
|
||||
}
|
||||
|
||||
$data = "名称,类型,金额或比例,开始时间,结束时间,可用次数,可用于订阅,券码,生成时间\r\n";
|
||||
foreach ($coupons as $coupon) {
|
||||
$type = ['', '金额', '比例'][$coupon['type']];
|
||||
$value = ['', ($coupon['value'] / 100), $coupon['value']][$coupon['type']];
|
||||
$startTime = date('Y-m-d H:i:s', $coupon['started_at']);
|
||||
$endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
|
||||
$limitUse = $coupon['limit_use'] ?? '不限制';
|
||||
$createTime = date('Y-m-d H:i:s', $coupon['created_at']);
|
||||
$limitPlanIds = isset($coupon['limit_plan_ids']) ? implode("/", $coupon['limit_plan_ids']) : '不限制';
|
||||
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
|
||||
}
|
||||
echo $data;
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
], [
|
||||
'id.required' => '优惠券ID不能为空',
|
||||
'id.numeric' => '优惠券ID必须为数字'
|
||||
]);
|
||||
$coupon = Coupon::find($request->input('id'));
|
||||
if (!$coupon) {
|
||||
return $this->fail([400202, '优惠券不存在']);
|
||||
}
|
||||
if (!$coupon->delete()) {
|
||||
return $this->fail([500, '删除失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
+21
-14
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -16,12 +16,13 @@ class KnowledgeController extends Controller
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$knowledge = Knowledge::find($request->input('id'))->toArray();
|
||||
if (!$knowledge) return $this->fail([400202,'知识不存在']);
|
||||
if (!$knowledge)
|
||||
return $this->fail([400202, '知识不存在']);
|
||||
return $this->success($knowledge);
|
||||
}
|
||||
$data = Knowledge::select(['title', 'id', 'updated_at', 'category', 'show'])
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
->orderBy('sort', 'ASC')
|
||||
->get();
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
@@ -36,14 +37,14 @@ class KnowledgeController extends Controller
|
||||
|
||||
if (!$request->input('id')) {
|
||||
if (!Knowledge::create($params)) {
|
||||
return $this->fail([500,'创建失败']);
|
||||
return $this->fail([500, '创建失败']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Knowledge::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'创建失败']);
|
||||
return $this->fail([500, '创建失败']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,8 +55,8 @@ class KnowledgeController extends Controller
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
],[
|
||||
'id.required' => '知识库ID不能为空'
|
||||
], [
|
||||
'id.required' => '知识库ID不能为空'
|
||||
]);
|
||||
$knowledge = Knowledge::find($request->input('id'));
|
||||
if (!$knowledge) {
|
||||
@@ -69,11 +70,17 @@ class KnowledgeController extends Controller
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function sort(KnowledgeSort $request)
|
||||
public function sort(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'ids' => 'required|array'
|
||||
], [
|
||||
'ids.required' => '参数有误',
|
||||
'ids.array' => '参数有误'
|
||||
]);
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('knowledge_ids') as $k => $v) {
|
||||
foreach ($request->input('ids') as $k => $v) {
|
||||
$knowledge = Knowledge::find($v);
|
||||
$knowledge->timestamps = false;
|
||||
$knowledge->update(['sort' => $k + 1]);
|
||||
@@ -90,15 +97,15 @@ class KnowledgeController extends Controller
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
],[
|
||||
'id.required' => '知识库ID不能为空'
|
||||
], [
|
||||
'id.required' => '知识库ID不能为空'
|
||||
]);
|
||||
$knowledge = Knowledge::find($request->input('id'));
|
||||
if (!$knowledge) {
|
||||
return $this->fail([400202,'知识不存在']);
|
||||
return $this->fail([400202, '知识不存在']);
|
||||
}
|
||||
if (!$knowledge->delete()) {
|
||||
return $this->fail([500,'删除失败']);
|
||||
return $this->fail([500, '删除失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\NoticeSave;
|
||||
use App\Models\Notice;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class NoticeController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
return $this->success(
|
||||
Notice::orderBy('sort', 'ASC')
|
||||
->orderBy('id', 'DESC')
|
||||
->get()
|
||||
);
|
||||
}
|
||||
|
||||
public function save(NoticeSave $request)
|
||||
{
|
||||
$data = $request->only([
|
||||
'title',
|
||||
'content',
|
||||
'img_url',
|
||||
'tags',
|
||||
'show',
|
||||
'popup'
|
||||
]);
|
||||
if (!$request->input('id')) {
|
||||
if (!Notice::create($data)) {
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Notice::find($request->input('id'))->update($data);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function show(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
return $this->fail([500, '公告ID不能为空']);
|
||||
}
|
||||
$notice = Notice::find($request->input('id'));
|
||||
if (!$notice) {
|
||||
return $this->fail([400202, '公告不存在']);
|
||||
}
|
||||
$notice->show = $notice->show ? 0 : 1;
|
||||
if (!$notice->save()) {
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
return $this->fail([422, '公告ID不能为空']);
|
||||
}
|
||||
$notice = Notice::find($request->input('id'));
|
||||
if (!$notice) {
|
||||
return $this->fail([400202, '公告不存在']);
|
||||
}
|
||||
if (!$notice->delete()) {
|
||||
return $this->fail([500, '删除失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function sort(Request $request)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'ids' => 'required|array'
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
foreach ($params['ids'] as $k => $v) {
|
||||
$notice = Notice::findOrFail($v);
|
||||
$notice->update(['sort' => $k + 1]);
|
||||
}
|
||||
DB::commit();
|
||||
return $this->success(true);
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '排序保存失败']);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\PlanService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
||||
class OrderController extends Controller
|
||||
{
|
||||
|
||||
public function detail(Request $request)
|
||||
{
|
||||
$order = Order::with(['user', 'plan', 'commission_log'])->find($request->input('id'));
|
||||
if (!$order)
|
||||
return $this->fail([400202, '订单不存在']);
|
||||
if ($order->surplus_order_ids) {
|
||||
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
|
||||
}
|
||||
$order['period'] = PlanService::getLegacyPeriod($order->period);
|
||||
return $this->success($order);
|
||||
}
|
||||
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current', 1);
|
||||
$pageSize = $request->input('pageSize', 10);
|
||||
$orderModel = Order::with('plan:id,name');
|
||||
|
||||
if ($request->boolean('is_commission')) {
|
||||
$orderModel->whereNotNull('invite_user_id')
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('commission_balance', '>', 0);
|
||||
}
|
||||
|
||||
$this->applyFiltersAndSorts($request, $orderModel);
|
||||
|
||||
return response()->json(
|
||||
$orderModel
|
||||
->latest('created_at')
|
||||
->paginate(
|
||||
perPage: $pageSize,
|
||||
page: $current
|
||||
)->through(fn($order) => [
|
||||
...$order->toArray(),
|
||||
'period' => PlanService::getLegacyPeriod($order->period)
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
private function applyFiltersAndSorts(Request $request, Builder $builder): void
|
||||
{
|
||||
$this->applyFilters($request, $builder);
|
||||
$this->applySorting($request, $builder);
|
||||
}
|
||||
|
||||
private function applyFilters(Request $request, Builder $builder): void
|
||||
{
|
||||
if (!$request->has('filter')) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($request->input('filter'))->each(function ($filter) use ($builder) {
|
||||
$field = $filter['id'];
|
||||
$value = $filter['value'];
|
||||
|
||||
$builder->where(function ($query) use ($field, $value) {
|
||||
$this->buildFilterQuery($query, $field, $value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private function buildFilterQuery(Builder $query, string $field, mixed $value): void
|
||||
{
|
||||
// Handle array values for 'in' operations
|
||||
if (is_array($value)) {
|
||||
$query->whereIn($field, $value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle operator-based filtering
|
||||
if (!is_string($value) || !str_contains($value, ':')) {
|
||||
$query->where($field, 'like', "%{$value}%");
|
||||
return;
|
||||
}
|
||||
|
||||
[$operator, $filterValue] = explode(':', $value, 2);
|
||||
|
||||
// Convert numeric strings to appropriate type
|
||||
if (is_numeric($filterValue)) {
|
||||
$filterValue = strpos($filterValue, '.') !== false
|
||||
? (float) $filterValue
|
||||
: (int) $filterValue;
|
||||
}
|
||||
|
||||
// Apply operator
|
||||
$query->where($field, match (strtolower($operator)) {
|
||||
'eq' => '=',
|
||||
'gt' => '>',
|
||||
'gte' => '>=',
|
||||
'lt' => '<',
|
||||
'lte' => '<=',
|
||||
'like' => 'like',
|
||||
'notlike' => 'not like',
|
||||
'null' => static fn($q) => $q->whereNull($queryField),
|
||||
'notnull' => static fn($q) => $q->whereNotNull($queryField),
|
||||
default => 'like'
|
||||
}, match (strtolower($operator)) {
|
||||
'like', 'notlike' => "%{$filterValue}%",
|
||||
'null', 'notnull' => null,
|
||||
default => $filterValue
|
||||
});
|
||||
}
|
||||
|
||||
private function applySorting(Request $request, Builder $builder): void
|
||||
{
|
||||
if (!$request->has('sort')) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($request->input('sort'))->each(function ($sort) use ($builder) {
|
||||
$field = $sort['id'];
|
||||
$direction = $sort['desc'] ? 'DESC' : 'ASC';
|
||||
$builder->orderBy($field, $direction);
|
||||
});
|
||||
}
|
||||
|
||||
public function paid(Request $request)
|
||||
{
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202, '订单不存在']);
|
||||
}
|
||||
if ($order->status !== 0)
|
||||
return $this->fail([400, '只能对待支付的订单进行操作']);
|
||||
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->paid('manual_operation')) {
|
||||
return $this->fail([500, '更新失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202, '订单不存在']);
|
||||
}
|
||||
if ($order->status !== 0)
|
||||
return $this->fail([400, '只能对待支付的订单进行操作']);
|
||||
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
return $this->fail([400, '更新失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function update(OrderUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'commission_status'
|
||||
]);
|
||||
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
return $this->fail([400202, '订单不存在']);
|
||||
}
|
||||
|
||||
try {
|
||||
$order->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '更新失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function assign(OrderAssign $request)
|
||||
{
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
|
||||
if (!$user) {
|
||||
return $this->fail([400202, '该用户不存在']);
|
||||
}
|
||||
|
||||
if (!$plan) {
|
||||
return $this->fail([400202, '该订阅不存在']);
|
||||
}
|
||||
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($user->id)) {
|
||||
return $this->fail([400, '该用户还有待支付的订单,无法分配']);
|
||||
}
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
$order->user_id = $user->id;
|
||||
$order->plan_id = $plan->id;
|
||||
$order->period = PlanService::getPeriodKey($request->input('period'));
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->total_amount = $request->input('total_amount');
|
||||
|
||||
if (PlanService::getPeriodKey($order->period) === Plan::PERIOD_RESET_TRAFFIC) {
|
||||
$order->type = Order::TYPE_RESET_TRAFFIC;
|
||||
} else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) {
|
||||
$order->type = Order::TYPE_UPGRADE;
|
||||
} else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) {
|
||||
$order->type = Order::TYPE_RENEWAL;
|
||||
} else {
|
||||
$order->type = Order::TYPE_NEW_PURCHASE;
|
||||
}
|
||||
|
||||
$orderService->setInvite($user);
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return $this->fail([500, '订单创建失败']);
|
||||
}
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return $this->success($order->trade_no);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -38,7 +38,7 @@ class PaymentController extends Controller
|
||||
public function getPaymentForm(Request $request)
|
||||
{
|
||||
$paymentService = new PaymentService($request->input('payment'), $request->input('id'));
|
||||
return $this->success($paymentService->form());
|
||||
return $this->success(collect($paymentService->form())->values());
|
||||
}
|
||||
|
||||
public function show(Request $request)
|
||||
Executable → Regular
+44
-34
@@ -1,16 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\PlanSave;
|
||||
use App\Http\Requests\Admin\PlanSort;
|
||||
use App\Http\Requests\Admin\PlanUpdate;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\PlanService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@@ -18,24 +13,34 @@ class PlanController extends Controller
|
||||
{
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$counts = PlanService::countActiveUsers();
|
||||
$plans = Plan::orderBy('sort', 'ASC')->get();
|
||||
foreach ($plans as $k => $v) {
|
||||
$plans[$k]->count = 0;
|
||||
foreach ($counts as $kk => $vv) {
|
||||
if ($plans[$k]->id === $counts[$kk]->plan_id) $plans[$k]->count = $counts[$kk]->count;
|
||||
}
|
||||
}
|
||||
$plans = Plan::orderBy('sort', 'ASC')
|
||||
->with([
|
||||
'group:id,name'
|
||||
])
|
||||
->withCount('users')
|
||||
->get();
|
||||
|
||||
return $this->success($plans);
|
||||
}
|
||||
|
||||
public function save(PlanSave $request)
|
||||
public function save(Request $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
$params = $request->validate([
|
||||
'id' => 'nullable|integer',
|
||||
'name' => 'required|string',
|
||||
'content' => 'nullable|string',
|
||||
'reset_traffic_method' => 'integer|nullable',
|
||||
'transfer_enable' => 'integer|required',
|
||||
'prices' => 'array|nullable',
|
||||
'group_id' => 'integer|nullable',
|
||||
'speed_limit' => 'integer|nullable',
|
||||
'device_limit' => 'integer|nullable',
|
||||
'capacity_limit' => 'integer|nullable',
|
||||
]);
|
||||
if ($request->input('id')) {
|
||||
$plan = Plan::find($request->input('id'));
|
||||
if (!$plan) {
|
||||
return $this->fail([400202 ,'该订阅不存在']);
|
||||
return $this->fail([400202, '该订阅不存在']);
|
||||
}
|
||||
DB::beginTransaction();
|
||||
// update user group id and transfer
|
||||
@@ -44,7 +49,8 @@ class PlanController extends Controller
|
||||
User::where('plan_id', $plan->id)->update([
|
||||
'group_id' => $params['group_id'],
|
||||
'transfer_enable' => $params['transfer_enable'] * 1073741824,
|
||||
'speed_limit' => $params['speed_limit']
|
||||
'speed_limit' => $params['speed_limit'],
|
||||
'device_limit' => $params['device_limit'],
|
||||
]);
|
||||
}
|
||||
$plan->update($params);
|
||||
@@ -53,11 +59,11 @@ class PlanController extends Controller
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
}
|
||||
if (!Plan::create($params)) {
|
||||
return $this->fail([500 ,'创建失败']);
|
||||
return $this->fail([500, '创建失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
@@ -65,57 +71,61 @@ class PlanController extends Controller
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if (Order::where('plan_id', $request->input('id'))->first()) {
|
||||
return $this->fail([400201 ,'该订阅下存在订单无法删除']);
|
||||
return $this->fail([400201, '该订阅下存在订单无法删除']);
|
||||
}
|
||||
if (User::where('plan_id', $request->input('id'))->first()) {
|
||||
return $this->fail([400201 ,'该订阅下存在用户无法删除']);
|
||||
return $this->fail([400201, '该订阅下存在用户无法删除']);
|
||||
}
|
||||
if ($request->input('id')) {
|
||||
$plan = Plan::find($request->input('id'));
|
||||
if (!$plan) {
|
||||
return $this->fail([400202 ,'该订阅不存在']);
|
||||
return $this->fail([400202, '该订阅不存在']);
|
||||
}
|
||||
}
|
||||
return $this->success($plan->delete());
|
||||
}
|
||||
|
||||
public function update(PlanUpdate $request)
|
||||
public function update(Request $request)
|
||||
{
|
||||
$updateData = $request->only([
|
||||
'show',
|
||||
'renew'
|
||||
'renew',
|
||||
'sell'
|
||||
]);
|
||||
|
||||
$plan = Plan::find($request->input('id'));
|
||||
if (!$plan) {
|
||||
return $this->fail([400202 ,'该订阅不存在']);
|
||||
return $this->fail([400202, '该订阅不存在']);
|
||||
}
|
||||
|
||||
try {
|
||||
$plan->update($updateData);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
|
||||
return $this->success();
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function sort(PlanSort $request)
|
||||
public function sort(Request $request)
|
||||
{
|
||||
|
||||
try{
|
||||
$params = $request->validate([
|
||||
'ids' => 'required|array'
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
foreach ($request->input('plan_ids') as $k => $v) {
|
||||
foreach ($params['ids'] as $k => $v) {
|
||||
if (!Plan::find($v)->update(['sort' => $k + 1])) {
|
||||
throw new \Exception();
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
}catch (\Exception $e){
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500 ,'保存失败']);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plugin;
|
||||
use App\Services\Plugin\PluginManager;
|
||||
use App\Services\Plugin\PluginConfigService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class PluginController extends Controller
|
||||
{
|
||||
protected PluginManager $pluginManager;
|
||||
protected PluginConfigService $configService;
|
||||
|
||||
public function __construct(
|
||||
PluginManager $pluginManager,
|
||||
PluginConfigService $configService
|
||||
) {
|
||||
$this->pluginManager = $pluginManager;
|
||||
$this->configService = $configService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件列表
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$installedPlugins = Plugin::get()
|
||||
->keyBy('code')
|
||||
->toArray();
|
||||
$pluginPath = base_path('plugins');
|
||||
$plugins = [];
|
||||
|
||||
if (File::exists($pluginPath)) {
|
||||
$directories = File::directories($pluginPath);
|
||||
foreach ($directories as $directory) {
|
||||
$pluginName = basename($directory);
|
||||
$configFile = $directory . '/config.json';
|
||||
if (File::exists($configFile)) {
|
||||
$config = json_decode(File::get($configFile), true);
|
||||
$installed = isset($installedPlugins[$pluginName]);
|
||||
// 使用配置服务获取配置
|
||||
$pluginConfig = $installed ? $this->configService->getConfig($pluginName) : ($config['config'] ?? []);
|
||||
$plugins[] = [
|
||||
'code' => $config['code'],
|
||||
'name' => $config['name'],
|
||||
'version' => $config['version'],
|
||||
'description' => $config['description'],
|
||||
'author' => $config['author'],
|
||||
'is_installed' => $installed,
|
||||
'is_enabled' => $installed ? $installedPlugins[$pluginName]['is_enabled'] : false,
|
||||
'config' => $pluginConfig,
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'data' => $plugins
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装插件
|
||||
*/
|
||||
public function install(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string'
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->pluginManager->install($request->input('code'));
|
||||
return response()->json([
|
||||
'message' => '插件安装成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '插件安装失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载插件
|
||||
*/
|
||||
public function uninstall(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string'
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->pluginManager->uninstall($request->input('code'));
|
||||
return response()->json([
|
||||
'message' => '插件卸载成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '插件卸载失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用插件
|
||||
*/
|
||||
public function enable(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string'
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->pluginManager->enable($request->input('code'));
|
||||
return response()->json([
|
||||
'message' => '插件启用成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '插件启用失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁用插件
|
||||
*/
|
||||
public function disable(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string'
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->pluginManager->disable($request->input('code'));
|
||||
return response()->json([
|
||||
'message' => '插件禁用成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '插件禁用失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件配置
|
||||
*/
|
||||
public function getConfig(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string'
|
||||
]);
|
||||
|
||||
try {
|
||||
$config = $this->configService->getConfig($request->input('code'));
|
||||
return response()->json([
|
||||
'data' => $config
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '获取配置失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新插件配置
|
||||
*/
|
||||
public function updateConfig(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'code' => 'required|string',
|
||||
'config' => 'required|array'
|
||||
]);
|
||||
|
||||
try {
|
||||
$this->configService->updateConfig(
|
||||
$request->input('code'),
|
||||
$request->input('config')
|
||||
);
|
||||
|
||||
return response()->json([
|
||||
'message' => '配置更新成功'
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
return response()->json([
|
||||
'message' => '配置更新失败:' . $e->getMessage()
|
||||
], 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin\Server;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
|
||||
class GroupController extends Controller
|
||||
{
|
||||
public function fetch(Request $request): JsonResponse
|
||||
{
|
||||
|
||||
$serverGroups = ServerGroup::query()
|
||||
->orderByDesc('id')
|
||||
->withCount('users')
|
||||
->get()
|
||||
->transform(function ($group) {
|
||||
$group->server_count = $group->servers()->count();
|
||||
return $group;
|
||||
});
|
||||
|
||||
return $this->success($serverGroups);
|
||||
}
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (empty($request->input('name'))) {
|
||||
return $this->fail([422, '组名不能为空']);
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$serverGroup = ServerGroup::find($request->input('id'));
|
||||
} else {
|
||||
$serverGroup = new ServerGroup();
|
||||
}
|
||||
|
||||
$serverGroup->name = $request->input('name');
|
||||
return $this->success($serverGroup->save());
|
||||
}
|
||||
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$groupId = $request->input('id');
|
||||
|
||||
$serverGroup = ServerGroup::find($groupId);
|
||||
if (!$serverGroup) {
|
||||
return $this->fail([400202, '组不存在']);
|
||||
}
|
||||
if (Server::whereJsonContains('group_ids', $groupId)->exists()) {
|
||||
return $this->fail([400, '该组已被节点所使用,无法删除']);
|
||||
}
|
||||
|
||||
if (Plan::where('group_id', $groupId)->exists()) {
|
||||
return $this->fail([400, '该组已被订阅所使用,无法删除']);
|
||||
}
|
||||
if (User::where('group_id', $groupId)->exists()) {
|
||||
return $this->fail([400, '该组已被用户所使用,无法删除']);
|
||||
}
|
||||
return $this->success($serverGroup->delete());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\ServerSave;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ManageController extends Controller
|
||||
{
|
||||
public function getNodes(Request $request)
|
||||
{
|
||||
$servers = collect(ServerService::getAllServers())->map(function ($item) {
|
||||
$item['groups'] = ServerGroup::whereIn('id', $item['group_ids'])->get(['name', 'id']);
|
||||
$item['parent'] = $item->parent;
|
||||
return $item;
|
||||
});
|
||||
return $this->success($servers);
|
||||
}
|
||||
|
||||
public function sort(Request $request)
|
||||
{
|
||||
ini_set('post_max_size', '1m');
|
||||
$params = $request->validate([
|
||||
'*.id' => 'numeric',
|
||||
'*.order' => 'numeric'
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
collect($params)->each(function ($item) {
|
||||
if (isset($item['id']) && isset($item['order'])) {
|
||||
Server::where('id', $item['id'])->update(['sort' => $item['order']]);
|
||||
}
|
||||
});
|
||||
DB::commit();
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function save(ServerSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
if ($request->input('id')) {
|
||||
$server = Server::find($request->input('id'));
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
try {
|
||||
$server->update($params);
|
||||
return $this->success(true);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Server::create($params);
|
||||
return $this->success(true);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500, '创建失败']);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public function update(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
'show' => 'integer',
|
||||
]);
|
||||
|
||||
if (Server::where('id', $request->id)->update(['show' => $request->show]) === false) {
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function drop(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|integer',
|
||||
]);
|
||||
if (Server::where('id', $request->id)->delete() === false) {
|
||||
return $this->fail([500, '删除失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 复制节点
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = Server::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
$server->code = null;
|
||||
if (!$server) {
|
||||
return $this->fail([400202, '服务器不存在']);
|
||||
}
|
||||
Server::create($server->toArray());
|
||||
return $this->success(true);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin\Server;
|
||||
namespace App\Http\Controllers\V2\Admin\Server;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
@@ -5,9 +5,7 @@ namespace App\Http\Controllers\V2\Admin;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CommissionLog;
|
||||
use App\Models\Order;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\ServerVmess;
|
||||
use App\Models\Server;
|
||||
use App\Models\Stat;
|
||||
use App\Models\StatServer;
|
||||
use App\Models\StatUser;
|
||||
@@ -15,78 +13,497 @@ use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use App\Services\StatisticalService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class StatController extends Controller
|
||||
{
|
||||
public function override(Request $request)
|
||||
private $service;
|
||||
public function __construct(StatisticalService $service)
|
||||
{
|
||||
$params = $request->validate([
|
||||
'start_at' => '',
|
||||
'end_at' => ''
|
||||
$this->service = $service;
|
||||
}
|
||||
public function getOverride(Request $request)
|
||||
{
|
||||
// 获取在线节点数
|
||||
$onlineNodes = Server::all()->filter(function ($server) {
|
||||
$server->loadServerStatus();
|
||||
return $server->is_online;
|
||||
})->count();
|
||||
// 获取在线设备数和在线用户数
|
||||
$onlineDevices = User::where('t', '>=', time() - 600)
|
||||
->sum('online_count');
|
||||
$onlineUsers = User::where('t', '>=', time() - 600)
|
||||
->count();
|
||||
|
||||
// 获取今日流量统计
|
||||
$todayStart = strtotime('today');
|
||||
$todayTraffic = StatServer::where('record_at', '>=', $todayStart)
|
||||
->where('record_at', '<', time())
|
||||
->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
// 获取本月流量统计
|
||||
$monthStart = strtotime(date('Y-m-1'));
|
||||
$monthTraffic = StatServer::where('record_at', '>=', $monthStart)
|
||||
->where('record_at', '<', time())
|
||||
->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
// 获取总流量统计
|
||||
$totalTraffic = StatServer::selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
'month_income' => Order::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->count(),
|
||||
'ticket_pending_total' => Ticket::where('status', 0)
|
||||
->count(),
|
||||
'commission_pending_total' => Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->where('commission_balance', '>', 0)
|
||||
->count(),
|
||||
'day_income' => Order::where('created_at', '>=', strtotime(date('Y-m-d')))
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'last_month_income' => Order::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount'),
|
||||
'commission_month_payout' => CommissionLog::where('created_at', '>=', strtotime(date('Y-m-1')))
|
||||
->where('created_at', '<', time())
|
||||
->sum('get_amount'),
|
||||
'commission_last_month_payout' => CommissionLog::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1'))))
|
||||
->where('created_at', '<', strtotime(date('Y-m-1')))
|
||||
->sum('get_amount'),
|
||||
// 新增统计数据
|
||||
'online_nodes' => $onlineNodes,
|
||||
'online_devices' => $onlineDevices,
|
||||
'online_users' => $onlineUsers,
|
||||
'today_traffic' => [
|
||||
'upload' => $todayTraffic->upload ?? 0,
|
||||
'download' => $todayTraffic->download ?? 0,
|
||||
'total' => $todayTraffic->total ?? 0
|
||||
],
|
||||
'month_traffic' => [
|
||||
'upload' => $monthTraffic->upload ?? 0,
|
||||
'download' => $monthTraffic->download ?? 0,
|
||||
'total' => $monthTraffic->total ?? 0
|
||||
],
|
||||
'total_traffic' => [
|
||||
'upload' => $totalTraffic->upload ?? 0,
|
||||
'download' => $totalTraffic->download ?? 0,
|
||||
'total' => $totalTraffic->total ?? 0
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get order statistics with filtering and pagination
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getOrder(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'start_date' => 'nullable|date_format:Y-m-d',
|
||||
'end_date' => 'nullable|date_format:Y-m-d',
|
||||
'type' => 'nullable|in:paid_total,paid_count,commission_total,commission_count',
|
||||
]);
|
||||
|
||||
if (isset($params['start_at']) && isset($params['end_at'])) {
|
||||
$stats = Stat::where('record_at', '>=', $params['start_at'])
|
||||
->where('record_at', '<', $params['end_at'])
|
||||
$query = Stat::where('record_type', 'd');
|
||||
|
||||
// Apply date filters
|
||||
if ($request->input('start_date')) {
|
||||
$query->where('record_at', '>=', strtotime($request->input('start_date')));
|
||||
}
|
||||
if ($request->input('end_date')) {
|
||||
$query->where('record_at', '<=', strtotime($request->input('end_date') . ' 23:59:59'));
|
||||
}
|
||||
|
||||
$statistics = $query->orderBy('record_at', 'DESC')
|
||||
->get();
|
||||
|
||||
$summary = [
|
||||
'paid_total' => 0,
|
||||
'paid_count' => 0,
|
||||
'commission_total' => 0,
|
||||
'commission_count' => 0,
|
||||
'start_date' => $request->input('start_date', date('Y-m-d', $statistics->last()?->record_at)),
|
||||
'end_date' => $request->input('end_date', date('Y-m-d', $statistics->first()?->record_at)),
|
||||
'avg_paid_amount' => 0,
|
||||
'avg_commission_amount' => 0
|
||||
];
|
||||
|
||||
$dailyStats = [];
|
||||
foreach ($statistics as $statistic) {
|
||||
$date = date('Y-m-d', $statistic['record_at']);
|
||||
|
||||
// Update summary
|
||||
$summary['paid_total'] += $statistic['paid_total'];
|
||||
$summary['paid_count'] += $statistic['paid_count'];
|
||||
$summary['commission_total'] += $statistic['commission_total'];
|
||||
$summary['commission_count'] += $statistic['commission_count'];
|
||||
|
||||
// Calculate daily stats
|
||||
$dailyData = [
|
||||
'date' => $date,
|
||||
'paid_total' => $statistic['paid_total'],
|
||||
'paid_count' => $statistic['paid_count'],
|
||||
'commission_total' => $statistic['commission_total'],
|
||||
'commission_count' => $statistic['commission_count'],
|
||||
'avg_order_amount' => $statistic['paid_count'] > 0 ? round($statistic['paid_total'] / $statistic['paid_count'], 2) : 0,
|
||||
'avg_commission_amount' => $statistic['commission_count'] > 0 ? round($statistic['commission_total'] / $statistic['commission_count'], 2) : 0
|
||||
];
|
||||
|
||||
if ($request->input('type')) {
|
||||
$dailyStats[] = [
|
||||
'date' => $date,
|
||||
'value' => $statistic[$request->input('type')],
|
||||
'type' => $this->getTypeLabel($request->input('type'))
|
||||
];
|
||||
} else {
|
||||
$dailyStats[] = $dailyData;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate averages for summary
|
||||
if ($summary['paid_count'] > 0) {
|
||||
$summary['avg_paid_amount'] = round($summary['paid_total'] / $summary['paid_count'], 2);
|
||||
}
|
||||
if ($summary['commission_count'] > 0) {
|
||||
$summary['avg_commission_amount'] = round($summary['commission_total'] / $summary['commission_count'], 2);
|
||||
}
|
||||
|
||||
// Add percentage calculations to summary
|
||||
$summary['commission_rate'] = $summary['paid_total'] > 0
|
||||
? round(($summary['commission_total'] / $summary['paid_total']) * 100, 2)
|
||||
: 0;
|
||||
|
||||
return [
|
||||
'code' => 0,
|
||||
'message' => 'success',
|
||||
'data' => [
|
||||
'list' => array_reverse($dailyStats),
|
||||
'summary' => $summary,
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get human readable label for statistic type
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
*/
|
||||
private function getTypeLabel(string $type): string
|
||||
{
|
||||
return match ($type) {
|
||||
'paid_total' => '收款金额',
|
||||
'paid_count' => '收款笔数',
|
||||
'commission_total' => '佣金金额(已发放)',
|
||||
'commission_count' => '佣金笔数(已发放)',
|
||||
default => $type
|
||||
};
|
||||
}
|
||||
|
||||
// 获取当日实时流量排行
|
||||
public function getServerLastRank()
|
||||
{
|
||||
$data = $this->service->getServerRank();
|
||||
return $this->success(data: $data);
|
||||
}
|
||||
// 获取昨日节点流量排行
|
||||
public function getServerYesterdayRank()
|
||||
{
|
||||
$data = $this->service->getServerRank('yesterday');
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
public function getStatUser(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|integer'
|
||||
]);
|
||||
|
||||
$pageSize = $request->input('pageSize', 10);
|
||||
$records = StatUser::orderBy('record_at', 'DESC')
|
||||
->where('user_id', $request->input('user_id'))
|
||||
->paginate($pageSize);
|
||||
|
||||
$data = $records->items();
|
||||
return [
|
||||
'data' => $data,
|
||||
'total' => $records->total(),
|
||||
];
|
||||
}
|
||||
|
||||
public function getStatRecord(Request $request)
|
||||
{
|
||||
return [
|
||||
'data' => $this->service->getStatRecord($request->input('type'))
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get comprehensive statistics data including income, users, and growth rates
|
||||
*/
|
||||
public function getStats()
|
||||
{
|
||||
$currentMonthStart = strtotime(date('Y-m-01'));
|
||||
$lastMonthStart = strtotime('-1 month', $currentMonthStart);
|
||||
$twoMonthsAgoStart = strtotime('-2 month', $currentMonthStart);
|
||||
|
||||
// Today's start timestamp
|
||||
$todayStart = strtotime('today');
|
||||
$yesterdayStart = strtotime('-1 day', $todayStart);
|
||||
|
||||
// 获取在线节点数
|
||||
$onlineNodes = Server::all()->filter(function ($server) {
|
||||
$server->loadServerStatus();
|
||||
return $server->is_online;
|
||||
})->count();
|
||||
|
||||
// 获取在线设备数和在线用户数
|
||||
$onlineDevices = User::where('t', '>=', time() - 600)
|
||||
->sum('online_count');
|
||||
$onlineUsers = User::where('t', '>=', time() - 600)
|
||||
->count();
|
||||
|
||||
// 获取今日流量统计
|
||||
$todayTraffic = StatServer::where('record_at', '>=', $todayStart)
|
||||
->where('record_at', '<', time())
|
||||
->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
// 获取本月流量统计
|
||||
$monthTraffic = StatServer::where('record_at', '>=', $currentMonthStart)
|
||||
->where('record_at', '<', time())
|
||||
->selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
// 获取总流量统计
|
||||
$totalTraffic = StatServer::selectRaw('SUM(u) as upload, SUM(d) as download, SUM(u + d) as total')
|
||||
->first();
|
||||
|
||||
// Today's income
|
||||
$todayIncome = Order::where('created_at', '>=', $todayStart)
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount');
|
||||
|
||||
// Yesterday's income for day growth calculation
|
||||
$yesterdayIncome = Order::where('created_at', '>=', $yesterdayStart)
|
||||
->where('created_at', '<', $todayStart)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount');
|
||||
|
||||
// Current month income
|
||||
$currentMonthIncome = Order::where('created_at', '>=', $currentMonthStart)
|
||||
->where('created_at', '<', time())
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount');
|
||||
|
||||
// Last month income
|
||||
$lastMonthIncome = Order::where('created_at', '>=', $lastMonthStart)
|
||||
->where('created_at', '<', $currentMonthStart)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount');
|
||||
|
||||
// Last month commission payout
|
||||
$lastMonthCommissionPayout = CommissionLog::where('created_at', '>=', $lastMonthStart)
|
||||
->where('created_at', '<', $currentMonthStart)
|
||||
->sum('get_amount');
|
||||
|
||||
// Current month commission payout
|
||||
$currentMonthCommissionPayout = CommissionLog::where('created_at', '>=', $currentMonthStart)
|
||||
->where('created_at', '<', time())
|
||||
->sum('get_amount');
|
||||
|
||||
// Current month new users
|
||||
$currentMonthNewUsers = User::where('created_at', '>=', $currentMonthStart)
|
||||
->where('created_at', '<', time())
|
||||
->count();
|
||||
|
||||
// Total users
|
||||
$totalUsers = User::count();
|
||||
|
||||
// Active users (users with valid subscription)
|
||||
$activeUsers = User::where(function ($query) {
|
||||
$query->where('expired_at', '>=', time())
|
||||
->orWhere('expired_at', NULL);
|
||||
})->count();
|
||||
|
||||
// Previous month income for growth calculation
|
||||
$twoMonthsAgoIncome = Order::where('created_at', '>=', $twoMonthsAgoStart)
|
||||
->where('created_at', '<', $lastMonthStart)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->sum('total_amount');
|
||||
|
||||
// Previous month commission for growth calculation
|
||||
$twoMonthsAgoCommission = CommissionLog::where('created_at', '>=', $twoMonthsAgoStart)
|
||||
->where('created_at', '<', $lastMonthStart)
|
||||
->sum('get_amount');
|
||||
|
||||
// Previous month users for growth calculation
|
||||
$lastMonthNewUsers = User::where('created_at', '>=', $lastMonthStart)
|
||||
->where('created_at', '<', $currentMonthStart)
|
||||
->count();
|
||||
|
||||
// Calculate growth rates
|
||||
$monthIncomeGrowth = $lastMonthIncome > 0 ? round(($currentMonthIncome - $lastMonthIncome) / $lastMonthIncome * 100, 1) : 0;
|
||||
$lastMonthIncomeGrowth = $twoMonthsAgoIncome > 0 ? round(($lastMonthIncome - $twoMonthsAgoIncome) / $twoMonthsAgoIncome * 100, 1) : 0;
|
||||
$commissionGrowth = $twoMonthsAgoCommission > 0 ? round(($lastMonthCommissionPayout - $twoMonthsAgoCommission) / $twoMonthsAgoCommission * 100, 1) : 0;
|
||||
$userGrowth = $lastMonthNewUsers > 0 ? round(($currentMonthNewUsers - $lastMonthNewUsers) / $lastMonthNewUsers * 100, 1) : 0;
|
||||
$dayIncomeGrowth = $yesterdayIncome > 0 ? round(($todayIncome - $yesterdayIncome) / $yesterdayIncome * 100, 1) : 0;
|
||||
|
||||
// 获取待处理工单和佣金数据
|
||||
$ticketPendingTotal = Ticket::where('status', 0)->count();
|
||||
$commissionPendingTotal = Order::where('commission_status', 0)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->whereIn('status', [Order::STATUS_COMPLETED])
|
||||
->where('commission_balance', '>', 0)
|
||||
->count();
|
||||
|
||||
return [
|
||||
'data' => [
|
||||
// 收入相关
|
||||
'todayIncome' => $todayIncome,
|
||||
'dayIncomeGrowth' => $dayIncomeGrowth,
|
||||
'currentMonthIncome' => $currentMonthIncome,
|
||||
'lastMonthIncome' => $lastMonthIncome,
|
||||
'monthIncomeGrowth' => $monthIncomeGrowth,
|
||||
'lastMonthIncomeGrowth' => $lastMonthIncomeGrowth,
|
||||
|
||||
// 佣金相关
|
||||
'currentMonthCommissionPayout' => $currentMonthCommissionPayout,
|
||||
'lastMonthCommissionPayout' => $lastMonthCommissionPayout,
|
||||
'commissionGrowth' => $commissionGrowth,
|
||||
'commissionPendingTotal' => $commissionPendingTotal,
|
||||
|
||||
// 用户相关
|
||||
'currentMonthNewUsers' => $currentMonthNewUsers,
|
||||
'totalUsers' => $totalUsers,
|
||||
'activeUsers' => $activeUsers,
|
||||
'userGrowth' => $userGrowth,
|
||||
'onlineUsers' => $onlineUsers,
|
||||
'onlineDevices' => $onlineDevices,
|
||||
|
||||
// 工单相关
|
||||
'ticketPendingTotal' => $ticketPendingTotal,
|
||||
|
||||
// 节点相关
|
||||
'onlineNodes' => $onlineNodes,
|
||||
|
||||
// 流量统计
|
||||
'todayTraffic' => [
|
||||
'upload' => $todayTraffic->upload ?? 0,
|
||||
'download' => $todayTraffic->download ?? 0,
|
||||
'total' => $todayTraffic->total ?? 0
|
||||
],
|
||||
'monthTraffic' => [
|
||||
'upload' => $monthTraffic->upload ?? 0,
|
||||
'download' => $monthTraffic->download ?? 0,
|
||||
'total' => $monthTraffic->total ?? 0
|
||||
],
|
||||
'totalTraffic' => [
|
||||
'upload' => $totalTraffic->upload ?? 0,
|
||||
'download' => $totalTraffic->download ?? 0,
|
||||
'total' => $totalTraffic->total ?? 0
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get traffic ranking data for nodes or users
|
||||
*
|
||||
* @param Request $request
|
||||
* @return array
|
||||
*/
|
||||
public function getTrafficRank(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'type' => 'required|in:node,user',
|
||||
'start_time' => 'nullable|integer|min:1000000000|max:9999999999',
|
||||
'end_time' => 'nullable|integer|min:1000000000|max:9999999999'
|
||||
]);
|
||||
|
||||
$type = $request->input('type');
|
||||
$startDate = $request->input('start_time', strtotime('-7 days'));
|
||||
$endDate = $request->input('end_time', time());
|
||||
$previousStartDate = $startDate - ($endDate - $startDate);
|
||||
$previousEndDate = $startDate;
|
||||
|
||||
if ($type === 'node') {
|
||||
// Get node traffic data
|
||||
$currentData = StatServer::selectRaw('server_id as id, SUM(u + d) as value')
|
||||
->where('record_at', '>=', $startDate)
|
||||
->where('record_at', '<=', $endDate)
|
||||
->groupBy('server_id')
|
||||
->orderBy('value', 'DESC')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
// Get previous period data for comparison
|
||||
$previousData = StatServer::selectRaw('server_id as id, SUM(u + d) as value')
|
||||
->where('record_at', '>=', $previousStartDate)
|
||||
->where('record_at', '<', $previousEndDate)
|
||||
->whereIn('server_id', $currentData->pluck('id'))
|
||||
->groupBy('server_id')
|
||||
->get()
|
||||
->makeHidden(['record_at', 'created_at', 'updated_at', 'id', 'record_type'])
|
||||
->toArray();
|
||||
->keyBy('id');
|
||||
|
||||
} else {
|
||||
$statisticalService = new StatisticalService();
|
||||
return [
|
||||
'data' => $statisticalService->generateStatData()
|
||||
// Get user traffic data
|
||||
$currentData = StatUser::selectRaw('user_id as id, SUM(u + d) as value')
|
||||
->where('record_at', '>=', $startDate)
|
||||
->where('record_at', '<=', $endDate)
|
||||
->groupBy('user_id')
|
||||
->orderBy('value', 'DESC')
|
||||
->limit(10)
|
||||
->get();
|
||||
|
||||
// Get previous period data for comparison
|
||||
$previousData = StatUser::selectRaw('user_id as id, SUM(u + d) as value')
|
||||
->where('record_at', '>=', $previousStartDate)
|
||||
->where('record_at', '<', $previousEndDate)
|
||||
->whereIn('user_id', $currentData->pluck('id'))
|
||||
->groupBy('user_id')
|
||||
->get()
|
||||
->keyBy('id');
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach ($currentData as $data) {
|
||||
$previousValue = isset($previousData[$data->id]) ? $previousData[$data->id]->value : 0;
|
||||
$change = $previousValue > 0 ? round(($data->value - $previousValue) / $previousValue * 100, 1) : 0;
|
||||
|
||||
$name = $type === 'node'
|
||||
? optional(Server::find($data->id))->name ?? "Node {$data->id}"
|
||||
: optional(User::find($data->id))->email ?? "User {$data->id}";
|
||||
|
||||
$result[] = [
|
||||
'id' => (string) $data->id,
|
||||
'name' => $name,
|
||||
'value' => $data->value, // Convert to GB
|
||||
'previousValue' => $previousValue, // Convert to GB
|
||||
'change' => $change,
|
||||
'timestamp' => date('c', $endDate)
|
||||
];
|
||||
}
|
||||
|
||||
$stats = array_reduce($stats, function($carry, $item) {
|
||||
foreach($item as $key => $value) {
|
||||
if(isset($carry[$key]) && $carry[$key]) {
|
||||
$carry[$key] += $value;
|
||||
} else {
|
||||
$carry[$key] = $value;
|
||||
}
|
||||
}
|
||||
return $carry;
|
||||
}, []);
|
||||
|
||||
return [
|
||||
'data' => $stats
|
||||
];
|
||||
}
|
||||
|
||||
public function record(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'type' => 'required|in:paid_total,commission_total,register_count',
|
||||
'start_at' => '',
|
||||
'end_at' => ''
|
||||
]);
|
||||
|
||||
$statisticalService = new StatisticalService();
|
||||
$statisticalService->setStartAt($request->input('start_at'));
|
||||
$statisticalService->setEndAt($request->input('end_at'));
|
||||
return [
|
||||
'data' => $statisticalService->getStatRecord($request->input('type'))
|
||||
];
|
||||
}
|
||||
|
||||
public function ranking(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'type' => 'required|in:server_traffic_rank,user_consumption_rank,invite_rank',
|
||||
'start_at' => '',
|
||||
'end_at' => '',
|
||||
'limit' => 'nullable|integer'
|
||||
]);
|
||||
|
||||
$statisticalService = new StatisticalService();
|
||||
$statisticalService->setStartAt($request->input('start_at'));
|
||||
$statisticalService->setEndAt($request->input('end_at'));
|
||||
return [
|
||||
'data' => $statisticalService->getRanking($request->input('type'), $request->input('limit') ?? 20)
|
||||
'timestamp' => date('c'),
|
||||
'data' => $result
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -1,14 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Log as LogModel;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Laravel\Horizon\Contracts\JobRepository;
|
||||
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
|
||||
use Laravel\Horizon\Contracts\MetricsRepository;
|
||||
@@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ThemeService;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class ThemeController extends Controller
|
||||
{
|
||||
private $themeService;
|
||||
|
||||
public function __construct(ThemeService $themeService)
|
||||
{
|
||||
$this->themeService = $themeService;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传新主题
|
||||
*
|
||||
* @throws ApiException
|
||||
*/
|
||||
public function upload(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'file' => [
|
||||
'required',
|
||||
'file',
|
||||
'mimes:zip',
|
||||
'max:10240', // 最大10MB
|
||||
]
|
||||
], [
|
||||
'file.required' => '请选择主题包文件',
|
||||
'file.file' => '无效的文件类型',
|
||||
'file.mimes' => '主题包必须是zip格式',
|
||||
'file.max' => '主题包大小不能超过10MB'
|
||||
]);
|
||||
|
||||
try {
|
||||
// 检查上传目录权限
|
||||
$uploadPath = storage_path('tmp');
|
||||
if (!File::exists($uploadPath)) {
|
||||
File::makeDirectory($uploadPath, 0755, true);
|
||||
}
|
||||
|
||||
if (!is_writable($uploadPath)) {
|
||||
throw new ApiException('上传目录无写入权限');
|
||||
}
|
||||
|
||||
// 检查主题目录权限
|
||||
$themePath = base_path('theme');
|
||||
if (!is_writable($themePath)) {
|
||||
throw new ApiException('主题目录无写入权限');
|
||||
}
|
||||
|
||||
$file = $request->file('file');
|
||||
|
||||
// 检查文件MIME类型
|
||||
$mimeType = $file->getMimeType();
|
||||
if (!in_array($mimeType, ['application/zip', 'application/x-zip-compressed'])) {
|
||||
throw new ApiException('无效的文件类型,仅支持ZIP格式');
|
||||
}
|
||||
|
||||
// 检查文件名安全性
|
||||
$originalName = $file->getClientOriginalName();
|
||||
if (!preg_match('/^[a-zA-Z0-9\-\_\.]+\.zip$/', $originalName)) {
|
||||
throw new ApiException('主题包文件名只能包含字母、数字、下划线、中划线和点');
|
||||
}
|
||||
|
||||
$this->themeService->upload($file);
|
||||
return $this->success(true);
|
||||
|
||||
} catch (ApiException $e) {
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Theme upload failed', [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $request->file('file')?->getClientOriginalName()
|
||||
]);
|
||||
throw new ApiException('主题上传失败:' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除主题
|
||||
*/
|
||||
public function delete(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required'
|
||||
]);
|
||||
$this->themeService->delete($payload['name']);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有主题和其配置列
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getThemes()
|
||||
{
|
||||
$data = [
|
||||
'themes' => $this->themeService->getList(),
|
||||
'active' => admin_setting('frontend_theme', 'Xboard')
|
||||
];
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换主题
|
||||
*/
|
||||
public function switchTheme(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required'
|
||||
]);
|
||||
$this->themeService->switch($payload['name']);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取主题配置
|
||||
*/
|
||||
public function getThemeConfig(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required'
|
||||
]);
|
||||
$data = $this->themeService->getConfig($payload['name']);
|
||||
return $this->success($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存主题配置
|
||||
*/
|
||||
public function saveThemeConfig(Request $request)
|
||||
{
|
||||
$payload = $request->validate([
|
||||
'name' => 'required',
|
||||
'config' => 'required'
|
||||
]);
|
||||
$this->themeService->updateConfig($payload['name'], $payload['config']);
|
||||
$config = $this->themeService->getConfig($payload['name']);
|
||||
return $this->success($config);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Ticket;
|
||||
use App\Services\TicketService;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TicketController extends Controller
|
||||
{
|
||||
private function applyFiltersAndSorts(Request $request, $builder)
|
||||
{
|
||||
if ($request->has('filter')) {
|
||||
collect($request->input('filter'))->each(function ($filter) use ($builder) {
|
||||
$key = $filter['id'];
|
||||
$value = $filter['value'];
|
||||
$builder->where(function ($query) use ($key, $value) {
|
||||
if (is_array($value)) {
|
||||
$query->whereIn($key, $value);
|
||||
} else {
|
||||
$query->where($key, 'like', "%{$value}%");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->has('sort')) {
|
||||
collect($request->input('sort'))->each(function ($sort) use ($builder) {
|
||||
$key = $sort['id'];
|
||||
$value = $sort['desc'] ? 'DESC' : 'ASC';
|
||||
$builder->orderBy($key, $value);
|
||||
});
|
||||
}
|
||||
}
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
return $this->fetchTicketById($request);
|
||||
} else {
|
||||
return $this->fetchTickets($request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of fetchTicketById
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
private function fetchTicketById(Request $request)
|
||||
{
|
||||
$ticket = Ticket::with('messages', 'user')->find($request->input('id'));
|
||||
|
||||
if (!$ticket) {
|
||||
return $this->fail([400202, '工单不存在']);
|
||||
}
|
||||
|
||||
$ticket->messages->each(function ($message) use ($ticket) {
|
||||
$message->is_me = $message->user_id !== $ticket->user_id;
|
||||
});
|
||||
|
||||
return $this->success($ticket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Summary of fetchTickets
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response
|
||||
*/
|
||||
private function fetchTickets(Request $request)
|
||||
{
|
||||
$ticketModel = Ticket::query()
|
||||
->when($request->has('status'), function ($query) use ($request) {
|
||||
$query->where('status', $request->input('status'));
|
||||
})
|
||||
->when($request->has('reply_status'), function ($query) use ($request) {
|
||||
$query->whereIn('reply_status', $request->input('reply_status'));
|
||||
})
|
||||
->when($request->has('email'), function ($query) use ($request) {
|
||||
$query->whereHas('user', function ($q) use ($request) {
|
||||
$q->where('email', $request->input('email'));
|
||||
});
|
||||
});
|
||||
|
||||
$this->applyFiltersAndSorts($request, $ticketModel);
|
||||
|
||||
return response()->json($ticketModel
|
||||
->latest('updated_at')
|
||||
->paginate(
|
||||
perPage: $request->integer('pageSize', 10),
|
||||
page: $request->integer('current', 1)
|
||||
));
|
||||
}
|
||||
|
||||
public function reply(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric',
|
||||
'message' => 'required|string'
|
||||
], [
|
||||
'id.required' => '工单ID不能为空',
|
||||
'message.required' => '消息不能为空'
|
||||
]);
|
||||
$ticketService = new TicketService();
|
||||
$ticketService->replyByAdmin(
|
||||
$request->input('id'),
|
||||
$request->input('message'),
|
||||
$request->user()->id
|
||||
);
|
||||
return $this->success(true);
|
||||
}
|
||||
|
||||
public function close(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id' => 'required|numeric'
|
||||
], [
|
||||
'id.required' => '工单ID不能为空'
|
||||
]);
|
||||
try {
|
||||
$ticket = Ticket::findOrFail($request->input('id'));
|
||||
$ticket->status = Ticket::STATUS_CLOSED;
|
||||
$ticket->save();
|
||||
return $this->success(true);
|
||||
} catch (ModelNotFoundException $e) {
|
||||
return $this->fail([400202, '工单不存在']);
|
||||
} catch (\Exception $e) {
|
||||
return $this->fail([500101, '关闭失败']);
|
||||
}
|
||||
}
|
||||
}
|
||||
+188
-78
@@ -1,10 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\V1\Admin;
|
||||
namespace App\Http\Controllers\V2\Admin;
|
||||
|
||||
use App\Exceptions\ApiException;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Admin\UserFetch;
|
||||
use App\Http\Requests\Admin\UserGenerate;
|
||||
use App\Http\Requests\Admin\UserSendMail;
|
||||
use App\Http\Requests\Admin\UserUpdate;
|
||||
@@ -13,6 +11,7 @@ use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Services\AuthService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
@@ -21,71 +20,173 @@ class UserController extends Controller
|
||||
public function resetSecret(Request $request)
|
||||
{
|
||||
$user = User::find($request->input('id'));
|
||||
if (!$user) return $this->fail([400202,'用户不存在']);
|
||||
if (!$user)
|
||||
return $this->fail([400202, '用户不存在']);
|
||||
$user->token = Helper::guid();
|
||||
$user->uuid = Helper::guid(true);
|
||||
return $this->success($user->save());
|
||||
}
|
||||
|
||||
private function filter(Request $request, $builder)
|
||||
/**
|
||||
* Apply filters and sorts to the query builder
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Builder $builder
|
||||
* @return void
|
||||
*/
|
||||
private function applyFiltersAndSorts(Request $request, Builder $builder): void
|
||||
{
|
||||
$filters = $request->input('filter');
|
||||
if ($filters) {
|
||||
foreach ($filters as $k => $filter) {
|
||||
if ($filter['condition'] === '模糊') {
|
||||
$filter['condition'] = 'like';
|
||||
$filter['value'] = "%{$filter['value']}%";
|
||||
}
|
||||
if ($filter['key'] === 'd' || $filter['key'] === 'transfer_enable') {
|
||||
$filter['value'] = $filter['value'] * 1073741824;
|
||||
}
|
||||
if ($filter['key'] === 'invite_by_email') {
|
||||
$user = User::where('email', $filter['condition'], $filter['value'])->first();
|
||||
$inviteUserId = isset($user->id) ? $user->id : 0;
|
||||
$builder->where('invite_user_id', $inviteUserId);
|
||||
unset($filters[$k]);
|
||||
continue;
|
||||
}
|
||||
$builder->where($filter['key'], $filter['condition'], $filter['value']);
|
||||
}
|
||||
}
|
||||
$this->applyFilters($request, $builder);
|
||||
$this->applySorting($request, $builder);
|
||||
}
|
||||
|
||||
public function fetch(UserFetch $request)
|
||||
/**
|
||||
* Apply filters to the query builder
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Builder $builder
|
||||
* @return void
|
||||
*/
|
||||
private function applyFilters(Request $request, Builder $builder): void
|
||||
{
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
|
||||
$sort = $request->input('sort') ? $request->input('sort') : 'created_at';
|
||||
$userModel = User::select(
|
||||
DB::raw('*'),
|
||||
DB::raw('(u+d) as total_used')
|
||||
)
|
||||
->orderBy($sort, $sortType);
|
||||
$this->filter($request, $userModel);
|
||||
$total = $userModel->count();
|
||||
$res = $userModel->forPage($current, $pageSize)
|
||||
->get();
|
||||
$plan = Plan::get();
|
||||
for ($i = 0; $i < count($res); $i++) {
|
||||
for ($k = 0; $k < count($plan); $k++) {
|
||||
if ($plan[$k]['id'] == $res[$i]['plan_id']) {
|
||||
$res[$i]['plan_name'] = $plan[$k]['name'];
|
||||
}
|
||||
}
|
||||
$res[$i]['subscribe_url'] = Helper::getSubscribeUrl( $res[$i]['token']);
|
||||
if (!$request->has('filter')) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($request->input('filter'))->each(function ($filter) use ($builder) {
|
||||
$field = $filter['id'];
|
||||
$value = $filter['value'];
|
||||
|
||||
$builder->where(function ($query) use ($field, $value) {
|
||||
$this->buildFilterQuery($query, $field, $value);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the filter query based on field and value
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
private function buildFilterQuery(Builder $query, string $field, mixed $value): void
|
||||
{
|
||||
// Handle array values for 'in' operations
|
||||
if (is_array($value)) {
|
||||
$query->whereIn($field === 'group_ids' ? 'group_id' : $field, $value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle operator-based filtering
|
||||
if (!is_string($value) || !str_contains($value, ':')) {
|
||||
$query->where($field, 'like', "%{$value}%");
|
||||
return;
|
||||
}
|
||||
|
||||
[$operator, $filterValue] = explode(':', $value, 2);
|
||||
|
||||
// Convert numeric strings to appropriate type
|
||||
if (is_numeric($filterValue)) {
|
||||
$filterValue = strpos($filterValue, '.') !== false
|
||||
? (float) $filterValue
|
||||
: (int) $filterValue;
|
||||
}
|
||||
|
||||
// Handle computed fields
|
||||
$queryField = match ($field) {
|
||||
'total_used' => DB::raw('(u + d)'),
|
||||
default => $field
|
||||
};
|
||||
|
||||
// Apply operator
|
||||
$query->where($queryField, match (strtolower($operator)) {
|
||||
'eq' => '=',
|
||||
'gt' => '>',
|
||||
'gte' => '>=',
|
||||
'lt' => '<',
|
||||
'lte' => '<=',
|
||||
'like' => 'like',
|
||||
'notlike' => 'not like',
|
||||
'null' => static fn($q) => $q->whereNull($queryField),
|
||||
'notnull' => static fn($q) => $q->whereNotNull($queryField),
|
||||
default => 'like'
|
||||
}, match (strtolower($operator)) {
|
||||
'like', 'notlike' => "%{$filterValue}%",
|
||||
'null', 'notnull' => null,
|
||||
default => $filterValue
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply sorting to the query builder
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Builder $builder
|
||||
* @return void
|
||||
*/
|
||||
private function applySorting(Request $request, Builder $builder): void
|
||||
{
|
||||
if (!$request->has('sort')) {
|
||||
return;
|
||||
}
|
||||
|
||||
collect($request->input('sort'))->each(function ($sort) use ($builder) {
|
||||
$field = $sort['id'];
|
||||
$direction = $sort['desc'] ? 'DESC' : 'ASC';
|
||||
$builder->orderBy($field, $direction);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch paginated user list with filters and sorting
|
||||
*
|
||||
* @param Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function fetch(Request $request)
|
||||
{
|
||||
$current = $request->input('current', 1);
|
||||
$pageSize = $request->input('pageSize', 10);
|
||||
|
||||
$userModel = User::with(['plan:id,name', 'invite_user:id,email', 'group:id,name'])
|
||||
->select(DB::raw('*, (u+d) as total_used'));
|
||||
|
||||
$this->applyFiltersAndSorts($request, $userModel);
|
||||
|
||||
$users = $userModel->orderBy('id', 'desc')
|
||||
->paginate($pageSize, ['*'], 'page', $current);
|
||||
|
||||
$users->getCollection()->transform(function ($user) {
|
||||
return $this->transformUserData($user);
|
||||
});
|
||||
|
||||
return response([
|
||||
'data' => $res,
|
||||
'total' => $total
|
||||
'data' => $users->items(),
|
||||
'total' => $users->total()
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform user data for response
|
||||
*
|
||||
* @param User $user
|
||||
* @return User
|
||||
*/
|
||||
private function transformUserData(User $user): User
|
||||
{
|
||||
$user->subscribe_url = Helper::getSubscribeUrl($user->token);
|
||||
$user->balance = $user->balance / 100;
|
||||
$user->commission_balance = $user->commission_balance / 100;
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function getUserInfoById(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'id'=> 'required|numeric'
|
||||
],[
|
||||
'id' => 'required|numeric'
|
||||
], [
|
||||
'id.required' => '用户ID不能为空'
|
||||
]);
|
||||
$user = User::find($request->input('id'))->load('invite_user');
|
||||
@@ -117,6 +218,7 @@ class UserController extends Controller
|
||||
if (!$plan) {
|
||||
return $this->fail([400202, '订阅计划不存在']);
|
||||
}
|
||||
// return json_encode($plan);
|
||||
$params['group_id'] = $plan->group_id;
|
||||
}
|
||||
// 处理邀请用户
|
||||
@@ -126,16 +228,22 @@ class UserController extends Controller
|
||||
$params['invite_user_id'] = null;
|
||||
}
|
||||
|
||||
if (isset($params['banned']) && (int)$params['banned'] === 1) {
|
||||
if (isset($params['banned']) && (int) $params['banned'] === 1) {
|
||||
$authService = new AuthService($user);
|
||||
$authService->removeAllSession();
|
||||
$authService->removeSession();
|
||||
}
|
||||
if (isset($params['balance'])) {
|
||||
$params['balance'] = $params['balance'] * 100;
|
||||
}
|
||||
if (isset($params['commission_balance'])) {
|
||||
$params['commission_balance'] = $params['commission_balance'] * 100;
|
||||
}
|
||||
|
||||
try {
|
||||
$user->update($params);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'保存失败']);
|
||||
return $this->fail([500, '保存失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
@@ -155,14 +263,14 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
$data = "邮箱,余额,推广佣金,总流量,剩余流量,套餐到期时间,订阅计划,订阅地址\r\n";
|
||||
foreach($res as $user) {
|
||||
foreach ($res as $user) {
|
||||
$expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
|
||||
$balance = $user['balance'] / 100;
|
||||
$commissionBalance = $user['commission_balance'] / 100;
|
||||
$transferEnable = $user['transfer_enable'] ? $user['transfer_enable'] / 1073741824 : 0;
|
||||
$notUseFlow = (($user['transfer_enable'] - ($user['u'] + $user['d'])) / 1073741824) ?? 0;
|
||||
$planName = $user['plan_name'] ?? '无订阅';
|
||||
$subscribeUrl = Helper::getSubscribeUrl($user['token']);
|
||||
$subscribeUrl = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $user['token']);
|
||||
$data .= "{$user['email']},{$balance},{$commissionBalance},{$transferEnable},{$notUseFlow},{$expireDate},{$planName},{$subscribeUrl}\r\n";
|
||||
}
|
||||
echo "\xEF\xBB\xBF" . $data;
|
||||
@@ -174,7 +282,7 @@ class UserController extends Controller
|
||||
if ($request->input('plan_id')) {
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
if (!$plan) {
|
||||
return $this->fail([400202,'订阅计划不存在']);
|
||||
return $this->fail([400202, '订阅计划不存在']);
|
||||
}
|
||||
}
|
||||
$user = [
|
||||
@@ -187,11 +295,11 @@ class UserController extends Controller
|
||||
'token' => Helper::guid()
|
||||
];
|
||||
if (User::where('email', $user['email'])->first()) {
|
||||
return $this->fail([400201,'邮箱已存在于系统中']);
|
||||
return $this->fail([400201, '邮箱已存在于系统中']);
|
||||
}
|
||||
$user['password'] = password_hash($request->input('password') ?? $user['email'], PASSWORD_DEFAULT);
|
||||
if (!User::create($user)) {
|
||||
return $this->fail([500,'生成失败']);
|
||||
return $this->fail([500, '生成失败']);
|
||||
}
|
||||
return $this->success(true);
|
||||
}
|
||||
@@ -205,11 +313,11 @@ class UserController extends Controller
|
||||
if ($request->input('plan_id')) {
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
if (!$plan) {
|
||||
return $this->fail([400202,'订阅计划不存在']);
|
||||
return $this->fail([400202, '订阅计划不存在']);
|
||||
}
|
||||
}
|
||||
$users = [];
|
||||
for ($i = 0;$i < $request->input('generate_count');$i++) {
|
||||
for ($i = 0; $i < $request->input('generate_count'); $i++) {
|
||||
$user = [
|
||||
'email' => Helper::randomChar(6) . '@' . $request->input('email_suffix'),
|
||||
'plan_id' => isset($plan->id) ? $plan->id : NULL,
|
||||
@@ -224,23 +332,23 @@ class UserController extends Controller
|
||||
$user['password'] = password_hash($request->input('password') ?? $user['email'], PASSWORD_DEFAULT);
|
||||
array_push($users, $user);
|
||||
}
|
||||
try{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
if (!User::insert($users)) {
|
||||
throw new \Exception();
|
||||
}
|
||||
DB::commit();
|
||||
}catch(\Exception $e){
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'生成失败']);
|
||||
return $this->fail([500, '生成失败']);
|
||||
}
|
||||
$data = "账号,密码,过期时间,UUID,创建时间,订阅地址\r\n";
|
||||
foreach($users as $user) {
|
||||
foreach ($users as $user) {
|
||||
$expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
|
||||
$createDate = date('Y-m-d H:i:s', $user['created_at']);
|
||||
$password = $request->input('password') ?? $user['email'];
|
||||
$subscribeUrl = Helper::getSubscribeUrl($user['token']);
|
||||
$subscribeUrl = Helper::getSubscribeUrl('/api/v1/client/subscribe?token=' . $user['token']);
|
||||
$data .= "{$user['email']},{$password},{$expireDate},{$user['uuid']},{$createDate},{$subscribeUrl}\r\n";
|
||||
}
|
||||
echo $data;
|
||||
@@ -254,17 +362,19 @@ class UserController extends Controller
|
||||
$this->filter($request, $builder);
|
||||
$users = $builder->get();
|
||||
foreach ($users as $user) {
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => $request->input('subject'),
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
'name' => admin_setting('app_name', 'XBoard'),
|
||||
'url' => admin_setting('app_url'),
|
||||
'content' => $request->input('content')
|
||||
]
|
||||
],
|
||||
'send_email_mass');
|
||||
SendEmailJob::dispatch(
|
||||
[
|
||||
'email' => $user->email,
|
||||
'subject' => $request->input('subject'),
|
||||
'template_name' => 'notify',
|
||||
'template_value' => [
|
||||
'name' => admin_setting('app_name', 'XBoard'),
|
||||
'url' => admin_setting('app_url'),
|
||||
'content' => $request->input('content')
|
||||
]
|
||||
],
|
||||
'send_email_mass'
|
||||
);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
@@ -282,7 +392,7 @@ class UserController extends Controller
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error($e);
|
||||
return $this->fail([500,'处理失败']);
|
||||
return $this->fail([500, '处理失败']);
|
||||
}
|
||||
|
||||
return $this->success(true);
|
||||
Reference in New Issue
Block a user