From 4c6c7182e206359d78c142432631fbe3766322c6 Mon Sep 17 00:00:00 2001 From: xboard Date: Wed, 10 Apr 2024 00:51:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81=E3=80=81=E9=82=AE?= =?UTF-8?q?=E4=BB=B6=E9=98=9F=E5=88=97=E5=A2=9E=E5=8A=A0=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E9=87=8D=E8=AF=95=E3=80=81=E5=8E=BB=E9=99=A4=E5=A4=9A=E4=B8=AA?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E6=96=B9=E5=BC=8F=E3=80=81=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Console/Commands/CheckServer.php | 3 +- app/Console/Commands/CheckTicket.php | 2 +- app/Console/Commands/SendRemindMail.php | 2 - app/Console/Commands/XboardInstall.php | 103 ++++++------- app/Exceptions/Handler.php | 4 +- app/Helpers/Functions.php | 9 +- .../Controllers/V1/Admin/ConfigController.php | 6 +- .../Controllers/V1/Admin/OrderController.php | 8 +- .../V1/Admin/Server/GroupController.php | 3 +- .../V1/Admin/Server/ManageController.php | 3 +- .../V1/Admin/Server/TrojanController.php | 8 - .../Controllers/V1/Admin/TicketController.php | 2 +- .../Controllers/V1/Client/AppController.php | 3 +- .../V1/Client/ClientController.php | 3 +- .../V1/Guest/PaymentController.php | 12 +- .../V1/Guest/TelegramController.php | 15 +- .../V1/Passport/AuthController.php | 1 - .../V1/Server/DeepbworkController.php | 13 +- .../Server/ShadowsocksTidalabController.php | 14 +- .../V1/Server/TrojanTidalabController.php | 27 +--- .../V1/Server/UniProxyController.php | 129 ++++++++-------- .../Controllers/V1/Staff/TicketController.php | 2 +- .../Controllers/V1/User/ServerController.php | 5 +- .../Controllers/V1/User/TicketController.php | 2 +- app/Http/Kernel.php | 2 - app/Http/Middleware/Authenticate.php | 21 --- app/Http/Middleware/Server.php | 27 ++-- app/Http/Routes/V1/ServerRoute.php | 31 +++- app/Jobs/SendEmailJob.php | 41 +---- app/Jobs/TrafficFetchJob.php | 3 - app/Models/Order.php | 26 ++++ app/Models/Ticket.php | 6 + app/Payments/AlipayF2F.php | 1 + app/Payments/BTCPay.php | 30 ++-- app/Payments/CoinPayments.php | 10 +- app/Payments/Coinbase.php | 30 ++-- app/Payments/EPay.php | 4 +- app/Payments/StripeAlipay.php | 118 --------------- app/Payments/StripeCheckout.php | 140 ------------------ app/Payments/StripeCredit.php | 126 ---------------- app/Payments/StripeWepay.php | 118 --------------- app/Payments/WechatPayNative.php | 85 ----------- app/Providers/SettingServiceProvider.php | 8 +- app/Services/MailService.php | 83 +++++++++-- app/Services/OrderService.php | 18 +-- app/Services/ServerService.php | 81 +++++----- app/Services/TicketService.php | 10 +- app/Services/UserService.php | 4 +- app/Support/Setting.php | 21 +-- composer.json | 3 +- 50 files changed, 421 insertions(+), 1005 deletions(-) delete mode 100755 app/Http/Middleware/Authenticate.php delete mode 100644 app/Payments/StripeAlipay.php delete mode 100644 app/Payments/StripeCheckout.php delete mode 100644 app/Payments/StripeCredit.php delete mode 100644 app/Payments/StripeWepay.php delete mode 100644 app/Payments/WechatPayNative.php diff --git a/app/Console/Commands/CheckServer.php b/app/Console/Commands/CheckServer.php index 3e94ea7..2d3dae9 100644 --- a/app/Console/Commands/CheckServer.php +++ b/app/Console/Commands/CheckServer.php @@ -46,8 +46,7 @@ class CheckServer extends Command private function checkOffline() { - $serverService = new ServerService(); - $servers = $serverService->getAllServers(); + $servers = ServerService::getAllServers(); foreach ($servers as $server) { if ($server['parent_id']) continue; if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) { diff --git a/app/Console/Commands/CheckTicket.php b/app/Console/Commands/CheckTicket.php index a4ea978..123e633 100644 --- a/app/Console/Commands/CheckTicket.php +++ b/app/Console/Commands/CheckTicket.php @@ -45,7 +45,7 @@ class CheckTicket extends Command ->get(); foreach ($tickets as $ticket) { if ($ticket->user_id === $ticket->last_reply_user_id) continue; - $ticket->status = 1; + $ticket->status = Ticket::STATUS_CLOSED; $ticket->save(); } } diff --git a/app/Console/Commands/SendRemindMail.php b/app/Console/Commands/SendRemindMail.php index 8a069fb..672c37a 100644 --- a/app/Console/Commands/SendRemindMail.php +++ b/app/Console/Commands/SendRemindMail.php @@ -5,8 +5,6 @@ namespace App\Console\Commands; use App\Services\MailService; use Illuminate\Console\Command; use App\Models\User; -use App\Models\MailLog; -use App\Jobs\SendEmailJob; class SendRemindMail extends Command { diff --git a/app/Console/Commands/XboardInstall.php b/app/Console/Commands/XboardInstall.php index b4c0c3b..458c773 100644 --- a/app/Console/Commands/XboardInstall.php +++ b/app/Console/Commands/XboardInstall.php @@ -44,7 +44,7 @@ class XboardInstall extends Command */ public function handle() { - try { + try { // \Artisan::call('config:clear'); $isDocker = env('docker', false); $this->info("__ __ ____ _ "); @@ -52,7 +52,8 @@ class XboardInstall extends Command $this->info(" \ \/ / | __ \ / _ \ / _` | '__/ _` | "); $this->info(" / /\ \ | |_) | (_) | (_| | | | (_| | "); $this->info("/_/ \_\|____/ \___/ \__,_|_| \__,_| "); - if ((\File::exists(base_path() . '/.env') && $this->getEnvValue('INSTALLED')) + if ( + (\File::exists(base_path() . '/.env') && $this->getEnvValue('INSTALLED')) || (env('INSTALLED', false) && $isDocker) ) { $securePath = admin_setting('secure_path', admin_setting('frontend_admin_path', hash('crc32b', config('app.key')))); @@ -60,23 +61,15 @@ class XboardInstall extends Command $this->warn("如需重新安装请清空目录下 .env 文件的内容(Docker安装方式不可以删除此文件)"); $this->warn("快捷清空.env命令:"); note('rm .env && touch .env'); - return ; + return; } - if (is_dir(base_path() . '/.env')){ + if (is_dir(base_path() . '/.env')) { $this->error('😔:安装失败,Docker环境下安装请保留空的 .env 文件'); - return ; + return; } // 选择是否使用Sqlite - if(confirm(label: '是否启用Sqlite(无需额外安装)代替Mysql',default: false, yes: '启用', no: '不启用')) { + if (confirm(label: '是否启用Sqlite(无需额外安装)代替Mysql', default: false, yes: '启用', no: '不启用')) { $sqliteFile = '.docker/.data/database.sqlite'; - if (!file_exists(base_path($sqliteFile))) { - // 创建空文件 - if (!touch(base_path($sqliteFile))) { - echo "sqlite创建成功: $sqliteFile"; - } else { - echo "sqlite创建失败"; - } - } $envConfig = [ 'DB_CONNECTION' => 'sqlite', 'DB_DATABASE' => $sqliteFile, @@ -84,16 +77,16 @@ class XboardInstall extends Command 'DB_USERNAME' => '', 'DB_PASSWORD' => '', ]; - }else{ + } else { $isMysqlValid = false; - while(!$isMysqlValid){ + while (!$isMysqlValid) { $envConfig = [ 'DB_CONNECTION' => 'mysql', 'DB_HOST' => text(label: "请输入数据库地址", default: '127.0.0.1', required: true), 'DB_PORT' => text(label: '请输入数据库端口', default: '3306', required: true), - 'DB_DATABASE' => text(label:'请输入数据库名', default:'xboard', required: true), - 'DB_USERNAME' => text(label:'请输入数据库用户名', required: true), - 'DB_PASSWORD' => text(label:'请输入数据库密码', required: false), + 'DB_DATABASE' => text(label: '请输入数据库名', default: 'xboard', required: true), + 'DB_USERNAME' => text(label: '请输入数据库用户名', required: true), + 'DB_PASSWORD' => text(label: '请输入数据库密码', required: false), ]; try { \Config::set("database.connections.mysql.host", $envConfig['DB_HOST']); @@ -114,15 +107,15 @@ class XboardInstall extends Command $envConfig['APP_KEY'] = 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')); $envConfig['INSTALLED'] = true; $isReidsValid = false; - while(!$isReidsValid){ + while (!$isReidsValid) { // 判断是否为Docker环境 - if ($isDocker == 'true' && (confirm(label: '是否启用Docker内置的Redis', default: true, yes:'启用', no:'不启用'))){ - $envConfig['REDIS_HOST'] = '/run/redis-socket/redis.sock'; - $envConfig['REDIS_PORT'] = 0; + if ($isDocker == 'true' && (confirm(label: '是否启用Docker内置的Redis', default: true, yes: '启用', no: '不启用'))) { + $envConfig['REDIS_HOST'] = '/run/redis-socket/redis.sock'; + $envConfig['REDIS_PORT'] = 0; $envConfig['REDIS_PASSWORD'] = null; - }else{ - $envConfig['REDIS_HOST'] = text(label: '请输入Redis地址', default: '127.0.0.1',required: true); - $envConfig['REDIS_PORT'] = text(label: '请输入Redis端口', default: '6379', required: true); + } else { + $envConfig['REDIS_HOST'] = text(label: '请输入Redis地址', default: '127.0.0.1', required: true); + $envConfig['REDIS_PORT'] = text(label: '请输入Redis端口', default: '6379', required: true); $envConfig['REDIS_PASSWORD'] = text(label: '请输入redis密码(默认: null)', default: ''); } $redisConfig = [ @@ -134,11 +127,11 @@ class XboardInstall extends Command 'database' => 0, ], ]; - try{ + try { $redis = new \Illuminate\Redis\RedisManager(app(), 'phpredis', $redisConfig); $redis->ping(); $isReidsValid = true; - }catch(\Exception $e){ + } catch (\Exception $e) { // 连接失败,输出错误消息 $this->error("redis连接失败:" . $e->getMessage()); $this->info("请重新输入REDIS配置"); @@ -147,12 +140,16 @@ class XboardInstall extends Command if (!copy(base_path() . '/.env.example', base_path() . '/.env')) { abort(500, '复制环境文件失败,请检查目录权限'); - }; - $email = text(label: '请输入管理员账号',required: true, - validate: fn (string $email): ?string => match (true) { - ! filter_var($email, FILTER_VALIDATE_EMAIL) => '请输入有效的邮箱地址.', + } + ; + $email = text( + label: '请输入管理员账号', + required: true, + validate: fn(string $email): ?string => match (true) { + !filter_var($email, FILTER_VALIDATE_EMAIL) => '请输入有效的邮箱地址.', default => null, - }); + } + ); $password = Helper::guid(false); $this->saveToEnv($envConfig); @@ -197,32 +194,30 @@ class XboardInstall extends Command private function saveToEnv($data = []) { - function set_env_var($key, $value) - { - if (! is_bool(strpos($value, ' '))) { - $value = '"' . $value . '"'; - } - $key = strtoupper($key); + foreach ($data as $key => $value) { + function ($key, $value) { + if (!is_bool(strpos($value, ' '))) { + $value = '"' . $value . '"'; + } + $key = strtoupper($key); - $envPath = app()->environmentFilePath(); - $contents = file_get_contents($envPath); + $envPath = app()->environmentFilePath(); + $contents = file_get_contents($envPath); - preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches); + preg_match("/^{$key}=[^\r\n]*/m", $contents, $matches); - $oldValue = count($matches) ? $matches[0] : ''; + $oldValue = count($matches) ? $matches[0] : ''; - if ($oldValue) { - $contents = str_replace("{$oldValue}", "{$key}={$value}", $contents); - } else { - $contents = $contents . "\n{$key}={$value}\n"; - } + if ($oldValue) { + $contents = str_replace("{$oldValue}", "{$key}={$value}", $contents); + } else { + $contents = $contents . "\n{$key}={$value}\n"; + } - $file = fopen($envPath, 'w'); - fwrite($file, $contents); - return fclose($file); - } - foreach($data as $key => $value) { - set_env_var($key, $value); + $file = fopen($envPath, 'w'); + fwrite($file, $contents); + return fclose($file); + }; } return true; } diff --git a/app/Exceptions/Handler.php b/app/Exceptions/Handler.php index ee26a4e..cfd7ba8 100755 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/Handler.php @@ -5,10 +5,8 @@ namespace App\Exceptions; use App\Helpers\ApiResponse; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; use Illuminate\Support\Arr; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; +use Illuminate\View\ViewException; use Throwable; -use Facade\Ignition\Exceptions\ViewException; -use Illuminate\Support\Facades\Log; class Handler extends ExceptionHandler { diff --git a/app/Helpers/Functions.php b/app/Helpers/Functions.php index 7447162..e37a580 100644 --- a/app/Helpers/Functions.php +++ b/app/Helpers/Functions.php @@ -1,4 +1,5 @@ toArray(); } if (is_array($key)) { - app('setting')->save($key); - return; + App::make(Setting::class)->save($key); + return ''; } $default = config('v2board.'. $key) ?? $default; - return app('setting')->get($key) ?? $default ; + return App::make(Setting::class)->get($key) ?? $default ; } } diff --git a/app/Http/Controllers/V1/Admin/ConfigController.php b/app/Http/Controllers/V1/Admin/ConfigController.php index b84d5fb..50cdbdf 100755 --- a/app/Http/Controllers/V1/Admin/ConfigController.php +++ b/app/Http/Controllers/V1/Admin/ConfigController.php @@ -4,8 +4,8 @@ namespace App\Http\Controllers\V1\Admin; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\ConfigSave; -use App\Jobs\SendEmailJob; use App\Models\Setting; +use App\Services\MailService; use App\Services\TelegramService; use App\Utils\Dict; use Illuminate\Http\Request; @@ -33,7 +33,7 @@ class ConfigController extends Controller public function testSendMail(Request $request) { - $obj = new SendEmailJob([ + $mailLog = MailService::sendEmail([ 'email' => $request->user['email'], 'subject' => 'This is xboard test email', 'template_name' => 'notify', @@ -45,7 +45,7 @@ class ConfigController extends Controller ]); return response([ 'data' => true, - 'log' => $obj->handle() + 'log' => $mailLog ]); } diff --git a/app/Http/Controllers/V1/Admin/OrderController.php b/app/Http/Controllers/V1/Admin/OrderController.php index 07270b8..b49c751 100644 --- a/app/Http/Controllers/V1/Admin/OrderController.php +++ b/app/Http/Controllers/V1/Admin/OrderController.php @@ -160,13 +160,13 @@ class OrderController extends Controller $order->total_amount = $request->input('total_amount'); if ($order->period === 'reset_price') { - $order->type = 4; + $order->type = Order::TYPE_RESET_TRAFFIC; } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id) { - $order->type = 3; + $order->type = Order::TYPE_UPGRADE; } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { - $order->type = 2; + $order->type = Order::TYPE_RENEWAL; } else { - $order->type = 1; + $order->type = Order::TYPE_NEW_PURCHASE; } $orderService->setInvite($user); diff --git a/app/Http/Controllers/V1/Admin/Server/GroupController.php b/app/Http/Controllers/V1/Admin/Server/GroupController.php index 40b544a..8c4d7cc 100644 --- a/app/Http/Controllers/V1/Admin/Server/GroupController.php +++ b/app/Http/Controllers/V1/Admin/Server/GroupController.php @@ -19,8 +19,7 @@ class GroupController extends Controller return $this->success([ServerGroup::find($request->input('group_id'))]); } $serverGroups = ServerGroup::get(); - $serverService = new ServerService(); - $servers = $serverService->getAllServers(); + $servers = ServerService::getAllServers(); foreach ($serverGroups as $k => $v) { $serverGroups[$k]['user_count'] = User::where('group_id', $v['id'])->count(); $serverGroups[$k]['server_count'] = 0; diff --git a/app/Http/Controllers/V1/Admin/Server/ManageController.php b/app/Http/Controllers/V1/Admin/Server/ManageController.php index c34d6f2..c3292db 100644 --- a/app/Http/Controllers/V1/Admin/Server/ManageController.php +++ b/app/Http/Controllers/V1/Admin/Server/ManageController.php @@ -12,8 +12,7 @@ class ManageController extends Controller { public function getNodes(Request $request) { - $serverService = new ServerService(); - return $this->success($serverService->getAllServers()); + return $this->success(ServerService::getAllServers()); } public function sort(Request $request) diff --git a/app/Http/Controllers/V1/Admin/Server/TrojanController.php b/app/Http/Controllers/V1/Admin/Server/TrojanController.php index a8797ec..0f1de3a 100644 --- a/app/Http/Controllers/V1/Admin/Server/TrojanController.php +++ b/app/Http/Controllers/V1/Admin/Server/TrojanController.php @@ -2,12 +2,10 @@ namespace App\Http\Controllers\V1\Admin\Server; -use App\Exceptions\ApiException; use App\Http\Controllers\Controller; use App\Http\Requests\Admin\ServerTrojanSave; use App\Http\Requests\Admin\ServerTrojanUpdate; use App\Models\ServerTrojan; -use App\Services\ServerService; use Illuminate\Http\Request; class TrojanController extends Controller @@ -75,10 +73,4 @@ class TrojanController extends Controller ServerTrojan::create($server->toArray()); return $this->success(true); } - public function viewConfig(Request $request) - { - $serverService = new ServerService(); - $config = $serverService->getTrojanConfig($request->input('node_id'), 23333); - return $this->success($config); - } } diff --git a/app/Http/Controllers/V1/Admin/TicketController.php b/app/Http/Controllers/V1/Admin/TicketController.php index 8dd7da9..7eb34dd 100644 --- a/app/Http/Controllers/V1/Admin/TicketController.php +++ b/app/Http/Controllers/V1/Admin/TicketController.php @@ -80,7 +80,7 @@ class TicketController extends Controller ]); try { $ticket = Ticket::findOrFail($request->input('id')); - $ticket->status = 1; + $ticket->status = Ticket::STATUS_CLOSED; $ticket->save(); return $this->success(true); } catch (ModelNotFoundException $e) { diff --git a/app/Http/Controllers/V1/Client/AppController.php b/app/Http/Controllers/V1/Client/AppController.php index 72ae9c9..0f41261 100644 --- a/app/Http/Controllers/V1/Client/AppController.php +++ b/app/Http/Controllers/V1/Client/AppController.php @@ -17,8 +17,7 @@ class AppController extends Controller $user = $request->user; $userService = new UserService(); if ($userService->isAvailable($user)) { - $serverService = new ServerService(); - $servers = $serverService->getAvailableServers($user); + $servers = ServerService::getAvailableServers($user); } $defaultConfig = base_path() . '/resources/rules/app.clash.yaml'; $customConfig = base_path() . '/resources/rules/custom.app.clash.yaml'; diff --git a/app/Http/Controllers/V1/Client/ClientController.php b/app/Http/Controllers/V1/Client/ClientController.php index e07f936..57132c2 100644 --- a/app/Http/Controllers/V1/Client/ClientController.php +++ b/app/Http/Controllers/V1/Client/ClientController.php @@ -65,8 +65,7 @@ class ClientController extends Controller $region = $geo['region'] ?? null; // 获取服务器列表 - $serverService = new ServerService(); - $servers = $serverService->getAvailableServers($user); + $servers = ServerService::getAvailableServers($user); // 判断不满足,不满足的直接过滤掉 $serversFiltered = collect($servers)->reject(function ($server) use ($typesArr, $filterArr, $region, $supportHy2){ diff --git a/app/Http/Controllers/V1/Guest/PaymentController.php b/app/Http/Controllers/V1/Guest/PaymentController.php index e9816de..5eca10a 100644 --- a/app/Http/Controllers/V1/Guest/PaymentController.php +++ b/app/Http/Controllers/V1/Guest/PaymentController.php @@ -17,14 +17,15 @@ class PaymentController extends Controller try { $paymentService = new PaymentService($method, null, $uuid); $verify = $paymentService->notify($request->input()); - if (!$verify) return $this->fail([422,'verify error']); + if (!$verify) + return $this->fail([422, 'verify error']); if (!$this->handle($verify['trade_no'], $verify['callback_no'])) { - return $this->fail([400,'handle error']); + return $this->fail([400, 'handle error']); } return (isset($verify['custom_result']) ? $verify['custom_result'] : 'success'); } catch (\Exception $e) { \Log::error($e); - return $this->fail([500,'fail']); + return $this->fail([500, 'fail']); } } @@ -32,9 +33,10 @@ class PaymentController extends Controller { $order = Order::where('trade_no', $tradeNo)->first(); if (!$order) { - return $this->fail([400202,'order is not found']); + return $this->fail([400202, 'order is not found']); } - if ($order->status !== 0) return true; + if ($order->status !== Order::STATUS_PENDING) + return true; $orderService = new OrderService($order); if (!$orderService->paid($callbackNo)) { return false; diff --git a/app/Http/Controllers/V1/Guest/TelegramController.php b/app/Http/Controllers/V1/Guest/TelegramController.php index 0bd8017..7edff49 100644 --- a/app/Http/Controllers/V1/Guest/TelegramController.php +++ b/app/Http/Controllers/V1/Guest/TelegramController.php @@ -13,24 +13,23 @@ class TelegramController extends Controller protected $commands = []; protected $telegramService; - public function __construct(Request $request) + public function __construct(TelegramService $telegramService) { - if ($request->input('access_token') !== md5(admin_setting('telegram_bot_token'))) { - throw new ApiException('access_token is error', 401); - } - - $this->telegramService = new TelegramService(); + $this->telegramService = $telegramService; } public function webhook(Request $request) { + 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); $this->formatMessage($data); $this->formatChatJoinRequest($data); $this->handle(); } - public function handle() + private function handle() { if (!$this->msg) return; $msg = $this->msg; @@ -68,7 +67,7 @@ class TelegramController extends Controller } } - public function getBotName() + private function getBotName() { $response = $this->telegramService->getMe(); return $response->result->username; diff --git a/app/Http/Controllers/V1/Passport/AuthController.php b/app/Http/Controllers/V1/Passport/AuthController.php index b8eb7dd..8979be6 100644 --- a/app/Http/Controllers/V1/Passport/AuthController.php +++ b/app/Http/Controllers/V1/Passport/AuthController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers\V1\Passport; -use App\Exceptions\ApiException; use App\Helpers\ResponseEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Passport\AuthForget; diff --git a/app/Http/Controllers/V1/Server/DeepbworkController.php b/app/Http/Controllers/V1/Server/DeepbworkController.php index f5f40cc..6a6b895 100644 --- a/app/Http/Controllers/V1/Server/DeepbworkController.php +++ b/app/Http/Controllers/V1/Server/DeepbworkController.php @@ -18,16 +18,6 @@ use Illuminate\Support\Facades\Cache; 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 __construct(Request $request) - { - $token = $request->input('token'); - if (empty($token)) { - throw new ApiException('token is null'); - } - if ($token !== admin_setting('server_token')) { - throw new ApiException('token is error'); - } - } // 后端获取用户 public function user(Request $request) @@ -39,8 +29,7 @@ class DeepbworkController extends Controller return $this->fail([400,'节点不存在']); } Cache::put(CacheKey::get('SERVER_VMESS_LAST_CHECK_AT', $server->id), time(), 3600); - $serverService = new ServerService(); - $users = $serverService->getAvailableUsers($server->group_id); + $users = ServerService::getAvailableUsers($server->group_id); $result = []; foreach ($users as $user) { $user->v2ray_user = [ diff --git a/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php b/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php index 5575b4e..d40dfd6 100644 --- a/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php +++ b/app/Http/Controllers/V1/Server/ShadowsocksTidalabController.php @@ -17,17 +17,6 @@ use Illuminate\Support\Facades\Cache; */ class ShadowsocksTidalabController extends Controller { - public function __construct(Request $request) - { - $token = $request->input('token'); - if (empty($token)) { - throw new ApiException('token is null'); - } - if ($token !== admin_setting('server_token')) { - throw new ApiException('token is error'); - } - } - // 后端获取用户 public function user(Request $request) { @@ -38,8 +27,7 @@ class ShadowsocksTidalabController extends Controller return $this->fail([400,'节点不存在']); } Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600); - $serverService = new ServerService(); - $users = $serverService->getAvailableUsers($server->group_id); + $users = ServerService::getAvailableUsers($server->group_id); $result = []; foreach ($users as $user) { array_push($result, [ diff --git a/app/Http/Controllers/V1/Server/TrojanTidalabController.php b/app/Http/Controllers/V1/Server/TrojanTidalabController.php index 6092622..2e47f65 100644 --- a/app/Http/Controllers/V1/Server/TrojanTidalabController.php +++ b/app/Http/Controllers/V1/Server/TrojanTidalabController.php @@ -17,17 +17,7 @@ use Illuminate\Support\Facades\Cache; */ class TrojanTidalabController extends Controller { - CONST TROJAN_CONFIG = '{"run_type":"server","local_addr":"0.0.0.0","local_port":443,"remote_addr":"www.taobao.com","remote_port":80,"password":[],"ssl":{"cert":"server.crt","key":"server.key","sni":"domain.com"},"api":{"enabled":true,"api_addr":"127.0.0.1","api_port":10000}}'; - public function __construct(Request $request) - { - $token = $request->input('token'); - if (empty($token)) { - throw new ApiException('token is null'); - } - if ($token !== admin_setting('server_token')) { - throw new ApiException('token is error'); - } - } + const TROJAN_CONFIG = '{"run_type":"server","local_addr":"0.0.0.0","local_port":443,"remote_addr":"www.taobao.com","remote_port":80,"password":[],"ssl":{"cert":"server.crt","key":"server.key","sni":"domain.com"},"api":{"enabled":true,"api_addr":"127.0.0.1","api_port":10000}}'; // 后端获取用户 public function user(Request $request) @@ -36,11 +26,10 @@ class TrojanTidalabController extends Controller $nodeId = $request->input('node_id'); $server = ServerTrojan::find($nodeId); if (!$server) { - return $this->fail([400,'节点不存在']); + return $this->fail([400, '节点不存在']); } Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600); - $serverService = new ServerService(); - $users = $serverService->getAvailableUsers($server->group_id); + $users = ServerService::getAvailableUsers($server->group_id); $result = []; foreach ($users as $user) { $user->trojan_user = [ @@ -50,8 +39,8 @@ class TrojanTidalabController extends Controller array_push($result, $user); } $eTag = sha1(json_encode($result)); - if (strpos($request->header('If-None-Match'), $eTag) !== false ) { - return response(null,304); + if (strpos($request->header('If-None-Match'), $eTag) !== false) { + return response(null, 304); } return response([ 'msg' => 'ok', @@ -92,7 +81,7 @@ class TrojanTidalabController extends Controller $request->validate([ 'node_id' => 'required', 'local_port' => 'required' - ],[ + ], [ 'node_id.required' => '节点ID不能为空', 'local_port.required' => '本地端口不能为空' ]); @@ -100,10 +89,10 @@ class TrojanTidalabController extends Controller $json = $this->getTrojanConfig($request->input('node_id'), $request->input('local_port')); } catch (\Exception $e) { \Log::error($e); - return $this->fail([500,'配置获取失败']); + return $this->fail([500, '配置获取失败']); } - return(json_encode($json, JSON_UNESCAPED_UNICODE)); + return (json_encode($json, JSON_UNESCAPED_UNICODE)); } private function getTrojanConfig(int $nodeId, int $localPort) diff --git a/app/Http/Controllers/V1/Server/UniProxyController.php b/app/Http/Controllers/V1/Server/UniProxyController.php index 2a52534..7fd49c2 100644 --- a/app/Http/Controllers/V1/Server/UniProxyController.php +++ b/app/Http/Controllers/V1/Server/UniProxyController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers\V1\Server; -use App\Exceptions\ApiException; use App\Http\Controllers\Controller; use App\Services\ServerService; use App\Services\UserService; @@ -10,40 +9,23 @@ use App\Utils\CacheKey; use App\Utils\Helper; use Illuminate\Http\Request; use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Log; class UniProxyController extends Controller { - private $nodeType; - private $nodeInfo; - private $nodeId; - private $serverService; - - public function __construct(ServerService $serverService, Request $request) - { - $this->serverService = $serverService; - $this->nodeId = $request->input('node_id'); - $this->nodeType = $request->input('node_type'); - $this->nodeInfo = $this->serverService->getServer($this->nodeId, $this->nodeType); - if(!$this->nodeInfo) { - throw new ApiException('server is not exist', 500); - }; - } // 后端获取用户 public function user(Request $request) { ini_set('memory_limit', -1); - Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_CHECK_AT', $this->nodeInfo->id), time(), 3600); - $users = $this->serverService->getAvailableUsers($this->nodeInfo->group_id); - $users = $users->toArray(); + 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(); $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); - }; + } return response($response)->header('ETag', "\"{$eTag}\""); } @@ -51,14 +33,18 @@ class UniProxyController extends Controller // 后端提交数据 public function push(Request $request) { - $data = get_request_content(); - $data = json_decode($data, true); + $data = $request->validate([ + "*.0" => 'integer', + "*.1" => 'integer' + ]); + $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($this->nodeType) . '_ONLINE_USER', $this->nodeInfo->id); + $cacheKey = CacheKey::get('MULTI_SERVER_' . strtoupper($nodeType) . '_ONLINE_USER', $nodeId); // 1、获取节点节点在线人数缓存 $onlineUsers = Cache::get($cacheKey) ?? []; @@ -87,91 +73,92 @@ class UniProxyController extends Controller Cache::put($cacheKey, $onlineUsers, 3600); $online_user = $onlineCollection->sum('online_user'); - Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_ONLINE_USER', $this->nodeInfo->id), $online_user, 3600); - Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_PUSH_AT', $this->nodeInfo->id), time(), 3600); + 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($this->nodeInfo->toArray(), $this->nodeType, $data , $ip); - + $userService->trafficFetch($request->input('node_info')->toArray(), $nodeType, $data, $ip); return $this->success(true); } // 后端获取配置 public function config(Request $request) { - switch ($this->nodeType) { + $nodeType = $request->input('node_type'); + $nodeInfo = $request->input('node_info'); + switch ($nodeType) { case 'shadowsocks': $response = [ - 'server_port' => $this->nodeInfo->server_port, - 'cipher' => $this->nodeInfo->cipher, - 'obfs' => $this->nodeInfo->obfs, - 'obfs_settings' => $this->nodeInfo->obfs_settings + 'server_port' => $nodeInfo->server_port, + 'cipher' => $nodeInfo->cipher, + 'obfs' => $nodeInfo->obfs, + 'obfs_settings' => $nodeInfo->obfs_settings ]; - if ($this->nodeInfo->cipher === '2022-blake3-aes-128-gcm') { - $response['server_key'] = Helper::getServerKey($this->nodeInfo->created_at, 16); + if ($nodeInfo->cipher === '2022-blake3-aes-128-gcm') { + $response['server_key'] = Helper::getServerKey($nodeInfo->created_at, 16); } - if ($this->nodeInfo->cipher === '2022-blake3-aes-256-gcm') { - $response['server_key'] = Helper::getServerKey($this->nodeInfo->created_at, 32); + if ($nodeInfo->cipher === '2022-blake3-aes-256-gcm') { + $response['server_key'] = Helper::getServerKey($nodeInfo->created_at, 32); } break; case 'vmess': $response = [ - 'server_port' => $this->nodeInfo->server_port, - 'network' => $this->nodeInfo->network, - 'networkSettings' => $this->nodeInfo->networkSettings, - 'tls' => $this->nodeInfo->tls + 'server_port' => $nodeInfo->server_port, + 'network' => $nodeInfo->network, + 'networkSettings' => $nodeInfo->networkSettings, + 'tls' => $nodeInfo->tls ]; break; case 'trojan': $response = [ - 'host' => $this->nodeInfo->host, - 'server_port' => $this->nodeInfo->server_port, - 'server_name' => $this->nodeInfo->server_name, - 'network' => $this->nodeInfo->network, - 'networkSettings' => $this->nodeInfo->networkSettings, + 'host' => $nodeInfo->host, + 'server_port' => $nodeInfo->server_port, + 'server_name' => $nodeInfo->server_name, + 'network' => $nodeInfo->network, + 'networkSettings' => $nodeInfo->networkSettings, ]; break; case 'hysteria': $response = [ - 'version' => $this->nodeInfo->version, - 'host' => $this->nodeInfo->host, - 'server_port' => $this->nodeInfo->server_port, - 'server_name' => $this->nodeInfo->server_name, - 'up_mbps' => $this->nodeInfo->up_mbps, - 'down_mbps' => $this->nodeInfo->down_mbps, - 'obfs' => $this->nodeInfo->is_obfs ? Helper::getServerKey($this->nodeInfo->created_at, 16) : null + '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' => $this->nodeInfo->server_port, - 'network' => $this->nodeInfo->network, - 'network_settings' => $this->nodeInfo->network_settings, - 'networkSettings' => $this->nodeInfo->network_settings, - 'tls' => $this->nodeInfo->tls, - 'flow' => $this->nodeInfo->flow, - 'tls_settings' => $this->nodeInfo->tls_settings + '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) + 'push_interval' => (int) admin_setting('server_push_interval', 60), + 'pull_interval' => (int) admin_setting('server_pull_interval', 60) ]; - if ($this->nodeInfo['route_id']) { - $response['routes'] = $this->serverService->getRoutes($this->nodeInfo['route_id']); + 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 (strpos($request->header('If-None-Match'), $eTag) !== false) { + return response(null, 304); } return response($response)->header('ETag', "\"{$eTag}\""); } - // 后端提交在线数据 - public function alive(Request $request) - { + // 后端提交在线数据 + public function alive(Request $request) + { return $this->success(true); - } + } } diff --git a/app/Http/Controllers/V1/Staff/TicketController.php b/app/Http/Controllers/V1/Staff/TicketController.php index 5944245..c63cf28 100644 --- a/app/Http/Controllers/V1/Staff/TicketController.php +++ b/app/Http/Controllers/V1/Staff/TicketController.php @@ -73,7 +73,7 @@ class TicketController extends Controller if (!$ticket) { return $this->fail([400202,'工单不存在']); } - $ticket->status = 1; + $ticket->status = Ticket::STATUS_CLOSED; if (!$ticket->save()) { return $this->fail([500, '工单关闭失败']); } diff --git a/app/Http/Controllers/V1/User/ServerController.php b/app/Http/Controllers/V1/User/ServerController.php index ffeec83..6e7ec51 100644 --- a/app/Http/Controllers/V1/User/ServerController.php +++ b/app/Http/Controllers/V1/User/ServerController.php @@ -8,8 +8,6 @@ use App\Models\User; use App\Services\ServerService; use App\Services\UserService; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\DB; class ServerController extends Controller { @@ -19,8 +17,7 @@ class ServerController extends Controller $servers = []; $userService = new UserService(); if ($userService->isAvailable($user)) { - $serverService = new ServerService(); - $servers = $serverService->getAvailableServers($user); + $servers = ServerService::getAvailableServers($user); } $eTag = sha1(json_encode(array_column($servers, 'cache_key'))); if (strpos($request->header('If-None-Match'), $eTag) !== false ) { diff --git a/app/Http/Controllers/V1/User/TicketController.php b/app/Http/Controllers/V1/User/TicketController.php index 8acb34f..35e3456 100644 --- a/app/Http/Controllers/V1/User/TicketController.php +++ b/app/Http/Controllers/V1/User/TicketController.php @@ -118,7 +118,7 @@ class TicketController extends Controller if (!$ticket) { return $this->fail([400, __('Ticket does not exist')]); } - $ticket->status = 1; + $ticket->status = Ticket::STATUS_CLOSED; if (!$ticket->save()) { return $this->fail([500, __('Close failed')]); } diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 03fd936..b9b061f 100755 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -56,7 +56,6 @@ class Kernel extends HttpKernel * @var array */ protected $middlewareAliases = [ - 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, @@ -83,7 +82,6 @@ class Kernel extends HttpKernel protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\Authenticate::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php deleted file mode 100755 index 0eec715..0000000 --- a/app/Http/Middleware/Authenticate.php +++ /dev/null @@ -1,21 +0,0 @@ -expectsJson()) { - return route('login'); - } - } -} diff --git a/app/Http/Middleware/Server.php b/app/Http/Middleware/Server.php index 90ce2ec..dbb99cc 100644 --- a/app/Http/Middleware/Server.php +++ b/app/Http/Middleware/Server.php @@ -2,6 +2,8 @@ namespace App\Http\Middleware; +use App\Exceptions\ApiException; +use App\Services\ServerService; use Closure; use Illuminate\Http\Request; @@ -22,27 +24,22 @@ class Server 'hysteria2' => 'hysteria' ]; $request->validate([ - 'token' => ['required','string',function ($attribute, $value, $fail) { - if ($value != admin_setting('server_token')) { - $fail("The $attribute is error."); - } - }], + 'token' => ['required', 'string', 'in:' . admin_setting('server_token')], 'node_id' => 'required', 'node_type' => [ - 'nullable', - 'string', 'regex:/^(?i)(hysteria|hysteria2|vless|trojan|vmess|v2ray|tuic|shadowsocks|shadowsocks-plugin)$/', - function ($attribute, $value, $fail)use($aliasTypes) { - // 将值转换为小写 - request()->merge([$attribute => strtolower($value)]); - // 类别别名 - if (in_array($value, array_keys($aliasTypes))){ - request()->merge([$attribute => $aliasTypes[$value]]); - } + function ($attribute, $value, $fail) use ($aliasTypes, $request) { + $request->merge([$attribute => strtolower(isset ($aliasTypes[$value]) ? $aliasTypes[$value] : $value)]); }, ] + ], [ + 'token.in' => 'Token is error!', + 'node_type.regex' => 'node_type is error!' ]); - + $nodeInfo = ServerService::getServer($request->input('node_id'), $request->input('node_type')); + if (!$nodeInfo) + throw new ApiException('server is not exist!'); + $request->merge(['node_info' => $nodeInfo]); return $next($request); } } diff --git a/app/Http/Routes/V1/ServerRoute.php b/app/Http/Routes/V1/ServerRoute.php index da4370d..33eb7d3 100644 --- a/app/Http/Routes/V1/ServerRoute.php +++ b/app/Http/Routes/V1/ServerRoute.php @@ -1,7 +1,10 @@ 'server', 'middleware' => 'server' ], function ($router) { - $router->any('/{class}/{action}', function($class, $action) { - $controllerClass = "\\App\\Http\\Controllers\\V1\\Server\\" . ucfirst($class) . "Controller"; - if(!(class_exists($controllerClass) && method_exists($controllerClass, $action))){ - throw new ApiException('Not Found',404); - }; - $ctrl = \App::make($controllerClass); - return \App::call([$ctrl, $action]); + $router->prefix('UniProxy')->group(function ($route) { + $route->get('config', [UniProxyController::class, 'config']); + $route->get('user', [UniProxyController::class, 'user']); + $route->post('push', [UniProxyController::class, 'push']); + $route->post('alive', [UniProxyController::class, 'alive']); + }); + $router->prefix('Deepbwork')->group(function ($route) { + $route->get('config', [DeepbworkController::class, 'config']); + $route->get('user', [DeepbworkController::class, 'user']); + $route->post('submit', [DeepbworkController::class, 'submit']); + }); + $router->prefix('ShadowsocksTidalab')->group(function ($route) { + $route->get('user', [ShadowsocksTidalabController::class, 'user']); + $route->post('submit', [ShadowsocksTidalabController::class, 'submit']); + }); + $router->prefix('TrojanTidalab')->group(function ($route) { + $route->get('config', [TrojanTidalabController::class, 'config']); + $route->get('user', [TrojanTidalabController::class, 'user']); + $route->post('submit', [TrojanTidalabController::class, 'submit']); }); }); } diff --git a/app/Jobs/SendEmailJob.php b/app/Jobs/SendEmailJob.php index 9a9f174..630368a 100644 --- a/app/Jobs/SendEmailJob.php +++ b/app/Jobs/SendEmailJob.php @@ -2,14 +2,12 @@ namespace App\Jobs; +use App\Services\MailService; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; use Illuminate\Queue\SerializesModels; -use Illuminate\Support\Facades\Config; -use Illuminate\Support\Facades\Mail; -use App\Models\MailLog; class SendEmailJob implements ShouldQueue { @@ -36,40 +34,9 @@ class SendEmailJob implements ShouldQueue */ public function handle() { - if (admin_setting('email_host')) { - Config::set('mail.host', admin_setting('email_host', config('mail.host'))); - Config::set('mail.port', admin_setting('email_port', config('mail.port'))); - Config::set('mail.encryption', admin_setting('email_encryption', config('mail.encryption'))); - Config::set('mail.username', admin_setting('email_username', config('mail.username'))); - Config::set('mail.password', admin_setting('email_password', config('mail.password'))); - Config::set('mail.from.address', admin_setting('email_from_address', config('mail.from.address'))); - Config::set('mail.from.name', admin_setting('app_name', 'XBoard')); + $mailLog = MailService::sendEmail($this->params); + if($mailLog['error']){ + $this->release(); //发送失败将触发重试 } - $params = $this->params; - $email = $params['email']; - $subject = $params['subject']; - $params['template_name'] = 'mail.' . admin_setting('email_template', 'default') . '.' . $params['template_name']; - try { - Mail::send( - $params['template_name'], - $params['template_value'], - function ($message) use ($email, $subject) { - $message->to($email)->subject($subject); - } - ); - } catch (\Exception $e) { - $error = $e->getMessage(); - } - - $log = [ - 'email' => $params['email'], - 'subject' => $params['subject'], - 'template_name' => $params['template_name'], - 'error' => isset($error) ? $error : NULL - ]; - - MailLog::create($log); - $log['config'] = config('mail'); - return $log; } } diff --git a/app/Jobs/TrafficFetchJob.php b/app/Jobs/TrafficFetchJob.php index a7fe2ea..532fe43 100644 --- a/app/Jobs/TrafficFetchJob.php +++ b/app/Jobs/TrafficFetchJob.php @@ -2,9 +2,6 @@ namespace App\Jobs; -use App\Models\User; -use App\Services\MailService; -use App\Services\ServerService; use App\Services\StatisticalService; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; diff --git a/app/Models/Order.php b/app/Models/Order.php index 576286e..62f38ce 100755 --- a/app/Models/Order.php +++ b/app/Models/Order.php @@ -14,4 +14,30 @@ class Order extends Model 'updated_at' => 'timestamp', 'surplus_order_ids' => 'array' ]; + + const STATUS_PENDING = 0; // 待支付 + const STATUS_PROCESSING = 1; // 开通中 + const STATUS_CANCELLED = 2; // 已取消 + const STATUS_COMPLETED = 3; // 已完成 + const STATUS_DISCOUNTED = 4; // 已折抵 + + public static $statusMap = [ + self::STATUS_PENDING => '待支付', + self::STATUS_PROCESSING => '开通中', + self::STATUS_CANCELLED => '已取消', + self::STATUS_COMPLETED => '已完成', + self::STATUS_DISCOUNTED => '已折抵', + ]; + + const TYPE_NEW_PURCHASE = 1; // 新购 + const TYPE_RENEWAL = 2; // 续费 + const TYPE_UPGRADE = 3; // 升级 + const TYPE_RESET_TRAFFIC = 4; //流量重置包 + public static $typeMap = [ + self::TYPE_NEW_PURCHASE => '新购', + self::TYPE_RENEWAL => '续费', + self::TYPE_UPGRADE => '升级', + self::TYPE_RESET_TRAFFIC => '流量重置', + ]; + } diff --git a/app/Models/Ticket.php b/app/Models/Ticket.php index ff098e4..5f50b48 100644 --- a/app/Models/Ticket.php +++ b/app/Models/Ticket.php @@ -14,6 +14,12 @@ class Ticket extends Model 'updated_at' => 'timestamp' ]; + const STATUS_OPENING = 0; + const STATUS_CLOSED = 1; + public static $statusMap = [ + self::STATUS_OPENING => '开启', + self::STATUS_CLOSED => '关闭' + ]; public function message() { diff --git a/app/Payments/AlipayF2F.php b/app/Payments/AlipayF2F.php index d4a587c..5fba829 100644 --- a/app/Payments/AlipayF2F.php +++ b/app/Payments/AlipayF2F.php @@ -7,6 +7,7 @@ namespace App\Payments; use App\Exceptions\ApiException; class AlipayF2F { + protected $config; public function __construct($config) { $this->config = $config; diff --git a/app/Payments/BTCPay.php b/app/Payments/BTCPay.php index 3822261..f2db1d1 100644 --- a/app/Payments/BTCPay.php +++ b/app/Payments/BTCPay.php @@ -1,11 +1,15 @@ config = $config; } @@ -35,7 +39,8 @@ class BTCPay { ]; } - public function pay($order) { + public function pay($order) + { $params = [ 'jsonResponse' => true, @@ -52,7 +57,7 @@ class BTCPay { $ret = @json_decode($ret_raw, true); - if(empty($ret['checkoutLink'])) { + if (empty($ret['checkoutLink'])) { throw new ApiException("error!"); } return [ @@ -61,7 +66,8 @@ class BTCPay { ]; } - public function notify($params) { + public function notify($params) + { $payload = trim(get_request_content()); $headers = getallheaders(); @@ -76,7 +82,7 @@ class BTCPay { $computedSignature = "sha256=" . \hash_hmac('sha256', $payload, $this->config['btcpay_webhook_key']); if (!self::hashEqual($signraturHeader, $computedSignature)) { - throw new ApiException('HMAC signature does not match',400); + throw new ApiException('HMAC signature does not match', 400); return false; } @@ -93,16 +99,16 @@ class BTCPay { $out_trade_no = $invoiceDetail['metadata']["orderId"]; - $pay_trade_no=$json_param['invoiceId']; + $pay_trade_no = $json_param['invoiceId']; return [ 'trade_no' => $out_trade_no, 'callback_no' => $pay_trade_no ]; - } - private function _curlPost($url,$params=false){ + private function _curlPost($url, $params = false) + { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); @@ -111,7 +117,9 @@ class BTCPay { curl_setopt($ch, CURLOPT_TIMEOUT, 300); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt( - $ch, CURLOPT_HTTPHEADER, array('Authorization:' .'token '.$this->config['btcpay_api_key'], 'Content-Type: application/json') + $ch, + CURLOPT_HTTPHEADER, + array('Authorization:' . 'token ' . $this->config['btcpay_api_key'], 'Content-Type: application/json') ); $result = curl_exec($ch); curl_close($ch); @@ -143,6 +151,4 @@ class BTCPay { return !$ret; } } - } - diff --git a/app/Payments/CoinPayments.php b/app/Payments/CoinPayments.php index e4bb244..93cbe47 100644 --- a/app/Payments/CoinPayments.php +++ b/app/Payments/CoinPayments.php @@ -1,10 +1,14 @@ config = $config; } @@ -99,7 +103,7 @@ class CoinPayments { throw new ApiException('Payment Timed Out or Error'); } else { //payment is pending, you can optionally add a note to the order page - return('IPN OK: pending'); + return ('IPN OK: pending'); } } } diff --git a/app/Payments/Coinbase.php b/app/Payments/Coinbase.php index 77897e1..5ceffda 100644 --- a/app/Payments/Coinbase.php +++ b/app/Payments/Coinbase.php @@ -1,10 +1,14 @@ config = $config; } @@ -29,7 +33,8 @@ class Coinbase { ]; } - public function pay($order) { + public function pay($order) + { $params = [ 'name' => '订阅套餐', @@ -50,7 +55,7 @@ class Coinbase { $ret = @json_decode($ret_raw, true); - if(empty($ret['data']['hosted_url'])) { + if (empty($ret['data']['hosted_url'])) { throw new ApiException("error!"); } return [ @@ -59,7 +64,8 @@ class Coinbase { ]; } - public function notify($params) { + public function notify($params) + { $payload = trim(get_request_content()); $json_param = json_decode($payload, true); @@ -71,20 +77,20 @@ class Coinbase { $computedSignature = \hash_hmac('sha256', $payload, $this->config['coinbase_webhook_key']); if (!self::hashEqual($signatureHeader, $computedSignature)) { - throw new ApiException( 'HMAC signature does not match', 400); + throw new ApiException('HMAC signature does not match', 400); } $out_trade_no = $json_param['event']['data']['metadata']['outTradeNo']; - $pay_trade_no=$json_param['event']['id']; + $pay_trade_no = $json_param['event']['id']; return [ 'trade_no' => $out_trade_no, 'callback_no' => $pay_trade_no ]; - } - private function _curlPost($url,$params=false){ + private function _curlPost($url, $params = false) + { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); @@ -93,7 +99,9 @@ class Coinbase { curl_setopt($ch, CURLOPT_TIMEOUT, 300); curl_setopt($ch, CURLOPT_POSTFIELDS, $params); curl_setopt( - $ch, CURLOPT_HTTPHEADER, array('X-CC-Api-Key:' .$this->config['coinbase_api_key'], 'X-CC-Version: 2018-03-22') + $ch, + CURLOPT_HTTPHEADER, + array('X-CC-Api-Key:' . $this->config['coinbase_api_key'], 'X-CC-Version: 2018-03-22') ); $result = curl_exec($ch); curl_close($ch); @@ -124,6 +132,4 @@ class Coinbase { return !$ret; } } - } - diff --git a/app/Payments/EPay.php b/app/Payments/EPay.php index a87b97a..cf60128 100644 --- a/app/Payments/EPay.php +++ b/app/Payments/EPay.php @@ -2,7 +2,9 @@ namespace App\Payments; -class EPay { +class EPay +{ + protected $config; public function __construct($config) { $this->config = $config; diff --git a/app/Payments/StripeAlipay.php b/app/Payments/StripeAlipay.php deleted file mode 100644 index ec2bc9a..0000000 --- a/app/Payments/StripeAlipay.php +++ /dev/null @@ -1,118 +0,0 @@ -config = $config; - } - - public function form() - { - return [ - 'currency' => [ - 'label' => '货币单位', - 'description' => '', - 'type' => 'input', - ], - 'stripe_sk_live' => [ - 'label' => 'SK_LIVE', - 'description' => '', - 'type' => 'input', - ], - 'stripe_webhook_key' => [ - 'label' => 'WebHook密钥签名', - 'description' => '', - 'type' => 'input', - ] - ]; - } - - public function pay($order) - { - $currency = $this->config['currency']; - $exchange = $this->exchange('CNY', strtoupper($currency)); - if (!$exchange) { - throw new ApiException(__('Currency conversion has timed out, please try again later')); - } - Stripe::setApiKey($this->config['stripe_sk_live']); - $source = Source::create([ - 'amount' => floor($order['total_amount'] * $exchange), - 'currency' => $currency, - 'type' => 'alipay', - 'statement_descriptor' => $order['trade_no'], - 'metadata' => [ - 'user_id' => $order['user_id'], - 'out_trade_no' => $order['trade_no'], - 'identifier' => '' - ], - 'redirect' => [ - 'return_url' => $order['return_url'] - ] - ]); - if (!$source['redirect']['url']) { - throw new ApiException(__('Payment gateway request failed')); - } - return [ - 'type' => 1, - 'data' => $source['redirect']['url'] - ]; - } - - public function notify($params) - { - \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $event = \Stripe\Webhook::constructEvent( - get_request_content(), - request()->header('HTTP_STRIPE_SIGNATURE'), - $this->config['stripe_webhook_key'] - ); - } catch (\Stripe\Error\SignatureVerification $e) { - abort(400); - } - switch ($event->type) { - case 'source.chargeable': - $object = $event->data->object; - \Stripe\Charge::create([ - 'amount' => $object->amount, - 'currency' => $object->currency, - 'source' => $object->id, - 'metadata' => json_decode($object->metadata, true) - ]); - break; - case 'charge.succeeded': - $object = $event->data->object; - if ($object->status === 'succeeded') { - if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { - return('order error'); - } - $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata; - $tradeNo = $metaData->out_trade_no; - return [ - 'trade_no' => $tradeNo, - 'callback_no' => $object->id - ]; - } - break; - default: - throw new ApiException('event is not support'); - } - return('success'); - } - - private function exchange($from, $to) - { - $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from); - $result = json_decode($result, true); - return $result['rates'][$to]; - } -} diff --git a/app/Payments/StripeCheckout.php b/app/Payments/StripeCheckout.php deleted file mode 100644 index cd0688e..0000000 --- a/app/Payments/StripeCheckout.php +++ /dev/null @@ -1,140 +0,0 @@ -config = $config; - } - - public function form() - { - return [ - 'currency' => [ - 'label' => '货币单位', - 'description' => '', - 'type' => 'input', - ], - 'stripe_sk_live' => [ - 'label' => 'SK_LIVE', - 'description' => 'API 密钥', - 'type' => 'input', - ], - 'stripe_pk_live' => [ - 'label' => 'PK_LIVE', - 'description' => 'API 公钥', - 'type' => 'input', - ], - 'stripe_webhook_key' => [ - 'label' => 'WebHook 密钥签名', - 'description' => '', - 'type' => 'input', - ], - 'stripe_custom_field_name' => [ - 'label' => '自定义字段名称', - 'description' => '例如可设置为“联系方式”,以便及时与客户取得联系', - 'type' => 'input', - ] - ]; - } - - public function pay($order) - { - $currency = $this->config['currency']; - $exchange = $this->exchange('CNY', strtoupper($currency)); - if (!$exchange) { - throw new ApiException(__('Currency conversion has timed out, please try again later')); - } - $customFieldName = isset($this->config['stripe_custom_field_name']) ? $this->config['stripe_custom_field_name'] : 'Contact Infomation'; - - $params = [ - 'success_url' => $order['return_url'], - 'cancel_url' => $order['return_url'], - 'client_reference_id' => $order['trade_no'], - 'line_items' => [ - [ - 'price_data' => [ - 'currency' => $currency, - 'product_data' => [ - 'name' => $order['trade_no'] - ], - 'unit_amount' => floor($order['total_amount'] * $exchange) - ], - 'quantity' => 1 - ] - ], - 'mode' => 'payment', - 'invoice_creation' => ['enabled' => true], - 'phone_number_collection' => ['enabled' => true], - 'custom_fields' => [ - [ - 'key' => 'contactinfo', - 'label' => ['type' => 'custom', 'custom' => $customFieldName], - 'type' => 'text', - ], - ], - // 'customer_email' => $user['email'] not support - - ]; - - Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $session = Session::create($params); - } catch (\Exception $e) { - info($e); - throw new ApiException("Failed to create order. Error: {$e->getMessage}"); - } - return [ - 'type' => 1, // 0:qrcode 1:url - 'data' => $session->url - ]; - } - - public function notify($params) - { - \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $event = \Stripe\Webhook::constructEvent( - get_request_content(), - request()->header('HTTP_STRIPE_SIGNATURE'), - $this->config['stripe_webhook_key'] - ); - } catch (\Stripe\Error\SignatureVerification $e) { - abort(400); - } - - switch ($event->type) { - case 'checkout.session.completed': - $object = $event->data->object; - if ($object->payment_status === 'paid') { - return [ - 'trade_no' => $object->client_reference_id, - 'callback_no' => $object->payment_intent - ]; - } - break; - case 'checkout.session.async_payment_succeeded': - $object = $event->data->object; - return [ - 'trade_no' => $object->client_reference_id, - 'callback_no' => $object->payment_intent - ]; - break; - default: - throw new ApiException('event is not support'); - } - return('success'); - } - - private function exchange($from, $to) - { - $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from); - $result = json_decode($result, true); - return $result['rates'][$to]; - } -} diff --git a/app/Payments/StripeCredit.php b/app/Payments/StripeCredit.php deleted file mode 100644 index 6cbb8ee..0000000 --- a/app/Payments/StripeCredit.php +++ /dev/null @@ -1,126 +0,0 @@ -config = $config; - } - - public function form() - { - return [ - 'currency' => [ - 'label' => '货币单位', - 'description' => '', - 'type' => 'input', - ], - 'stripe_sk_live' => [ - 'label' => 'SK_LIVE', - 'description' => '', - 'type' => 'input', - ], - 'stripe_pk_live' => [ - 'label' => 'PK_LIVE', - 'description' => '', - 'type' => 'input', - ], - 'stripe_webhook_key' => [ - 'label' => 'WebHook密钥签名', - 'description' => '', - 'type' => 'input', - ] - ]; - } - - public function pay($order) - { - info($order); - $currency = $this->config['currency']; - $exchange = $this->exchange('CNY', strtoupper($currency)); - if (!$exchange) { - throw new ApiException(__('Currency conversion has timed out, please try again later')); - } - Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $charge = \Stripe\Charge::create([ - 'amount' => floor($order['total_amount'] * $exchange), - 'currency' => $currency, - 'source' => $order['stripe_token'], - 'metadata' => [ - 'user_id' => $order['user_id'], - 'out_trade_no' => $order['trade_no'], - 'identifier' => '' - ] - ]); - } catch (\Exception $e) { - info($e); - throw new ApiException(__('Payment failed. Please check your credit card information')); - } - if (!$charge->paid) { - throw new ApiException(__('Payment failed. Please check your credit card information')); - } - return [ - 'type' => 2, - 'data' => $charge->paid - ]; - } - - public function notify($params) - { - \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $event = \Stripe\Webhook::constructEvent( - get_request_content(), - request()->header('HTTP_STRIPE_SIGNATURE'), - $this->config['stripe_webhook_key'] - ); - } catch (\Stripe\Error\SignatureVerification $e) { - \Log::error($e); - abort(400); - } - switch ($event->type) { - case 'source.chargeable': - $object = $event->data->object; - \Stripe\Charge::create([ - 'amount' => $object->amount, - 'currency' => $object->currency, - 'source' => $object->id, - 'metadata' => json_decode($object->metadata, true) - ]); - break; - case 'charge.succeeded': - $object = $event->data->object; - if ($object->status === 'succeeded') { - if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { - return('order error'); - } - $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata; - $tradeNo = $metaData->out_trade_no; - return [ - 'trade_no' => $tradeNo, - 'callback_no' => $object->id - ]; - } - break; - default: - throw new ApiException('event is not support'); - } - return('success'); - } - - private function exchange($from, $to) - { - $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from); - $result = json_decode($result, true); - return $result['rates'][$to]; - } -} diff --git a/app/Payments/StripeWepay.php b/app/Payments/StripeWepay.php deleted file mode 100644 index 885655e..0000000 --- a/app/Payments/StripeWepay.php +++ /dev/null @@ -1,118 +0,0 @@ -config = $config; - } - - public function form() - { - return [ - 'currency' => [ - 'label' => '货币单位', - 'description' => '', - 'type' => 'input', - ], - 'stripe_sk_live' => [ - 'label' => 'SK_LIVE', - 'description' => '', - 'type' => 'input', - ], - 'stripe_webhook_key' => [ - 'label' => 'WebHook密钥签名', - 'description' => '', - 'type' => 'input', - ] - ]; - } - - public function pay($order) - { - $currency = $this->config['currency']; - $exchange = $this->exchange('CNY', strtoupper($currency)); - if (!$exchange) { - throw new ApiException(__('Currency conversion has timed out, please try again later')); - } - Stripe::setApiKey($this->config['stripe_sk_live']); - $source = Source::create([ - 'amount' => floor($order['total_amount'] * $exchange), - 'currency' => $currency, - 'type' => 'wechat', - 'statement_descriptor' => $order['trade_no'], - 'metadata' => [ - 'user_id' => $order['user_id'], - 'out_trade_no' => $order['trade_no'], - 'identifier' => '' - ], - 'redirect' => [ - 'return_url' => $order['return_url'] - ] - ]); - if (!$source['wechat']['qr_code_url']) { - throw new ApiException(__('Payment gateway request failed')); - } - return [ - 'type' => 0, - 'data' => $source['wechat']['qr_code_url'] - ]; - } - - public function notify($params) - { - \Stripe\Stripe::setApiKey($this->config['stripe_sk_live']); - try { - $event = \Stripe\Webhook::constructEvent( - get_request_content(), - request()->header('HTTP_STRIPE_SIGNATURE'), - $this->config['stripe_webhook_key'] - ); - } catch (\Stripe\Error\SignatureVerification $e) { - abort(400); - } - switch ($event->type) { - case 'source.chargeable': - $object = $event->data->object; - \Stripe\Charge::create([ - 'amount' => $object->amount, - 'currency' => $object->currency, - 'source' => $object->id, - 'metadata' => json_decode($object->metadata, true) - ]); - break; - case 'charge.succeeded': - $object = $event->data->object; - if ($object->status === 'succeeded') { - if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { - return('order error'); - } - $metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata; - $tradeNo = $metaData->out_trade_no; - return [ - 'trade_no' => $tradeNo, - 'callback_no' => $object->id - ]; - } - break; - default: - throw new ApiException('event is not support'); - } - return('success'); - } - - private function exchange($from, $to) - { - $result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from); - $result = json_decode($result, true); - return $result['rates'][$to]; - } -} diff --git a/app/Payments/WechatPayNative.php b/app/Payments/WechatPayNative.php deleted file mode 100644 index 7ac31cf..0000000 --- a/app/Payments/WechatPayNative.php +++ /dev/null @@ -1,85 +0,0 @@ -config = $config; - } - - public function form() - { - return [ - 'app_id' => [ - 'label' => 'APPID', - 'description' => '绑定微信支付商户的APPID', - 'type' => 'input', - ], - 'mch_id' => [ - 'label' => '商户号', - 'description' => '微信支付商户号', - 'type' => 'input', - ], - 'api_key' => [ - 'label' => 'APIKEY(v1)', - 'description' => '', - 'type' => 'input', - ] - ]; - } - - public function pay($order) - { - $gateway = Omnipay::create('WechatPay_Native'); - $gateway->setAppId($this->config['app_id']); - $gateway->setMchId($this->config['mch_id']); - $gateway->setApiKey($this->config['api_key']); - $gateway->setNotifyUrl($order['notify_url']); - - $params = [ - 'body' => $order['trade_no'], - 'out_trade_no' => $order['trade_no'], - 'total_fee' => $order['total_amount'], - 'spbill_create_ip' => '0.0.0.0', - 'fee_type' => 'CNY' - ]; - - $request = $gateway->purchase($params); - $response = $request->send(); - $response = $response->getData(); - if ($response['return_code'] !== 'SUCCESS') { - throw new ApiException($response['return_msg']); - } - return [ - 'type' => 0, - 'data' => $response['code_url'], - 'custom_result' => '' - ]; - } - - public function notify($params) - { - $data = Helper::xml2array(get_request_content()); - $gateway = Omnipay::create('WechatPay'); - $gateway->setAppId($this->config['app_id']); - $gateway->setMchId($this->config['mch_id']); - $gateway->setApiKey($this->config['api_key']); - $response = $gateway->completePurchase([ - 'request_params' => get_request_content() - ])->send(); - - if (!$response->isPaid()) { - return('FAIL'); - } - - return [ - 'trade_no' => $data['out_trade_no'], - 'callback_no' => $data['transaction_id'] - ]; - } -} diff --git a/app/Providers/SettingServiceProvider.php b/app/Providers/SettingServiceProvider.php index 9654fb9..82736e1 100644 --- a/app/Providers/SettingServiceProvider.php +++ b/app/Providers/SettingServiceProvider.php @@ -4,6 +4,7 @@ namespace App\Providers; use App\Support\Setting; use Illuminate\Support\ServiceProvider; +use Illuminate\Contracts\Foundation\Application; class SettingServiceProvider extends ServiceProvider { @@ -14,9 +15,11 @@ class SettingServiceProvider extends ServiceProvider */ public function register() { - $this->app->bind('setting', function ($app) { - return Setting::fromDatabase(); // 假设 AdminSetting 是您的设置类 + + $this->app->bind(Setting::class, function (Application $app) { + return new Setting(); }); + } /** @@ -26,6 +29,5 @@ class SettingServiceProvider extends ServiceProvider */ public function boot() { - // } } diff --git a/app/Services/MailService.php b/app/Services/MailService.php index ac6bcfe..4144662 100644 --- a/app/Services/MailService.php +++ b/app/Services/MailService.php @@ -9,13 +9,17 @@ use Illuminate\Support\Facades\Cache; class MailService { - public function remindTraffic (User $user) + public function remindTraffic(User $user) { - if (!$user->remind_traffic) return; - if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) return; + if (!$user->remind_traffic) + return; + if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) + return; $flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id); - if (Cache::get($flag)) return; - if (!Cache::put($flag, 1, 24 * 3600)) return; + if (Cache::get($flag)) + return; + if (!Cache::put($flag, 1, 24 * 3600)) + return; SendEmailJob::dispatch([ 'email' => $user->email, 'subject' => __('The traffic usage in :app_name has reached 80%', [ @@ -31,11 +35,12 @@ class MailService public function remindExpire(User $user) { - if (!($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time())) return; + if (!($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time())) + return; SendEmailJob::dispatch([ 'email' => $user->email, 'subject' => __('The service in :app_name is about to expire', [ - 'app_name' => admin_setting('app_name', 'XBoard') + 'app_name' => admin_setting('app_name', 'XBoard') ]), 'template_name' => 'remindExpire', 'template_value' => [ @@ -48,11 +53,67 @@ class MailService private function remindTrafficIsWarnValue($u, $d, $transfer_enable) { $ud = $u + $d; - if (!$ud) return false; - if (!$transfer_enable) return false; + if (!$ud) + return false; + if (!$transfer_enable) + return false; $percentage = ($ud / $transfer_enable) * 100; - if ($percentage < 80) return false; - if ($percentage >= 100) return false; + if ($percentage < 80) + return false; + if ($percentage >= 100) + return false; return true; } + + /** + * 发送邮件 + * + * @param array $params 包含邮件参数的数组,必须包含以下字段: + * - email: 收件人邮箱地址 + * - subject: 邮件主题 + * - template_name: 邮件模板名称,例如 "welcome" 或 "password_reset" + * - template_value: 邮件模板变量,一个关联数组,包含模板中需要替换的变量和对应的值 + * @return array 包含邮件发送结果的数组,包含以下字段: + * - email: 收件人邮箱地址 + * - subject: 邮件主题 + * - template_name: 邮件模板名称 + * - error: 如果邮件发送失败,包含错误信息;否则为 null + * @throws \InvalidArgumentException 如果 $params 参数缺少必要的字段,抛出此异常 + */ + public static function sendEmail(array $params) + { + if (admin_setting('email_host')) { + \Config::set('mail.host', admin_setting('email_host', config('mail.host'))); + \Config::set('mail.port', admin_setting('email_port', config('mail.port'))); + \Config::set('mail.encryption', admin_setting('email_encryption', config('mail.encryption'))); + \Config::set('mail.username', admin_setting('email_username', config('mail.username'))); + \Config::set('mail.password', admin_setting('email_password', config('mail.password'))); + \Config::set('mail.from.address', admin_setting('email_from_address', config('mail.from.address'))); + \Config::set('mail.from.name', admin_setting('app_name', 'XBoard')); + } + $email = $params['email']; + $subject = $params['subject']; + $params['template_name'] = 'mail.' . admin_setting('email_template', 'default') . '.' . $params['template_name']; + try { + \Mail::send( + $params['template_name'], + $params['template_value'], + function ($message) use ($email, $subject) { + $message->to($email)->subject($subject); + } + ); + $error = null; + } catch (\Exception $e) { + $error = $e->getMessage(); + } + $log = [ + 'email' => $params['email'], + 'subject' => $params['subject'], + 'template_name' => $params['template_name'], + 'error' => $error, + 'config' => config('mail') + ]; + \App\Models\MailLog::create($log); + return $log; + } } diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 615e1ec..85b1e98 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -40,7 +40,7 @@ class OrderService DB::beginTransaction(); if ($order->surplus_order_ids) { Order::whereIn('id', $order->surplus_order_ids)->update([ - 'status' => 4 + 'status' => Order::STATUS_DISCOUNTED ]); } switch ((string)$order->period) { @@ -71,7 +71,7 @@ class OrderService if (!$this->user->save()) { throw new \Exception('用户信息保存失败'); } - $order->status = 3; + $order->status = Order::STATUS_COMPLETED; if (!$order->save()) { throw new \Exception('订单信息保存失败'); } @@ -88,10 +88,10 @@ class OrderService { $order = $this->order; if ($order->period === 'reset_price') { - $order->type = 4; + $order->type = Order::TYPE_RESET_TRAFFIC; } else if ($user->plan_id !== NULL && $order->plan_id !== $user->plan_id && ($user->expired_at > time() || $user->expired_at === NULL)) { if (!(int)admin_setting('plan_change_enable', 1)) throw new ApiException('目前不允许更改订阅,请联系客服或提交工单操作'); - $order->type = 3; + $order->type = Order::TYPE_UPGRADE; if ((int)admin_setting('surplus_enable', 1)) $this->getSurplusValue($user, $order); if ($order->surplus_amount >= $order->total_amount) { $order->refund_amount = $order->surplus_amount - $order->total_amount; @@ -100,9 +100,9 @@ class OrderService $order->total_amount = $order->total_amount - $order->surplus_amount; } } else if ($user->expired_at > time() && $order->plan_id == $user->plan_id) { // 用户订阅未过期且购买订阅与当前订阅相同 === 续费 - $order->type = 2; + $order->type = Order::TYPE_RENEWAL; } else { // 新购 - $order->type = 1; + $order->type = Order::TYPE_NEW_PURCHASE; } } @@ -215,8 +215,8 @@ class OrderService public function paid(string $callbackNo) { $order = $this->order; - if ($order->status !== 0) return true; - $order->status = 1; + if ($order->status !== Order::STATUS_PENDING) return true; + $order->status = Order::STATUS_PROCESSING; $order->paid_at = time(); $order->callback_no = $callbackNo; if (!$order->save()) return false; @@ -233,7 +233,7 @@ class OrderService $order = $this->order; try { DB::beginTransaction(); - $order->status = 2; + $order->status = Order::STATUS_CANCELLED; if (!$order->save()) { throw new \Exception('Failed to save order status.'); } diff --git a/app/Services/ServerService.php b/app/Services/ServerService.php index 5881b23..26299b2 100644 --- a/app/Services/ServerService.php +++ b/app/Services/ServerService.php @@ -17,7 +17,8 @@ use Illuminate\Support\Facades\Cache; class ServerService { - public function getAvailableVless(User $user):array + // 获取可用的 VLESS 服务器列表 + public static function getAvailableVless(User $user): array { $servers = []; $model = ServerVless::orderBy('sort', 'ASC'); @@ -41,15 +42,15 @@ class ServerService unset($serverData['tls_settings']['private_key']); } } - + $servers[] = $serverData; } - return $servers; } - public function getAvailableVmess(User $user):array + // 获取可用的 VMESS 服务器列表 + public static function getAvailableVmess(User $user): array { $servers = []; $model = ServerVmess::orderBy('sort', 'ASC'); @@ -69,11 +70,11 @@ class ServerService $servers[] = $vmess[$key]->toArray(); } - return $servers; } - public function getAvailableTrojan(User $user):array + // 获取可用的 TROJAN 服务器列表 + public static function getAvailableTrojan(User $user): array { $servers = []; $model = ServerTrojan::orderBy('sort', 'ASC'); @@ -95,7 +96,8 @@ class ServerService return $servers; } - public function getAvailableHysteria(User $user) + // 获取可用的 HYSTERIA 服务器列表 + public static function getAvailableHysteria(User $user) { $availableServers = []; $model = ServerHysteria::orderBy('sort', 'ASC'); @@ -119,7 +121,8 @@ class ServerService return $availableServers; } - public function getAvailableShadowsocks(User $user) + // 获取可用的 SHADOWSOCKS 服务器列表 + public static function getAvailableShadowsocks(User $user) { $servers = []; $model = ServerShadowsocks::orderBy('sort', 'ASC'); @@ -141,15 +144,16 @@ class ServerService return $servers; } - public function getAvailableServers(User $user) + // 获取可用的服务器列表 + public static function getAvailableServers(User $user) { $servers = Cache::remember('serversAvailable_'. $user->id, 5, function() use($user){ return array_merge( - $this->getAvailableShadowsocks($user), - $this->getAvailableVmess($user), - $this->getAvailableTrojan($user), - $this->getAvailableHysteria($user), - $this->getAvailableVless($user) + self::getAvailableShadowsocks($user), + self::getAvailableVmess($user), + self::getAvailableTrojan($user), + self::getAvailableHysteria($user), + self::getAvailableVless($user) ); }); $tmp = array_column($servers, 'sort'); @@ -162,7 +166,8 @@ class ServerService }, $servers); } - public function getAvailableUsers($groupId): Collection + // 获取可用的用户列表 + public static function getAvailableUsers($groupId): Collection { return \DB::table('v2_user') ->whereIn('group_id', $groupId) @@ -180,7 +185,8 @@ class ServerService ->get(); } - public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method) + // 记录流量日志 + public static function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method) { if (($u + $d) < 10240) return true; $timestamp = strtotime(date('Y-m-d')); @@ -212,7 +218,8 @@ class ServerService } } - public function getAllShadowsocks() + // 获取所有 SHADOWSOCKS 服务器列表 + public static function getAllShadowsocks() { $servers = ServerShadowsocks::orderBy('sort', 'ASC') ->get() @@ -223,7 +230,8 @@ class ServerService return $servers; } - public function getAllVMess() + // 获取所有 VMESS 服务器列表 + public static function getAllVMess() { $servers = ServerVmess::orderBy('sort', 'ASC') ->get() @@ -234,7 +242,8 @@ class ServerService return $servers; } - public function getAllVLess() + // 获取所有 VLESS 服务器列表 + public static function getAllVLess() { $servers = ServerVless::orderBy('sort', 'ASC') ->get() @@ -245,7 +254,8 @@ class ServerService return $servers; } - public function getAllTrojan() + // 获取所有 TROJAN 服务器列表 + public static function getAllTrojan() { $servers = ServerTrojan::orderBy('sort', 'ASC') ->get() @@ -256,7 +266,8 @@ class ServerService return $servers; } - public function getAllHysteria() + // 获取所有 HYSTERIA 服务器列表 + public static function getAllHysteria() { $servers = ServerHysteria::orderBy('sort', 'ASC') ->get() @@ -267,7 +278,8 @@ class ServerService return $servers; } - private function mergeData(&$servers) + // 合并数据 + private static function mergeData(&$servers) { foreach ($servers as $k => $v) { $serverType = strtoupper($v['type']); @@ -291,22 +303,24 @@ class ServerService } } - public function getAllServers() + // 获取所有服务器列表 + public static function getAllServers() { $servers = array_merge( - $this->getAllShadowsocks(), - $this->getAllVMess(), - $this->getAllTrojan(), - $this->getAllHysteria(), - $this->getAllVLess() + self::getAllShadowsocks(), + self::getAllVMess(), + self::getAllTrojan(), + self::getAllHysteria(), + self::getAllVLess() ); - $this->mergeData($servers); + self::mergeData($servers); $tmp = array_column($servers, 'sort'); array_multisort($tmp, SORT_ASC, $servers); return $servers; } - public function getRoutes(array $routeIds) + // 获取路由规则 + public static function getRoutes(array $routeIds) { $routes = ServerRoute::select(['id', 'match', 'action', 'action_value'])->whereIn('id', $routeIds)->get(); // TODO: remove on 1.8.0 @@ -318,7 +332,8 @@ class ServerService return $routes; } - public function getServer($serverId, $serverType) + // 获取服务器 + public static function getServer($serverId, $serverType) { switch ($serverType) { case 'vmess': @@ -336,8 +351,8 @@ class ServerService } } - // 根据节点IP和父级别节点ID查询字节点 - public function getChildServer($serverId, $serverType, $nodeIp){ + // 根据节点IP和父级别节点ID查询子节点 + public static function getChildServer($serverId, $serverType, $nodeIp){ switch ($serverType) { case 'vmess': return ServerVmess::query() diff --git a/app/Services/TicketService.php b/app/Services/TicketService.php index 2ee5b5a..11cbed1 100644 --- a/app/Services/TicketService.php +++ b/app/Services/TicketService.php @@ -21,9 +21,9 @@ class TicketService { 'message' => $message ]); if ($userId !== $ticket->user_id) { - $ticket->reply_status = 0; + $ticket->reply_status = Ticket::STATUS_OPENING; } else { - $ticket->reply_status = 1; + $ticket->reply_status = Ticket::STATUS_CLOSED; } if (!$ticketMessage || !$ticket->save()) { throw new \Exception(); @@ -43,7 +43,7 @@ class TicketService { if (!$ticket) { throw new ApiException('工单不存在'); } - $ticket->status = 0; + $ticket->status = Ticket::STATUS_OPENING; try{ DB::beginTransaction(); $ticketMessage = TicketMessage::create([ @@ -52,9 +52,9 @@ class TicketService { 'message' => $message ]); if ($userId !== $ticket->user_id) { - $ticket->reply_status = 0; + $ticket->reply_status = Ticket::STATUS_OPENING; } else { - $ticket->reply_status = 1; + $ticket->reply_status = Ticket::STATUS_CLOSED; } if (!$ticketMessage || !$ticket->save()) { throw new ApiException('工单回复失败'); diff --git a/app/Services/UserService.php b/app/Services/UserService.php index 79613bc..0bc4fd6 100644 --- a/app/Services/UserService.php +++ b/app/Services/UserService.php @@ -2,8 +2,6 @@ namespace App\Services; -use App\Jobs\StatServerJob; -use App\Jobs\StatUserJob; use App\Jobs\TrafficFetchJob; use App\Models\Order; use App\Models\Plan; @@ -172,7 +170,7 @@ class UserService { // 获取子节点 $childServer = ($server['parent_id'] == null && !blank($nodeIp)) - ? (new ServerService())->getChildServer($server['id'], $protocol, $nodeIp) + ? ServerService::getChildServer($server['id'], $protocol, $nodeIp) : null; $timestamp = strtotime(date('Y-m-d')); collect($data)->chunk(1000)->each(function($chunk) use ($timestamp,$server,$protocol, $childServer){ diff --git a/app/Support/Setting.php b/app/Support/Setting.php index e646add..fa84b5c 100644 --- a/app/Support/Setting.php +++ b/app/Support/Setting.php @@ -10,6 +10,10 @@ use Illuminate\Support\Fluent; class Setting extends Fluent { + public function __construct() + { + $this->attributes = self::fromDatabase(); + } /** * 获取配置,并转化为数组. * @@ -129,21 +133,12 @@ class Setting extends Fluent public static function fromDatabase() { $values = []; - try { - if(env('ADMIN_SETTING_CACHE') > 0){ - $values = Cache::remember('admin_settings', env('ADMIN_SETTING_CACHE'), function () { - return SettingModel::pluck('value', 'name')->toArray(); - } - ); - }else{ - $values = SettingModel::pluck('value', 'name')->toArray(); - } + $values = Cache::remember('admin_settings', env('ADMIN_SETTING_CACHE', 0), function () { + return SettingModel::pluck('value', 'name')->toArray(); + }); } catch (QueryException $e) { - return new static($values); - // throw new \Exception('配置获取失败、请检查数据库配置'); } - - return new static($values); + return $values; } } diff --git a/composer.json b/composer.json index 66a96ae..fc92b1f 100755 --- a/composer.json +++ b/composer.json @@ -22,10 +22,8 @@ "joanhey/adapterman": "^0.6.1", "laravel/framework": "^10.0", "laravel/horizon": "^5.9.6", - "laravel/pulse": "*", "laravel/tinker": "^2.5", "linfo/linfo": "^4.0", - "nunomaduro/collision": "^7.10", "paragonie/sodium_compat": "^1.20", "php-curl-class/php-curl-class": "^8.6", "spatie/db-dumper": "^3.4", @@ -39,6 +37,7 @@ "barryvdh/laravel-debugbar": "^3.9", "fakerphp/faker": "^1.9.1", "mockery/mockery": "^1.3.1", + "nunomaduro/collision": "^7.10", "orangehill/iseed": "^3.0", "phpunit/phpunit": "^10.0", "spatie/laravel-ignition": "^2.0"