mirror of
				https://github.com/v2board/v2board.git
				synced 2025-11-01 01:41:47 +08:00 
			
		
		
		
	| @@ -41,6 +41,7 @@ class CheckTicket extends Command | ||||
|         ini_set('memory_limit', -1); | ||||
|         $tickets = Ticket::where('status', 0) | ||||
|             ->where('updated_at', '<=', time() - 24 * 3600) | ||||
|             ->where('reply_status', 0) | ||||
|             ->get(); | ||||
|         foreach ($tickets as $ticket) { | ||||
|             if ($ticket->user_id === $ticket->last_reply_user_id) continue; | ||||
|   | ||||
							
								
								
									
										49
									
								
								app/Console/Commands/ResetLog.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								app/Console/Commands/ResetLog.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Console\Commands; | ||||
|  | ||||
| use App\Models\Plan; | ||||
| use App\Models\StatUser; | ||||
| use App\Utils\Helper; | ||||
| use Illuminate\Console\Command; | ||||
| use App\Models\User; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class ResetLog extends Command | ||||
| { | ||||
|     protected $builder; | ||||
|     /** | ||||
|      * The name and signature of the console command. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $signature = 'reset:log'; | ||||
|  | ||||
|     /** | ||||
|      * The console command description. | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $description = '清空日志'; | ||||
|  | ||||
|     /** | ||||
|      * Create a new command instance. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() | ||||
|     { | ||||
|         parent::__construct(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the console command. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function handle() | ||||
|     { | ||||
|         StatUser::where('record_at', '<', strtotime('-2 month', time())) | ||||
|             ->delete(); | ||||
|     } | ||||
| } | ||||
| @@ -2,15 +2,7 @@ | ||||
|  | ||||
| namespace App\Console\Commands; | ||||
|  | ||||
| use App\Models\Order; | ||||
| use App\Models\User; | ||||
| use App\Utils\CacheKey; | ||||
| use App\Utils\Helper; | ||||
| use Illuminate\Console\Command; | ||||
| use Illuminate\Filesystem\Filesystem; | ||||
| use Illuminate\Foundation\Console\ConfigCacheCommand; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use Matriphe\Larinfo; | ||||
|  | ||||
| class Test extends Command | ||||
| { | ||||
|   | ||||
| @@ -53,11 +53,10 @@ class V2boardStatistics extends Command | ||||
|             ->whereNotIn('status', [0, 2]); | ||||
|         $orderCount = $orderBuilder->count(); | ||||
|         $orderAmount = $orderBuilder->sum('total_amount'); | ||||
|         $commissionBuilder = CommissionLog::where('created_at', '>=', $startAt) | ||||
|             ->where('created_at', '<', $endAt) | ||||
|             ->where('get_amount', '>', 0); | ||||
|         $commissionBuilder = Order::where('created_at', '>=', $startAt) | ||||
|             ->where('created_at', '<', $endAt); | ||||
|         $commissionCount = $commissionBuilder->count(); | ||||
|         $commissionAmount = $commissionBuilder->sum('get_amount'); | ||||
|         $commissionAmount = $commissionBuilder->sum('actual_commission_balance'); | ||||
|         $data = [ | ||||
|             'order_count' => $orderCount, | ||||
|             'order_amount' => $orderAmount, | ||||
|   | ||||
| @@ -35,6 +35,7 @@ class Kernel extends ConsoleKernel | ||||
|         $schedule->command('check:ticket')->everyMinute(); | ||||
|         // reset | ||||
|         $schedule->command('reset:traffic')->daily(); | ||||
|         $schedule->command('reset:log')->daily(); | ||||
|         // send | ||||
|         $schedule->command('send:remindMail')->dailyAt('11:30'); | ||||
|         // horizon metrics | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class ConfigController extends Controller | ||||
|     public function testSendMail(Request $request) | ||||
|     { | ||||
|         $obj = new SendEmailJob([ | ||||
|             'email' => $request->session()->get('email'), | ||||
|             'email' => $request->user['email'], | ||||
|             'subject' => 'This is v2board test email', | ||||
|             'template_name' => 'notify', | ||||
|             'template_value' => [ | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use App\Utils\Helper; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Models\Payment; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class PaymentController extends Controller | ||||
| { | ||||
| @@ -24,7 +25,7 @@ class PaymentController extends Controller | ||||
|  | ||||
|     public function fetch() | ||||
|     { | ||||
|         $payments = Payment::all(); | ||||
|         $payments = Payment::orderBy('sort', 'ASC')->get(); | ||||
|         foreach ($payments as $k => $v) { | ||||
|             $notifyUrl = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}"); | ||||
|             if ($v->notify_domain) { | ||||
| @@ -107,4 +108,26 @@ class PaymentController extends Controller | ||||
|             'data' => $payment->delete() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function sort(Request $request) | ||||
|     { | ||||
|         $request->validate([ | ||||
|             'ids' => 'required|array' | ||||
|         ], [ | ||||
|             'ids.required' => '参数有误', | ||||
|             'ids.array' => '参数有误' | ||||
|         ]); | ||||
|         DB::beginTransaction(); | ||||
|         foreach ($request->input('ids') as $k => $v) { | ||||
|             if (!Payment::find($v)->update(['sort' => $k + 1])) { | ||||
|                 DB::rollBack(); | ||||
|                 abort(500, '保存失败'); | ||||
|             } | ||||
|         } | ||||
|         DB::commit(); | ||||
|         return response([ | ||||
|             'data' => true | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin; | ||||
| use App\Http\Requests\Admin\PlanSave; | ||||
| use App\Http\Requests\Admin\PlanSort; | ||||
| use App\Http\Requests\Admin\PlanUpdate; | ||||
| use App\Services\PlanService; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Models\Plan; | ||||
| @@ -16,17 +17,7 @@ class PlanController extends Controller | ||||
| { | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $counts = User::select( | ||||
|             DB::raw("plan_id"), | ||||
|             DB::raw("count(*) as count") | ||||
|         ) | ||||
|             ->where('plan_id', '!=', NULL) | ||||
|             ->where(function ($query) { | ||||
|                 $query->where('expired_at', '>=', time()) | ||||
|                     ->orWhere('expired_at', NULL); | ||||
|             }) | ||||
|             ->groupBy("plan_id") | ||||
|             ->get(); | ||||
|         $counts = PlanService::countActiveUsers(); | ||||
|         $plans = Plan::orderBy('sort', 'ASC')->get(); | ||||
|         foreach ($plans as $k => $v) { | ||||
|             $plans[$k]->count = 0; | ||||
|   | ||||
| @@ -89,13 +89,4 @@ class V2rayController extends Controller | ||||
|             'data' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function viewConfig(Request $request) | ||||
|     { | ||||
|         $serverService = new ServerService(); | ||||
|         $config = $serverService->getV2RayConfig($request->input('node_id'), 23333); | ||||
|         return response([ | ||||
|             'data' => $config | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Admin; | ||||
|  | ||||
| use App\Models\ServerShadowsocks; | ||||
| use App\Models\ServerTrojan; | ||||
| use App\Models\StatUser; | ||||
| use App\Services\ServerService; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Http\Controllers\Controller; | ||||
| @@ -45,7 +46,15 @@ class StatController extends Controller | ||||
|                 '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') | ||||
|                     ->sum('total_amount'), | ||||
|                 'commission_month_payout' => Order::where('actual_commission_balance' ,'!=', NULL) | ||||
|                     ->where('created_at', '>=', strtotime(date('Y-m-1'))) | ||||
|                     ->where('created_at', '<', time()) | ||||
|                     ->sum('actual_commission_balance'), | ||||
|                 'commission_last_month_payout' => Order::where('actual_commission_balance' ,'!=', NULL) | ||||
|                     ->where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1')))) | ||||
|                     ->where('created_at', '<', strtotime(date('Y-m-1'))) | ||||
|                     ->sum('actual_commission_balance'), | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
| @@ -123,5 +132,23 @@ class StatController extends Controller | ||||
|             '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(); | ||||
|         return [ | ||||
|             'data' => $records, | ||||
|             'total' => $total | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -19,11 +19,16 @@ use App\Models\StatServer; | ||||
| 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; | ||||
| use Laravel\Horizon\Contracts\SupervisorRepository; | ||||
| use Laravel\Horizon\Contracts\WorkloadRepository; | ||||
| use Laravel\Horizon\WaitTimeCalculator; | ||||
|  | ||||
| class SystemController extends Controller | ||||
| { | ||||
|     public function getStatus() | ||||
|     public function getSystemStatus() | ||||
|     { | ||||
|         return response([ | ||||
|             'data' => [ | ||||
| @@ -33,6 +38,13 @@ class SystemController extends Controller | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getQueueWorkload(WorkloadRepository $workload) | ||||
|     { | ||||
|         return response([ | ||||
|             'data' => collect($workload->get())->sortBy('name')->values()->toArray() | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     protected function getScheduleStatus():bool | ||||
|     { | ||||
|         return (time() - 120) < Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null)); | ||||
| @@ -48,5 +60,56 @@ class SystemController extends Controller | ||||
|             return $master->status === 'paused'; | ||||
|         }) ? false : true; | ||||
|     } | ||||
|  | ||||
|     public function getQueueStats() | ||||
|     { | ||||
|         return response([ | ||||
|             'data' => [ | ||||
|                 'failedJobs' => app(JobRepository::class)->countRecentlyFailed(), | ||||
|                 'jobsPerMinute' => app(MetricsRepository::class)->jobsProcessedPerMinute(), | ||||
|                 'pausedMasters' => $this->totalPausedMasters(), | ||||
|                 'periods' => [ | ||||
|                     'failedJobs' => config('horizon.trim.recent_failed', config('horizon.trim.failed')), | ||||
|                     'recentJobs' => config('horizon.trim.recent'), | ||||
|                 ], | ||||
|                 'processes' => $this->totalProcessCount(), | ||||
|                 'queueWithMaxRuntime' => app(MetricsRepository::class)->queueWithMaximumRuntime(), | ||||
|                 'queueWithMaxThroughput' => app(MetricsRepository::class)->queueWithMaximumThroughput(), | ||||
|                 'recentJobs' => app(JobRepository::class)->countRecent(), | ||||
|                 'status' => $this->getHorizonStatus(), | ||||
|                 'wait' => collect(app(WaitTimeCalculator::class)->calculate())->take(1), | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the total process count across all supervisors. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     protected function totalProcessCount() | ||||
|     { | ||||
|         $supervisors = app(SupervisorRepository::class)->all(); | ||||
|  | ||||
|         return collect($supervisors)->reduce(function ($carry, $supervisor) { | ||||
|             return $carry + collect($supervisor->processes)->sum(); | ||||
|         }, 0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the number of master supervisors that are currently paused. | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     protected function totalPausedMasters() | ||||
|     { | ||||
|         if (! $masters = app(MasterSupervisorRepository::class)->all()) { | ||||
|             return 0; | ||||
|         } | ||||
|  | ||||
|         return collect($masters)->filter(function ($master) { | ||||
|             return $master->status === 'paused'; | ||||
|         })->count(); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -68,7 +68,7 @@ class TicketController extends Controller | ||||
|         $ticketService->replyByAdmin( | ||||
|             $request->input('id'), | ||||
|             $request->input('message'), | ||||
|             $request->session()->get('id') | ||||
|             $request->user['id'] | ||||
|         ); | ||||
|         return response([ | ||||
|             'data' => true | ||||
|   | ||||
| @@ -24,6 +24,7 @@ class Clash | ||||
|         header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}"); | ||||
|         header('profile-update-interval: 24'); | ||||
|         header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName)); | ||||
|         header("profile-web-page-url:" . config('v2board.app_url')); | ||||
|         $defaultConfig = base_path() . '/resources/rules/default.clash.yaml'; | ||||
|         $customConfig = base_path() . '/resources/rules/custom.clash.yaml'; | ||||
|         if (\File::exists($customConfig)) { | ||||
| @@ -121,7 +122,6 @@ class Clash | ||||
|                     $array['ws-opts']['path'] = $wsSettings['path']; | ||||
|                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) | ||||
|                     $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']]; | ||||
|                 // TODO: 2022.06.01 remove it | ||||
|                 if (isset($wsSettings['path']) && !empty($wsSettings['path'])) | ||||
|                     $array['ws-path'] = $wsSettings['path']; | ||||
|                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) | ||||
|   | ||||
| @@ -122,7 +122,6 @@ class Stash | ||||
|                     $array['ws-opts']['path'] = $wsSettings['path']; | ||||
|                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) | ||||
|                     $array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']]; | ||||
|                 // TODO: 2022.06.01 remove it | ||||
|                 if (isset($wsSettings['path']) && !empty($wsSettings['path'])) | ||||
|                     $array['ws-path'] = $wsSettings['path']; | ||||
|                 if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host'])) | ||||
| @@ -155,6 +154,11 @@ class Stash | ||||
|         return $array; | ||||
|     } | ||||
|  | ||||
|     private function isRegex($exp) | ||||
|     { | ||||
|         return @preg_match($exp, null) !== false; | ||||
|     } | ||||
|  | ||||
|     private function isMatch($exp, $str) | ||||
|     { | ||||
|         try { | ||||
|   | ||||
| @@ -21,6 +21,9 @@ class Surfboard | ||||
|         $servers = $this->servers; | ||||
|         $user = $this->user; | ||||
|  | ||||
|         $appName = config('v2board.app_name', 'V2Board'); | ||||
|         header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName).".conf"); | ||||
|  | ||||
|         $proxies = ''; | ||||
|         $proxyGroup = ''; | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,9 @@ class Surge | ||||
|         $servers = $this->servers; | ||||
|         $user = $this->user; | ||||
|  | ||||
|         $appName = config('v2board.app_name', 'V2Board'); | ||||
|         header("content-disposition:attachment;filename*=UTF-8''".rawurlencode($appName).".conf"); | ||||
|  | ||||
|         $proxies = ''; | ||||
|         $proxyGroup = ''; | ||||
|  | ||||
|   | ||||
| @@ -10,25 +10,28 @@ class TelegramController extends Controller | ||||
| { | ||||
|     protected $msg; | ||||
|     protected $commands = []; | ||||
|     protected $telegramService; | ||||
|  | ||||
|     public function __construct(Request $request) | ||||
|     { | ||||
|         if ($request->input('access_token') !== md5(config('v2board.telegram_bot_token'))) { | ||||
|             abort(401); | ||||
|         } | ||||
|  | ||||
|         $this->telegramService = new TelegramService(); | ||||
|     } | ||||
|  | ||||
|     public function webhook(Request $request) | ||||
|     { | ||||
|         $this->msg = $this->getMessage($request->input()); | ||||
|         if (!$this->msg) return; | ||||
|         $this->formatMessage($request->input()); | ||||
|         $this->formatChatJoinRequest($request->input()); | ||||
|         $this->handle(); | ||||
|     } | ||||
|  | ||||
|     public function handle() | ||||
|     { | ||||
|         if (!$this->msg) return; | ||||
|         $msg = $this->msg; | ||||
|  | ||||
|         $commandName = explode('@', $msg->command); | ||||
|  | ||||
|         // To reduce request, only commands contains @ will get the bot name | ||||
| @@ -59,34 +62,62 @@ class TelegramController extends Controller | ||||
|                 } | ||||
|             } | ||||
|         } catch (\Exception $e) { | ||||
|             $telegramService = new TelegramService(); | ||||
|             $telegramService->sendMessage($msg->chat_id, $e->getMessage()); | ||||
|             $this->telegramService->sendMessage($msg->chat_id, $e->getMessage()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getBotName() | ||||
|     { | ||||
|         $telegramService = new TelegramService(); | ||||
|         $response = $telegramService->getMe(); | ||||
|         $response = $this->telegramService->getMe(); | ||||
|         return $response->result->username; | ||||
|     } | ||||
|  | ||||
|     private function getMessage(array $data) | ||||
|     private function formatMessage(array $data) | ||||
|     { | ||||
|         if (!isset($data['message'])) return false; | ||||
|         if (!isset($data['message'])) return; | ||||
|         if (!isset($data['message']['text'])) return; | ||||
|         $obj = new \StdClass(); | ||||
|         if (!isset($data['message']['text'])) return false; | ||||
|         $text = explode(' ', $data['message']['text']); | ||||
|         $obj->command = $text[0]; | ||||
|         $obj->args = array_slice($text, 1); | ||||
|         $obj->chat_id = $data['message']['chat']['id']; | ||||
|         $obj->message_id = $data['message']['message_id']; | ||||
|         $obj->message_type = !isset($data['message']['reply_to_message']['text']) ? 'message' : 'reply_message'; | ||||
|         $obj->message_type = 'message'; | ||||
|         $obj->text = $data['message']['text']; | ||||
|         $obj->is_private = $data['message']['chat']['type'] === 'private'; | ||||
|         if ($obj->message_type === 'reply_message') { | ||||
|         if (isset($data['message']['reply_to_message']['text'])) { | ||||
|             $obj->message_type = 'reply_message'; | ||||
|             $obj->reply_text = $data['message']['reply_to_message']['text']; | ||||
|         } | ||||
|         return $obj; | ||||
|         $this->msg = $obj; | ||||
|     } | ||||
|  | ||||
|     private function formatChatJoinRequest(array $data) | ||||
|     { | ||||
|         if (!isset($data['chat_join_request'])) return; | ||||
|         if (!isset($data['chat_join_request']['from']['id'])) return; | ||||
|         if (!isset($data['chat_join_request']['chat']['id'])) return; | ||||
|         $user = \App\Models\User::where('telegram_id', $data['chat_join_request']['from']['id']) | ||||
|             ->first(); | ||||
|         if (!$user) { | ||||
|             $this->telegramService->declineChatJoinRequest( | ||||
|                 $data['chat_join_request']['chat']['id'], | ||||
|                 $data['chat_join_request']['from']['id'] | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         $userService = new \App\Services\UserService(); | ||||
|         if (!$userService->isAvailable($user)) { | ||||
|             $this->telegramService->declineChatJoinRequest( | ||||
|                 $data['chat_join_request']['chat']['id'], | ||||
|                 $data['chat_join_request']['from']['id'] | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         $userService = new \App\Services\UserService(); | ||||
|         $this->telegramService->approveChatJoinRequest( | ||||
|             $data['chat_join_request']['chat']['id'], | ||||
|             $data['chat_join_request']['from']['id'] | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -6,6 +6,7 @@ use App\Http\Controllers\Controller; | ||||
| use App\Http\Requests\Passport\AuthRegister; | ||||
| use App\Http\Requests\Passport\AuthForget; | ||||
| use App\Http\Requests\Passport\AuthLogin; | ||||
| use App\Jobs\SendEmailJob; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use App\Models\Plan; | ||||
| @@ -18,6 +19,59 @@ use ReCaptcha\ReCaptcha; | ||||
|  | ||||
| class AuthController extends Controller | ||||
| { | ||||
|     public function loginWithMailLink(Request $request) | ||||
|     { | ||||
|         if (!(int)config('v2board.login_with_mail_link_enable')) { | ||||
|             abort(404); | ||||
|         } | ||||
|         $params = $request->validate([ | ||||
|             'email' => 'required|email', | ||||
|             'redirect' => 'nullable' | ||||
|         ]); | ||||
|  | ||||
|         if (Cache::get(CacheKey::get('LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP', $params['email']))) { | ||||
|             abort(500, __('Sending frequently, please try again later')); | ||||
|         } | ||||
|  | ||||
|         $user = User::where('email', $params['email'])->first(); | ||||
|         if (!$user) { | ||||
|             return response([ | ||||
|                 'data' => true | ||||
|             ]); | ||||
|         } | ||||
|  | ||||
|         $code = Helper::guid(); | ||||
|         $key = CacheKey::get('TEMP_TOKEN', $code); | ||||
|         Cache::put($key, $user->id, 300); | ||||
|         Cache::put(CacheKey::get('LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP', $params['email']), time(), 60); | ||||
|  | ||||
|  | ||||
|         $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard'); | ||||
|         if (config('v2board.app_url')) { | ||||
|             $link = config('v2board.app_url') . $redirect; | ||||
|         } else { | ||||
|             $link = url($redirect); | ||||
|         } | ||||
|  | ||||
|         SendEmailJob::dispatch([ | ||||
|             'email' => $user->email, | ||||
|             'subject' => __('Login to :name', [ | ||||
|                 'name' => config('v2board.app_name', 'V2Board') | ||||
|             ]), | ||||
|             'template_name' => 'login', | ||||
|             'template_value' => [ | ||||
|                 'name' => config('v2board.app_name', 'V2Board'), | ||||
|                 'link' => $link, | ||||
|                 'url' => config('v2board.app_url') | ||||
|             ] | ||||
|         ]); | ||||
|  | ||||
|         return response([ | ||||
|             'data' => $link | ||||
|         ]); | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public function register(AuthRegister $request) | ||||
|     { | ||||
|         if ((int)config('v2board.register_limit_by_ip_enable', 0)) { | ||||
| @@ -113,8 +167,7 @@ class AuthController extends Controller | ||||
|             'token' => $user->token, | ||||
|             'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|         ]; | ||||
|         $request->session()->put('email', $user->email); | ||||
|         $request->session()->put('id', $user->id); | ||||
|  | ||||
|         $user->last_login_at = time(); | ||||
|         $user->save(); | ||||
|  | ||||
| @@ -156,16 +209,8 @@ class AuthController extends Controller | ||||
|             'token' => $user->token, | ||||
|             'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|         ]; | ||||
|         $request->session()->put('email', $user->email); | ||||
|         $request->session()->put('id', $user->id); | ||||
|         if ($user->is_admin) { | ||||
|             $request->session()->put('is_admin', true); | ||||
|             $data['is_admin'] = true; | ||||
|         } | ||||
|         if ($user->is_staff) { | ||||
|             $request->session()->put('is_staff', true); | ||||
|             $data['is_staff'] = true; | ||||
|         } | ||||
|  | ||||
|         if ($user->is_admin) $data['is_admin'] = true; | ||||
|         return response([ | ||||
|             'data' => $data | ||||
|         ]); | ||||
| @@ -196,14 +241,13 @@ class AuthController extends Controller | ||||
|             if ($user->banned) { | ||||
|                 abort(500, __('Your account has been suspended')); | ||||
|             } | ||||
|             $request->session()->put('email', $user->email); | ||||
|             $request->session()->put('id', $user->id); | ||||
|             if ($user->is_admin) { | ||||
|                 $request->session()->put('is_admin', true); | ||||
|             } | ||||
|             $data = [ | ||||
|                 'token' => $user->token, | ||||
|                 'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|             ]; | ||||
|             Cache::forget($key); | ||||
|             return response([ | ||||
|                 'data' => true | ||||
|                 'data' => $data | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
| @@ -225,8 +269,11 @@ class AuthController extends Controller | ||||
|  | ||||
|     public function getQuickLoginUrl(Request $request) | ||||
|     { | ||||
|         $authData = explode(':', base64_decode($request->input('auth_data'))); | ||||
|         if (!isset($authData[0])) abort(403, __('Token error')); | ||||
|         $authorization = $request->input('auth_data') ?? $request->header('authorization'); | ||||
|         if (!$authorization) abort(403, '未登录或登陆已过期'); | ||||
|  | ||||
|         $authData = explode(':', base64_decode($authorization)); | ||||
|         if (!isset($authData[0]) || !isset($authData[1])) abort(403, __('Token error')); | ||||
|         $user = User::where('email', $authData[0]) | ||||
|             ->where('password', $authData[1]) | ||||
|             ->first(); | ||||
| @@ -248,19 +295,6 @@ class AuthController extends Controller | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function check(Request $request) | ||||
|     { | ||||
|         $data = [ | ||||
|             'is_login' => $request->session()->get('id') ? true : false | ||||
|         ]; | ||||
|         if ($request->session()->get('is_admin')) { | ||||
|             $data['is_admin'] = true; | ||||
|         } | ||||
|         return response([ | ||||
|             'data' => $data | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function forget(AuthForget $request) | ||||
|     { | ||||
|         if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) { | ||||
| @@ -281,5 +315,4 @@ class AuthController extends Controller | ||||
|             'data' => true | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -17,24 +17,6 @@ use ReCaptcha\ReCaptcha; | ||||
|  | ||||
| class CommController extends Controller | ||||
| { | ||||
|     // TODO: remove on 1.5.5 | ||||
|     public function config() | ||||
|     { | ||||
|         return response([ | ||||
|             'data' => [ | ||||
|                 'isEmailVerify' => (int)config('v2board.email_verify', 0) ? 1 : 0, | ||||
|                 'isInviteForce' => (int)config('v2board.invite_force', 0) ? 1 : 0, | ||||
|                 'emailWhitelistSuffix' => (int)config('v2board.email_whitelist_enable', 0) | ||||
|                     ? $this->getEmailSuffix() | ||||
|                     : 0, | ||||
|                 'isRecaptcha' => (int)config('v2board.recaptcha_enable', 0) ? 1 : 0, | ||||
|                 'recaptchaSiteKey' => config('v2board.recaptcha_site_key'), | ||||
|                 'appDescription' => config('v2board.app_description'), | ||||
|                 'appUrl' => config('v2board.app_url') | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     private function isEmailVerify() | ||||
|     { | ||||
|         return response([ | ||||
|   | ||||
| @@ -82,9 +82,9 @@ class DeepbworkController extends Controller | ||||
|         Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600); | ||||
|         $userService = new UserService(); | ||||
|         foreach ($data as $item) { | ||||
|             $u = $item['u'] * $server->rate; | ||||
|             $d = $item['d'] * $server->rate; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess'); | ||||
|             $u = $item['u']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server->toArray(), 'vmess'); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|   | ||||
| @@ -74,9 +74,9 @@ class ShadowsocksTidalabController extends Controller | ||||
|         Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600); | ||||
|         $userService = new UserService(); | ||||
|         foreach ($data as $item) { | ||||
|             $u = $item['u'] * $server->rate; | ||||
|             $d = $item['d'] * $server->rate; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server, 'shadowsocks'); | ||||
|             $u = $item['u']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server->toArray(), 'shadowsocks'); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|   | ||||
| @@ -79,9 +79,9 @@ class TrojanTidalabController extends Controller | ||||
|         Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600); | ||||
|         $userService = new UserService(); | ||||
|         foreach ($data as $item) { | ||||
|             $u = $item['u'] * $server->rate; | ||||
|             $d = $item['d'] * $server->rate; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server, 'trojan'); | ||||
|             $u = $item['u']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server->toArray(), 'trojan'); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|   | ||||
| @@ -86,9 +86,9 @@ class VProxyController extends Controller | ||||
|         Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_PUSH_AT', $this->nodeInfo->id), time(), 3600); | ||||
|         $userService = new UserService(); | ||||
|         foreach ($data as $item) { | ||||
|             $u = $item['u'] * $this->nodeInfo->rate; | ||||
|             $d = $item['d'] * $this->nodeInfo->rate; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $this->nodeInfo, $this->nodeType); | ||||
|             $u = $item['u']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $this->nodeInfo->toArray(), $this->nodeType); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|   | ||||
| @@ -57,7 +57,7 @@ class TicketController extends Controller | ||||
|         $ticketService->replyByAdmin( | ||||
|             $request->input('id'), | ||||
|             $request->input('message'), | ||||
|             $request->session()->get('id') | ||||
|             $request->user['id'] | ||||
|         ); | ||||
|         return response([ | ||||
|             'data' => true | ||||
|   | ||||
| @@ -19,7 +19,11 @@ class CommController extends Controller | ||||
|                 'withdraw_methods' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT), | ||||
|                 'withdraw_close' => (int)config('v2board.withdraw_close_enable', 0), | ||||
|                 'currency' => config('v2board.currency', 'CNY'), | ||||
|                 'currency_symbol' => config('v2board.currency_symbol', '¥') | ||||
|                 'currency_symbol' => config('v2board.currency_symbol', '¥'), | ||||
|                 'commission_distribution_enable' => (int)config('v2board.commission_distribution_enable', 0), | ||||
|                 'commission_distribution_l1' => config('v2board.commission_distribution_l1'), | ||||
|                 'commission_distribution_l2' => config('v2board.commission_distribution_l2'), | ||||
|                 'commission_distribution_l3' => config('v2board.commission_distribution_l3') | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
|   | ||||
| @@ -16,7 +16,7 @@ class CouponController extends Controller | ||||
|         } | ||||
|         $couponService = new CouponService($request->input('code')); | ||||
|         $couponService->setPlanId($request->input('plan_id')); | ||||
|         $couponService->setUserId($request->session()->get('id')); | ||||
|         $couponService->setUserId($request->user['id']); | ||||
|         $couponService->check(); | ||||
|         return response([ | ||||
|             'data' => $couponService->getCoupon() | ||||
|   | ||||
| @@ -14,11 +14,11 @@ class InviteController extends Controller | ||||
| { | ||||
|     public function save(Request $request) | ||||
|     { | ||||
|         if (InviteCode::where('user_id', $request->session()->get('id'))->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) { | ||||
|         if (InviteCode::where('user_id', $request->user['id'])->where('status', 0)->count() >= config('v2board.invite_gen_limit', 5)) { | ||||
|             abort(500, __('The maximum number of creations has been reached')); | ||||
|         } | ||||
|         $inviteCode = new InviteCode(); | ||||
|         $inviteCode->user_id = $request->session()->get('id'); | ||||
|         $inviteCode->user_id = $request->user['id']; | ||||
|         $inviteCode->code = Helper::randomChar(8); | ||||
|         return response([ | ||||
|             'data' => $inviteCode->save() | ||||
| @@ -27,42 +27,49 @@ class InviteController extends Controller | ||||
|  | ||||
|     public function details(Request $request) | ||||
|     { | ||||
|         $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']) | ||||
|             ->where('get_amount', '>', 0) | ||||
|             ->select([ | ||||
|                 'id', | ||||
|                 'trade_no', | ||||
|                 'order_amount', | ||||
|                 'get_amount', | ||||
|                 'created_at' | ||||
|             ]) | ||||
|             ->orderBy('created_at', 'DESC'); | ||||
|         $total = $builder->count(); | ||||
|         $details = $builder->forPage($current, $pageSize) | ||||
|             ->get(); | ||||
|         return response([ | ||||
|             'data' => CommissionLog::where('invite_user_id', $request->session()->get('id')) | ||||
|                 ->where('get_amount', '>', 0) | ||||
|                 ->select([ | ||||
|                     'id', | ||||
|                     'trade_no', | ||||
|                     'order_amount', | ||||
|                     'get_amount', | ||||
|                     'created_at' | ||||
|                 ]) | ||||
|                 ->get() | ||||
|             'data' => $details, | ||||
|             'total' => $total | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $codes = InviteCode::where('user_id', $request->session()->get('id')) | ||||
|         $codes = InviteCode::where('user_id', $request->user['id']) | ||||
|             ->where('status', 0) | ||||
|             ->get(); | ||||
|         $commission_rate = config('v2board.invite_commission', 10); | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if ($user->commission_rate) { | ||||
|             $commission_rate = $user->commission_rate; | ||||
|         } | ||||
|         $stat = [ | ||||
|             //已注册用户数 | ||||
|             (int)User::where('invite_user_id', $request->session()->get('id'))->count(), | ||||
|             (int)User::where('invite_user_id', $request->user['id'])->count(), | ||||
|             //有效的佣金 | ||||
|             (int)Order::where('status', 3) | ||||
|                 ->where('commission_status', 2) | ||||
|                 ->where('invite_user_id', $request->session()->get('id')) | ||||
|                 ->where('invite_user_id', $request->user['id']) | ||||
|                 ->sum('commission_balance'), | ||||
|             //确认中的佣金 | ||||
|             (int)Order::where('status', 3) | ||||
|                 ->where('commission_status', 0) | ||||
|                 ->where('invite_user_id', $request->session()->get('id')) | ||||
|                 ->where('invite_user_id', $request->user['id']) | ||||
|                 ->sum('commission_balance'), | ||||
|             //佣金比例 | ||||
|             (int)$commission_rate, | ||||
|   | ||||
| @@ -19,14 +19,9 @@ class KnowledgeController extends Controller | ||||
|                 ->first() | ||||
|                 ->toArray(); | ||||
|             if (!$knowledge) abort(500, __('Article does not exist')); | ||||
|             $user = User::find($request->session()->get('id')); | ||||
|             $user = User::find($request->user['id']); | ||||
|             $userService = new UserService(); | ||||
|             if ($userService->isAvailable($user)) { | ||||
|                 $appleId = config('v2board.apple_id'); | ||||
|                 $appleIdPassword = config('v2board.apple_id_password'); | ||||
|             } else { | ||||
|                 $appleId = __('No active subscription. Unable to use our provided Apple ID'); | ||||
|                 $appleIdPassword = __('No active subscription. Unable to use our provided Apple ID'); | ||||
|             if (!$userService->isAvailable($user)) { | ||||
|                 $this->formatAccessData($knowledge['body']); | ||||
|             } | ||||
|             $subscribeUrl = Helper::getSubscribeUrl("/api/v1/client/subscribe?token={$user['token']}"); | ||||
| @@ -46,11 +41,19 @@ class KnowledgeController extends Controller | ||||
|                 'data' => $knowledge | ||||
|             ]); | ||||
|         } | ||||
|         $knowledges = Knowledge::select(['id', 'category', 'title', 'updated_at']) | ||||
|         $builder = Knowledge::select(['id', 'category', 'title', 'updated_at']) | ||||
|             ->where('language', $request->input('language')) | ||||
|             ->where('show', 1) | ||||
|             ->orderBy('sort', 'ASC') | ||||
|             ->get() | ||||
|             ->orderBy('sort', 'ASC'); | ||||
|         $keyword = $request->input('keyword'); | ||||
|         if ($keyword) { | ||||
|             $builder = $builder->where(function ($query) use ($keyword) { | ||||
|                 $query->where('title', 'LIKE', "%{$keyword}%") | ||||
|                     ->orWhere('body', 'LIKE', "%{$keyword}%"); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         $knowledges = $builder->get() | ||||
|             ->groupBy('category'); | ||||
|         return response([ | ||||
|             'data' => $knowledges | ||||
|   | ||||
| @@ -9,6 +9,7 @@ use App\Models\Payment; | ||||
| use App\Services\CouponService; | ||||
| use App\Services\OrderService; | ||||
| use App\Services\PaymentService; | ||||
| use App\Services\PlanService; | ||||
| use App\Services\UserService; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| @@ -28,7 +29,7 @@ class OrderController extends Controller | ||||
| { | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $model = Order::where('user_id', $request->session()->get('id')) | ||||
|         $model = Order::where('user_id', $request->user['id']) | ||||
|             ->orderBy('created_at', 'DESC'); | ||||
|         if ($request->input('status') !== null) { | ||||
|             $model->where('status', $request->input('status')); | ||||
| @@ -49,7 +50,7 @@ class OrderController extends Controller | ||||
|  | ||||
|     public function detail(Request $request) | ||||
|     { | ||||
|         $order = Order::where('user_id', $request->session()->get('id')) | ||||
|         $order = Order::where('user_id', $request->user['id']) | ||||
|             ->where('trade_no', $request->input('trade_no')) | ||||
|             ->first(); | ||||
|         if (!$order) { | ||||
| @@ -71,28 +72,30 @@ class OrderController extends Controller | ||||
|     public function save(OrderSave $request) | ||||
|     { | ||||
|         $userService = new UserService(); | ||||
|         if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) { | ||||
|         if ($userService->isNotCompleteOrderByUserId($request->user['id'])) { | ||||
|             abort(500, __('You have an unpaid or pending order, please try again later or cancel it')); | ||||
|         } | ||||
|  | ||||
|         $plan = Plan::find($request->input('plan_id')); | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $planService = new PlanService($request->input('plan_id')); | ||||
|  | ||||
|         $plan = $planService->plan; | ||||
|         $user = User::find($request->user['id']); | ||||
|  | ||||
|         if (!$plan) { | ||||
|             abort(500, __('Subscription plan does not exist')); | ||||
|         } | ||||
|  | ||||
|         if (!$planService->haveCapacity() && $request->input('period') !== 'reset_price') { | ||||
|             abort(500, __('Current product is sold out')); | ||||
|         } | ||||
|  | ||||
|         if ($plan[$request->input('period')] === NULL) { | ||||
|             abort(500, __('This payment period cannot be purchased, please choose another period')); | ||||
|         } | ||||
|  | ||||
|         if ($request->input('period') === 'reset_price') { | ||||
|             if (!$user->plan_id) { | ||||
|             if (!$userService->isAvailable($user) || $plan->id !== $user->plan_id) { | ||||
|                 abort(500, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package')); | ||||
|             } else { | ||||
|                 if ($user->plan_id !== $plan->id) { | ||||
|                     abort(500, __('This subscription reset package does not apply to your subscription')); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -114,7 +117,7 @@ class OrderController extends Controller | ||||
|         DB::beginTransaction(); | ||||
|         $order = new Order(); | ||||
|         $orderService = new OrderService($order); | ||||
|         $order->user_id = $request->session()->get('id'); | ||||
|         $order->user_id = $request->user['id']; | ||||
|         $order->plan_id = $plan->id; | ||||
|         $order->period = $request->input('period'); | ||||
|         $order->trade_no = Helper::generateOrderNo(); | ||||
| @@ -170,7 +173,7 @@ class OrderController extends Controller | ||||
|         $tradeNo = $request->input('trade_no'); | ||||
|         $method = $request->input('method'); | ||||
|         $order = Order::where('trade_no', $tradeNo) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->where('status', 0) | ||||
|             ->first(); | ||||
|         if (!$order) { | ||||
| @@ -209,7 +212,7 @@ class OrderController extends Controller | ||||
|     { | ||||
|         $tradeNo = $request->input('trade_no'); | ||||
|         $order = Order::where('trade_no', $tradeNo) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->first(); | ||||
|         if (!$order) { | ||||
|             abort(500, __('Order does not exist')); | ||||
| @@ -229,7 +232,9 @@ class OrderController extends Controller | ||||
|             'handling_fee_fixed', | ||||
|             'handling_fee_percent' | ||||
|         ]) | ||||
|             ->where('enable', 1)->get(); | ||||
|             ->where('enable', 1) | ||||
|             ->orderBy('sort', 'ASC') | ||||
|             ->get(); | ||||
|  | ||||
|         return response([ | ||||
|             'data' => $methods | ||||
| @@ -242,7 +247,7 @@ class OrderController extends Controller | ||||
|             abort(500, __('Invalid parameter')); | ||||
|         } | ||||
|         $order = Order::where('trade_no', $request->input('trade_no')) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->first(); | ||||
|         if (!$order) { | ||||
|             abort(500, __('Order does not exist')); | ||||
|   | ||||
| @@ -4,14 +4,16 @@ namespace App\Http\Controllers\User; | ||||
|  | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Models\User; | ||||
| use App\Services\PlanService; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Models\Plan; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class PlanController extends Controller | ||||
| { | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if ($request->input('id')) { | ||||
|             $plan = Plan::where('id', $request->input('id'))->first(); | ||||
|             if (!$plan) { | ||||
| @@ -24,11 +26,18 @@ class PlanController extends Controller | ||||
|                 'data' => $plan | ||||
|             ]); | ||||
|         } | ||||
|         $plan = Plan::where('show', 1) | ||||
|  | ||||
|         $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 response([ | ||||
|             'data' => $plan | ||||
|             'data' => $plans | ||||
|         ]); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class ServerController extends Controller | ||||
| { | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         $servers = []; | ||||
|         $userService = new UserService(); | ||||
|         if ($userService->isAvailable($user)) { | ||||
|   | ||||
| @@ -18,7 +18,7 @@ class StatController extends Controller | ||||
|             'user_id', | ||||
|             'server_rate' | ||||
|         ]) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->where('record_at', '>=', strtotime(date('Y-m-1'))) | ||||
|             ->orderBy('record_at', 'DESC'); | ||||
|         return response([ | ||||
|   | ||||
| @@ -22,6 +22,6 @@ class TelegramController extends Controller | ||||
|  | ||||
|     public function unbind(Request $request) | ||||
|     { | ||||
|         $user = User::where('user_id', $request->session()->get('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->session()->get('id')) | ||||
|                 ->where('user_id', $request->user['id']) | ||||
|                 ->first(); | ||||
|             if (!$ticket) { | ||||
|                 abort(500, __('Ticket does not exist')); | ||||
| @@ -38,7 +38,7 @@ class TicketController extends Controller | ||||
|                 'data' => $ticket | ||||
|             ]); | ||||
|         } | ||||
|         $ticket = Ticket::where('user_id', $request->session()->get('id')) | ||||
|         $ticket = Ticket::where('user_id', $request->user['id']) | ||||
|             ->orderBy('created_at', 'DESC') | ||||
|             ->get(); | ||||
|         return response([ | ||||
| @@ -49,21 +49,21 @@ class TicketController extends Controller | ||||
|     public function save(TicketSave $request) | ||||
|     { | ||||
|         DB::beginTransaction(); | ||||
|         if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->lockForUpdate()->count()) { | ||||
|         if ((int)Ticket::where('status', 0)->where('user_id', $request->user['id'])->lockForUpdate()->count()) { | ||||
|             abort(500, __('There are other unresolved tickets')); | ||||
|         } | ||||
|         $ticket = Ticket::create(array_merge($request->only([ | ||||
|             'subject', | ||||
|             'level' | ||||
|         ]), [ | ||||
|             'user_id' => $request->session()->get('id') | ||||
|             'user_id' => $request->user['id'] | ||||
|         ])); | ||||
|         if (!$ticket) { | ||||
|             DB::rollback(); | ||||
|             abort(500, __('Failed to open ticket')); | ||||
|         } | ||||
|         $ticketMessage = TicketMessage::create([ | ||||
|             'user_id' => $request->session()->get('id'), | ||||
|             'user_id' => $request->user['id'], | ||||
|             'ticket_id' => $ticket->id, | ||||
|             'message' => $request->input('message') | ||||
|         ]); | ||||
| @@ -87,7 +87,7 @@ class TicketController extends Controller | ||||
|             abort(500, __('Message cannot be empty')); | ||||
|         } | ||||
|         $ticket = Ticket::where('id', $request->input('id')) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->first(); | ||||
|         if (!$ticket) { | ||||
|             abort(500, __('Ticket does not exist')); | ||||
| @@ -95,14 +95,14 @@ class TicketController extends Controller | ||||
|         if ($ticket->status) { | ||||
|             abort(500, __('The ticket is closed and cannot be replied')); | ||||
|         } | ||||
|         if ($request->session()->get('id') == $this->getLastMessage($ticket->id)->user_id) { | ||||
|         if ($request->user['id'] == $this->getLastMessage($ticket->id)->user_id) { | ||||
|             abort(500, __('Please wait for the technical enginneer to reply')); | ||||
|         } | ||||
|         $ticketService = new TicketService(); | ||||
|         if (!$ticketService->reply( | ||||
|             $ticket, | ||||
|             $request->input('message'), | ||||
|             $request->session()->get('id') | ||||
|             $request->user['id'] | ||||
|         )) { | ||||
|             abort(500, __('Ticket reply failed')); | ||||
|         } | ||||
| @@ -119,7 +119,7 @@ class TicketController extends Controller | ||||
|             abort(500, __('Invalid parameter')); | ||||
|         } | ||||
|         $ticket = Ticket::where('id', $request->input('id')) | ||||
|             ->where('user_id', $request->session()->get('id')) | ||||
|             ->where('user_id', $request->user['id']) | ||||
|             ->first(); | ||||
|         if (!$ticket) { | ||||
|             abort(500, __('Ticket does not exist')); | ||||
| @@ -154,7 +154,7 @@ class TicketController extends Controller | ||||
|         )) { | ||||
|             abort(500, __('Unsupported withdrawal method')); | ||||
|         } | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         $limit = config('v2board.commission_withdraw_limit', 100); | ||||
|         if ($limit > ($user->commission_balance / 100)) { | ||||
|             abort(500, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit])); | ||||
| @@ -164,7 +164,7 @@ class TicketController extends Controller | ||||
|         $ticket = Ticket::create([ | ||||
|             'subject' => $subject, | ||||
|             'level' => 2, | ||||
|             'user_id' => $request->session()->get('id') | ||||
|             'user_id' => $request->user['id'] | ||||
|         ]); | ||||
|         if (!$ticket) { | ||||
|             DB::rollback(); | ||||
| @@ -175,7 +175,7 @@ class TicketController extends Controller | ||||
|             __('Withdrawal account') . ":" . $request->input('withdraw_account') | ||||
|         ); | ||||
|         $ticketMessage = TicketMessage::create([ | ||||
|             'user_id' => $request->session()->get('id'), | ||||
|             'user_id' => $request->user['id'], | ||||
|             'ticket_id' => $ticket->id, | ||||
|             'message' => $message | ||||
|         ]); | ||||
|   | ||||
| @@ -18,17 +18,22 @@ use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class UserController extends Controller | ||||
| { | ||||
|     public function logout(Request $request) | ||||
|     public function checkLogin(Request $request) | ||||
|     { | ||||
|         $request->session()->flush(); | ||||
|         $data = [ | ||||
|             'is_login' => $request->user['id'] ? true : false | ||||
|         ]; | ||||
|         if ($request->user['is_admin']) { | ||||
|             $data['is_admin'] = true; | ||||
|         } | ||||
|         return response([ | ||||
|             'data' => true | ||||
|             'data' => $data | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function changePassword(UserChangePassword $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if (!$user) { | ||||
|             abort(500, __('The user does not exist')); | ||||
|         } | ||||
| @@ -46,7 +51,6 @@ class UserController extends Controller | ||||
|         if (!$user->save()) { | ||||
|             abort(500, __('Save failed')); | ||||
|         } | ||||
|         $request->session()->flush(); | ||||
|         return response([ | ||||
|             'data' => true | ||||
|         ]); | ||||
| @@ -54,7 +58,7 @@ class UserController extends Controller | ||||
|  | ||||
|     public function info(Request $request) | ||||
|     { | ||||
|         $user = User::where('id', $request->session()->get('id')) | ||||
|         $user = User::where('id', $request->user['id']) | ||||
|             ->select([ | ||||
|                 'email', | ||||
|                 'transfer_enable', | ||||
| @@ -86,12 +90,12 @@ class UserController extends Controller | ||||
|     { | ||||
|         $stat = [ | ||||
|             Order::where('status', 0) | ||||
|                 ->where('user_id', $request->session()->get('id')) | ||||
|                 ->where('user_id', $request->user['id']) | ||||
|                 ->count(), | ||||
|             Ticket::where('status', 0) | ||||
|                 ->where('user_id', $request->session()->get('id')) | ||||
|                 ->where('user_id', $request->user['id']) | ||||
|                 ->count(), | ||||
|             User::where('invite_user_id', $request->session()->get('id')) | ||||
|             User::where('invite_user_id', $request->user['id']) | ||||
|                 ->count() | ||||
|         ]; | ||||
|         return response([ | ||||
| @@ -101,7 +105,7 @@ class UserController extends Controller | ||||
|  | ||||
|     public function getSubscribe(Request $request) | ||||
|     { | ||||
|         $user = User::where('id', $request->session()->get('id')) | ||||
|         $user = User::where('id', $request->user['id']) | ||||
|             ->select([ | ||||
|                 'plan_id', | ||||
|                 'token', | ||||
| @@ -131,7 +135,7 @@ class UserController extends Controller | ||||
|  | ||||
|     public function resetSecurity(Request $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if (!$user) { | ||||
|             abort(500, __('The user does not exist')); | ||||
|         } | ||||
| @@ -152,7 +156,7 @@ class UserController extends Controller | ||||
|             'remind_traffic' | ||||
|         ]); | ||||
|  | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if (!$user) { | ||||
|             abort(500, __('The user does not exist')); | ||||
|         } | ||||
| @@ -169,7 +173,7 @@ class UserController extends Controller | ||||
|  | ||||
|     public function transfer(UserTransfer $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if (!$user) { | ||||
|             abort(500, __('The user does not exist')); | ||||
|         } | ||||
| @@ -188,7 +192,7 @@ class UserController extends Controller | ||||
|  | ||||
|     public function getQuickLoginUrl(Request $request) | ||||
|     { | ||||
|         $user = User::find($request->session()->get('id')); | ||||
|         $user = User::find($request->user['id']); | ||||
|         if (!$user) { | ||||
|             abort(500, __('The user does not exist')); | ||||
|         } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace App\Http; | ||||
|  | ||||
| use Fruitcake\Cors\HandleCors; | ||||
| use Illuminate\Foundation\Http\Kernel as HttpKernel; | ||||
|  | ||||
| class Kernel extends HttpKernel | ||||
| @@ -14,6 +15,7 @@ class Kernel extends HttpKernel | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $middleware = [ | ||||
|         \App\Http\Middleware\CORS::class, | ||||
|         \App\Http\Middleware\TrustProxies::class, | ||||
|         \App\Http\Middleware\CheckForMaintenanceMode::class, | ||||
|         \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, | ||||
| @@ -28,22 +30,20 @@ class Kernel extends HttpKernel | ||||
|      */ | ||||
|     protected $middlewareGroups = [ | ||||
|         'web' => [ | ||||
|             \App\Http\Middleware\EncryptCookies::class, | ||||
|             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, | ||||
|             \Illuminate\Session\Middleware\StartSession::class, | ||||
| //            \App\Http\Middleware\EncryptCookies::class, | ||||
| //            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, | ||||
| //            \Illuminate\Session\Middleware\StartSession::class, | ||||
|             // \Illuminate\Session\Middleware\AuthenticateSession::class, | ||||
|             \Illuminate\View\Middleware\ShareErrorsFromSession::class, | ||||
|             \App\Http\Middleware\VerifyCsrfToken::class, | ||||
|             \Illuminate\Routing\Middleware\SubstituteBindings::class, | ||||
|             \App\Http\Middleware\CORS::class, | ||||
| //            \Illuminate\View\Middleware\ShareErrorsFromSession::class, | ||||
| //            \App\Http\Middleware\VerifyCsrfToken::class, | ||||
| //            \Illuminate\Routing\Middleware\SubstituteBindings::class, | ||||
|         ], | ||||
|  | ||||
|         'api' => [ | ||||
|             \App\Http\Middleware\EncryptCookies::class, | ||||
|             \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, | ||||
|             \Illuminate\Session\Middleware\StartSession::class, | ||||
| //            \App\Http\Middleware\EncryptCookies::class, | ||||
| //            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, | ||||
| //            \Illuminate\Session\Middleware\StartSession::class, | ||||
|             \App\Http\Middleware\ForceJson::class, | ||||
|             \App\Http\Middleware\CORS::class, | ||||
|             \App\Http\Middleware\Language::class, | ||||
|             'bindings', | ||||
|         ], | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class Admin | ||||
| { | ||||
| @@ -15,9 +16,28 @@ class Admin | ||||
|      */ | ||||
|     public function handle($request, Closure $next) | ||||
|     { | ||||
|         if (!$request->session()->get('is_admin')) { | ||||
|             abort(403, '权限不足'); | ||||
|         $authorization = $request->input('auth_data') ?? $request->header('authorization'); | ||||
|         if (!$authorization) abort(403, '未登录或登陆已过期'); | ||||
|  | ||||
|         $authData = explode(':', base64_decode($authorization)); | ||||
|         if (!Cache::has($authorization)) { | ||||
|             if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入'); | ||||
|             $user = \App\Models\User::where('password', $authData[1]) | ||||
|                 ->where('email', $authData[0]) | ||||
|                 ->select([ | ||||
|                     'id', | ||||
|                     'email', | ||||
|                     'is_admin', | ||||
|                     'is_staff' | ||||
|                 ]) | ||||
|                 ->first(); | ||||
|             if (!$user) abort(403, '鉴权失败,请重新登入'); | ||||
|             if (!$user->is_admin) abort(403, '鉴权失败,请重新登入'); | ||||
|             Cache::put($authorization, $user->toArray(), 3600); | ||||
|         } | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -17,8 +17,8 @@ class CORS | ||||
|         } | ||||
|         $response = $next($request); | ||||
|         $response->header('Access-Control-Allow-Origin', trim($origin, '/')); | ||||
|         $response->header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'); | ||||
|         $response->header('Access-Control-Allow-Headers', 'Content-Type,X-Requested-With'); | ||||
|         $response->header('Access-Control-Allow-Methods', 'GET,POST,OPTIONS,HEAD'); | ||||
|         $response->header('Access-Control-Allow-Headers', 'Origin,Content-Type,Accept,Authorization,X-Request-With'); | ||||
|         $response->header('Access-Control-Allow-Credentials', 'true'); | ||||
|         $response->header('Access-Control-Max-Age', 10080); | ||||
|  | ||||
|   | ||||
| @@ -26,7 +26,9 @@ class Client | ||||
|         if (!$user) { | ||||
|             abort(403, 'token is error'); | ||||
|         } | ||||
|         $request->user = $user; | ||||
|         $request->merge([ | ||||
|             'user' => $user | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class Staff | ||||
| { | ||||
| @@ -15,9 +16,28 @@ class Staff | ||||
|      */ | ||||
|     public function handle($request, Closure $next) | ||||
|     { | ||||
|         if (!$request->session()->get('is_staff')) { | ||||
|             abort(403, '权限不足'); | ||||
|         $authorization = $request->input('auth_data') ?? $request->header('authorization'); | ||||
|         if (!$authorization) abort(403, '未登录或登陆已过期'); | ||||
|  | ||||
|         $authData = explode(':', base64_decode($authorization)); | ||||
|         if (!Cache::has($authorization)) { | ||||
|             if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入'); | ||||
|             $user = \App\Models\User::where('password', $authData[1]) | ||||
|                 ->where('email', $authData[0]) | ||||
|                 ->select([ | ||||
|                     'id', | ||||
|                     'email', | ||||
|                     'is_admin', | ||||
|                     'is_staff' | ||||
|                 ]) | ||||
|                 ->first(); | ||||
|             if (!$user) abort(403, '鉴权失败,请重新登入'); | ||||
|             if (!$user->is_staff) abort(403, '鉴权失败,请重新登入'); | ||||
|             Cache::put($authorization, $user->toArray(), 3600); | ||||
|         } | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class User | ||||
| { | ||||
| @@ -16,19 +17,26 @@ class User | ||||
|     public function handle($request, Closure $next) | ||||
|     { | ||||
|         $authorization = $request->input('auth_data') ?? $request->header('authorization'); | ||||
|         if ($authorization) { | ||||
|             $authData = explode(':', base64_decode($authorization)); | ||||
|         if (!$authorization) abort(403, '未登录或登陆已过期'); | ||||
|  | ||||
|         $authData = explode(':', base64_decode($authorization)); | ||||
|         if (!Cache::has($authorization)) { | ||||
|             if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入'); | ||||
|             $user = \App\Models\User::where('password', $authData[1]) | ||||
|                 ->where('email', $authData[0]) | ||||
|                 ->select([ | ||||
|                     'id', | ||||
|                     'email', | ||||
|                     'is_admin', | ||||
|                     'is_staff' | ||||
|                 ]) | ||||
|                 ->first(); | ||||
|             if (!$user) abort(403, '鉴权失败,请重新登入'); | ||||
|             $request->session()->put('email', $user->email); | ||||
|             $request->session()->put('id', $user->id); | ||||
|         } | ||||
|         if (!$request->session()->get('id')) { | ||||
|             abort(403, '未登录或登陆已过期'); | ||||
|             Cache::put($authorization, $user->toArray(), 3600); | ||||
|         } | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -26,7 +26,8 @@ class PlanSave extends FormRequest | ||||
|             'three_year_price' => 'nullable|integer', | ||||
|             'onetime_price' => 'nullable|integer', | ||||
|             'reset_price' => 'nullable|integer', | ||||
|             'reset_traffic_method' => 'nullable|integer|in:0,1,2,3,4' | ||||
|             'reset_traffic_method' => 'nullable|integer|in:0,1,2,3,4', | ||||
|             'capacity_limit' => 'nullable|integer' | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @@ -47,7 +48,8 @@ class PlanSave extends FormRequest | ||||
|             'onetime_price.integer' => '一次性金额有误', | ||||
|             'reset_price.integer' => '流量重置包金额有误', | ||||
|             'reset_traffic_method.integer' => '流量重置方式格式有误', | ||||
|             'reset_traffic_method.in' => '流量重置方式格式有误' | ||||
|             'reset_traffic_method.in' => '流量重置方式格式有误', | ||||
|             'capacity_limit.integer' => '容纳用户量限制格式有误' | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -50,7 +50,6 @@ class AdminRoute | ||||
|                 $router->post('update', 'Admin\\Server\\V2rayController@update'); | ||||
|                 $router->post('copy', 'Admin\\Server\\V2rayController@copy'); | ||||
|                 $router->post('sort', 'Admin\\Server\\V2rayController@sort'); | ||||
|                 $router->post('viewConfig', 'Admin\\Server\\V2rayController@viewConfig'); | ||||
|             }); | ||||
|             $router->group([ | ||||
|                 'prefix' => 'server/shadowsocks' | ||||
| @@ -83,6 +82,7 @@ class AdminRoute | ||||
|             $router->get ('/stat/getOverride', 'Admin\\StatController@getOverride'); | ||||
|             $router->get ('/stat/getServerLastRank', 'Admin\\StatController@getServerLastRank'); | ||||
|             $router->get ('/stat/getOrder', 'Admin\\StatController@getOrder'); | ||||
|             $router->get ('/stat/getStatUser', 'Admin\\StatController@getStatUser'); | ||||
|             // Notice | ||||
|             $router->get ('/notice/fetch', 'Admin\\NoticeController@fetch'); | ||||
|             $router->post('/notice/save', 'Admin\\NoticeController@save'); | ||||
| @@ -112,8 +112,12 @@ class AdminRoute | ||||
|             $router->post('/payment/save', 'Admin\\PaymentController@save'); | ||||
|             $router->post('/payment/drop', 'Admin\\PaymentController@drop'); | ||||
|             $router->post('/payment/show', 'Admin\\PaymentController@show'); | ||||
|             $router->post('/payment/sort', 'Admin\\PaymentController@sort'); | ||||
|             // System | ||||
|             $router->get ('/system/getStatus', 'Admin\\SystemController@getStatus'); | ||||
|             $router->get ('/system/getSystemStatus', 'Admin\\SystemController@getSystemStatus'); | ||||
|             $router->get ('/system/getQueueStats', 'Admin\\SystemController@getQueueStats'); | ||||
|             $router->get ('/system/getQueueWorkload', 'Admin\\SystemController@getQueueWorkload'); | ||||
|             $router->get ('/system/getQueueMasters', '\\Laravel\\Horizon\\Http\\Controllers\\MasterSupervisorController@index'); | ||||
|             // Theme | ||||
|             $router->get ('/theme/getThemes', 'Admin\\ThemeController@getThemes'); | ||||
|             $router->post('/theme/saveThemeConfig', 'Admin\\ThemeController@saveThemeConfig'); | ||||
|   | ||||
| @@ -14,12 +14,10 @@ class PassportRoute | ||||
|             $router->post('/auth/register', 'Passport\\AuthController@register'); | ||||
|             $router->post('/auth/login', 'Passport\\AuthController@login'); | ||||
|             $router->get ('/auth/token2Login', 'Passport\\AuthController@token2Login'); | ||||
|             $router->get ('/auth/check', 'Passport\\AuthController@check'); | ||||
|             $router->post('/auth/forget', 'Passport\\AuthController@forget'); | ||||
|             $router->post('/auth/getTempToken', 'Passport\\AuthController@getTempToken'); | ||||
|             $router->post('/auth/getQuickLoginUrl', 'Passport\\AuthController@getQuickLoginUrl'); | ||||
|             $router->post('/auth/loginWithMailLink', 'Passport\\AuthController@loginWithMailLink'); | ||||
|             // Comm | ||||
|             $router->get ('/comm/config', 'Passport\\CommController@config'); | ||||
|             $router->post('/comm/sendEmailVerify', 'Passport\\CommController@sendEmailVerify'); | ||||
|             $router->post('/comm/pv', 'Passport\\CommController@pv'); | ||||
|         }); | ||||
|   | ||||
| @@ -13,21 +13,19 @@ class UserRoute | ||||
|         ], function ($router) { | ||||
|             // User | ||||
|             $router->get ('/resetSecurity', 'User\\UserController@resetSecurity'); | ||||
|             $router->get ('/logout', 'User\\UserController@logout'); | ||||
|             $router->get ('/info', 'User\\UserController@info'); | ||||
|             $router->post('/changePassword', 'User\\UserController@changePassword'); | ||||
|             $router->post('/update', 'User\\UserController@update'); | ||||
|             $router->get ('/getSubscribe', 'User\\UserController@getSubscribe'); | ||||
|             $router->get ('/getStat', 'User\\UserController@getStat'); | ||||
|             $router->get ('/checkLogin', 'User\\UserController@checkLogin'); | ||||
|             $router->post('/transfer', 'User\\UserController@transfer'); | ||||
|             $router->post('/getQuickLoginUrl', 'User\\UserController@getQuickLoginUrl'); | ||||
|             // Order | ||||
|             $router->post('/order/save', 'User\\OrderController@save'); | ||||
|             $router->post('/order/checkout', 'User\\OrderController@checkout'); | ||||
|             $router->get ('/order/check', 'User\\OrderController@check'); | ||||
|             // TODO: 1.5.6 remove | ||||
|             $router->get ('/order/details', 'User\\OrderController@detail'); | ||||
|             // TODO: 1.5.6 remove | ||||
|             $router->get ('/order/details', 'User\\OrderController@detail');                                            // TODO: 1.7.0 remove | ||||
|             $router->get ('/order/detail', 'User\\OrderController@detail'); | ||||
|             $router->get ('/order/fetch', 'User\\OrderController@fetch'); | ||||
|             $router->get ('/order/getPaymentMethod', 'User\\OrderController@getPaymentMethod'); | ||||
|   | ||||
| @@ -48,10 +48,10 @@ class StatServerJob implements ShouldQueue | ||||
|             // | ||||
|         } | ||||
|  | ||||
|         $data = StatServer::where('record_at', $recordAt) | ||||
|             ->where('server_id', $this->server->id) | ||||
|         $data = StatServer::lockForUpdate() | ||||
|             ->where('record_at', $recordAt) | ||||
|             ->where('server_id', $this->server['id']) | ||||
|             ->where('server_type', $this->protocol) | ||||
|             ->lockForUpdate() | ||||
|             ->first(); | ||||
|         if ($data) { | ||||
|             try { | ||||
| @@ -64,7 +64,7 @@ class StatServerJob implements ShouldQueue | ||||
|             } | ||||
|         } else { | ||||
|             if (!StatServer::create([ | ||||
|                 'server_id' => $this->server->id, | ||||
|                 'server_id' => $this->server['id'], | ||||
|                 'server_type' => $this->protocol, | ||||
|                 'u' => $this->u, | ||||
|                 'd' => $this->d, | ||||
|   | ||||
| @@ -28,7 +28,7 @@ class StatUserJob implements ShouldQueue | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($u, $d, $userId, $server, $protocol, $recordType = 'd') | ||||
|     public function __construct($u, $d, $userId, array $server, $protocol, $recordType = 'd') | ||||
|     { | ||||
|         $this->onQueue('stat'); | ||||
|         $this->u = $u; | ||||
| @@ -52,14 +52,14 @@ class StatUserJob implements ShouldQueue | ||||
|         } | ||||
|  | ||||
|         $data = StatUser::where('record_at', $recordAt) | ||||
|             ->where('server_rate', $this->server->rate) | ||||
|             ->where('server_rate', $this->server['rate']) | ||||
|             ->where('user_id', $this->userId) | ||||
|             ->first(); | ||||
|         if ($data) { | ||||
|             try { | ||||
|                 $data->update([ | ||||
|                     'u' => $data['u'] + $this->u, | ||||
|                     'd' => $data['d'] + $this->d | ||||
|                     'u' => $data['u'] + ($this->u * $this->server['rate']), | ||||
|                     'd' => $data['d'] + ($this->d * $this->server['rate']) | ||||
|                 ]); | ||||
|             } catch (\Exception $e) { | ||||
|                 abort(500, '用户统计数据更新失败'); | ||||
| @@ -67,7 +67,7 @@ class StatUserJob implements ShouldQueue | ||||
|         } else { | ||||
|             if (!StatUser::create([ | ||||
|                 'user_id' => $this->userId, | ||||
|                 'server_rate' => $this->server->rate, | ||||
|                 'server_rate' => $this->server['rate'], | ||||
|                 'u' => $this->u, | ||||
|                 'd' => $this->d, | ||||
|                 'record_type' => $this->recordType, | ||||
|   | ||||
| @@ -27,7 +27,7 @@ class TrafficFetchJob implements ShouldQueue | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct($u, $d, $userId, $server, $protocol) | ||||
|     public function __construct($u, $d, $userId, array $server, $protocol) | ||||
|     { | ||||
|         $this->onQueue('traffic_fetch'); | ||||
|         $this->u = $u; | ||||
| @@ -46,10 +46,10 @@ class TrafficFetchJob implements ShouldQueue | ||||
|     { | ||||
|         $user = User::lockForUpdate()->find($this->userId); | ||||
|         if (!$user) return; | ||||
|          | ||||
|  | ||||
|         $user->t = time(); | ||||
|         $user->u = $user->u + $this->u; | ||||
|         $user->d = $user->d + $this->d; | ||||
|         $user->u = $user->u + ($this->u * $this->server['rate']); | ||||
|         $user->d = $user->d + ($this->d * $this->server['rate']); | ||||
|         if (!$user->save()) throw new \Exception('流量更新失败'); | ||||
|         $mailService = new MailService(); | ||||
|         $mailService->remindTraffic($user); | ||||
|   | ||||
| @@ -91,11 +91,14 @@ class StripeAlipay { | ||||
|             case 'charge.succeeded': | ||||
|                 $object = $event->data->object; | ||||
|                 if ($object->status === 'succeeded') { | ||||
|                     if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { | ||||
|                         die('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->balance_transaction | ||||
|                         'callback_no' => $object->id | ||||
|                     ]; | ||||
|                 } | ||||
|                 break; | ||||
|   | ||||
							
								
								
									
										124
									
								
								app/Payments/StripeCheckout.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								app/Payments/StripeCheckout.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,124 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Payments; | ||||
|  | ||||
| use Stripe\Stripe; | ||||
| use Stripe\Checkout\Session; | ||||
|  | ||||
| class StripeCheckout { | ||||
|     public function __construct($config) | ||||
|     { | ||||
|         $this->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', | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function pay($order) | ||||
|     { | ||||
|         $currency = $this->config['currency']; | ||||
|         $exchange = $this->exchange('CNY', strtoupper($currency)); | ||||
|         if (!$exchange) { | ||||
|             abort(500, __('Currency conversion has timed out, please try again later')); | ||||
|         } | ||||
|  | ||||
|         $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' | ||||
|             // 'customer_email' => $user['email'] not support | ||||
|  | ||||
|         ]; | ||||
|  | ||||
|         Stripe::setApiKey($this->config['stripe_sk_live']); | ||||
|         try { | ||||
|             $session = Session::create($params); | ||||
|         } catch (\Exception $e) { | ||||
|             info($e); | ||||
|             abort(500, "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( | ||||
|                 file_get_contents('php://input'), | ||||
|                 $_SERVER['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: | ||||
|                 abort(500, 'event is not support'); | ||||
|         } | ||||
|         die('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]; | ||||
|     } | ||||
| } | ||||
| @@ -98,11 +98,14 @@ class StripeCredit { | ||||
|             case 'charge.succeeded': | ||||
|                 $object = $event->data->object; | ||||
|                 if ($object->status === 'succeeded') { | ||||
|                     if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { | ||||
|                         die('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->balance_transaction | ||||
|                         'callback_no' => $object->id | ||||
|                     ]; | ||||
|                 } | ||||
|                 break; | ||||
|   | ||||
| @@ -91,11 +91,14 @@ class StripeWepay { | ||||
|             case 'charge.succeeded': | ||||
|                 $object = $event->data->object; | ||||
|                 if ($object->status === 'succeeded') { | ||||
|                     if (!isset($object->metadata->out_trade_no) && !isset($object->source->metadata)) { | ||||
|                         die('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->balance_transaction | ||||
|                         'callback_no' => $object->id | ||||
|                     ]; | ||||
|                 } | ||||
|                 break; | ||||
|   | ||||
| @@ -15,7 +15,9 @@ class CouponService | ||||
|  | ||||
|     public function __construct($code) | ||||
|     { | ||||
|         $this->coupon = Coupon::where('code', $code)->first(); | ||||
|         $this->coupon = Coupon::where('code', $code) | ||||
|             ->lockForUpdate() | ||||
|             ->first(); | ||||
|     } | ||||
|  | ||||
|     public function use(Order $order):bool | ||||
| @@ -36,6 +38,7 @@ class CouponService | ||||
|             $order->discount_amount = $order->total_amount; | ||||
|         } | ||||
|         if ($this->coupon->limit_use !== NULL) { | ||||
|             if ($this->coupon->limit_use <= 0) return false; | ||||
|             $this->coupon->limit_use = $this->coupon->limit_use - 1; | ||||
|             if (!$this->coupon->save()) { | ||||
|                 return false; | ||||
|   | ||||
							
								
								
									
										41
									
								
								app/Services/PlanService.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								app/Services/PlanService.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Services; | ||||
|  | ||||
| use App\Models\Plan; | ||||
| use App\Models\User; | ||||
| use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class PlanService | ||||
| { | ||||
|     public $plan; | ||||
|  | ||||
|     public function __construct(int $planId) | ||||
|     { | ||||
|         $this->plan = Plan::lockForUpdate()->find($planId); | ||||
|     } | ||||
|  | ||||
|     public function haveCapacity(): bool | ||||
|     { | ||||
|         if ($this->plan->capacity_limit === NULL) return true; | ||||
|         $count = self::countActiveUsers(); | ||||
|         $count = $count[$this->plan->id]['count'] ?? 0; | ||||
|         return ($this->plan->capacity_limit - $count) > 0; | ||||
|     } | ||||
|  | ||||
|     public static function countActiveUsers() | ||||
|     { | ||||
|         return User::select( | ||||
|             DB::raw("plan_id"), | ||||
|             DB::raw("count(*) as count") | ||||
|         ) | ||||
|             ->where('plan_id', '!=', NULL) | ||||
|             ->where(function ($query) { | ||||
|                 $query->where('expired_at', '>=', time()) | ||||
|                     ->orWhere('expired_at', NULL); | ||||
|             }) | ||||
|             ->groupBy("plan_id") | ||||
|             ->get() | ||||
|             ->keyBy('plan_id'); | ||||
|     } | ||||
| } | ||||
| @@ -26,6 +26,22 @@ class TelegramService { | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function approveChatJoinRequest(int $chatId, int $userId) | ||||
|     { | ||||
|         $this->request('approveChatJoinRequest', [ | ||||
|             'chat_id' => $chatId, | ||||
|             'user_id' => $userId | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function declineChatJoinRequest(int $chatId, int $userId) | ||||
|     { | ||||
|         $this->request('declineChatJoinRequest', [ | ||||
|             'chat_id' => $chatId, | ||||
|             'user_id' => $userId | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getMe() | ||||
|     { | ||||
|         return $this->request('getMe'); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use App\Jobs\StatUserJob; | ||||
| use App\Jobs\TrafficFetchJob; | ||||
| use App\Models\InviteCode; | ||||
| use App\Models\Order; | ||||
| use App\Models\Plan; | ||||
| use App\Models\ServerV2ray; | ||||
| use App\Models\Ticket; | ||||
| use App\Models\User; | ||||
| @@ -15,48 +16,87 @@ use Illuminate\Support\Facades\DB; | ||||
|  | ||||
| class UserService | ||||
| { | ||||
|     public function getResetDay(User $user) | ||||
|     private function calcResetDayByMonthFirstDay() | ||||
|     { | ||||
|         if ($user->expired_at <= time() || $user->expired_at === NULL) return null; | ||||
|         // if reset method is not reset | ||||
|         if (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 2) return null; | ||||
|         $today = date('d'); | ||||
|         $lastDay = date('d', strtotime('last day of +0 months')); | ||||
|         return $lastDay - $today; | ||||
|     } | ||||
|  | ||||
|         if ((int)config('v2board.reset_traffic_method') === 0 || | ||||
|             (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 0)) | ||||
|         { | ||||
|             $day = date('d', $user->expired_at); | ||||
|             $today = date('d'); | ||||
|             $lastDay = date('d', strtotime('last day of +0 months')); | ||||
|     private function calcResetDayByExpireDay(int $expiredAt) | ||||
|     { | ||||
|         $day = date('d', $expiredAt); | ||||
|         $today = date('d'); | ||||
|         $lastDay = date('d', strtotime('last day of +0 months')); | ||||
|         if ((int)$day >= (int)$today && (int)$day >= (int)$lastDay) { | ||||
|             return $lastDay - $today; | ||||
|         } | ||||
|         if ((int)config('v2board.reset_traffic_method') === 1 || | ||||
|             (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 1)) | ||||
|         { | ||||
|             $day = date('d', $user->expired_at); | ||||
|             $today = date('d'); | ||||
|             $lastDay = date('d', strtotime('last day of +0 months')); | ||||
|             if ((int)$day >= (int)$today && (int)$day >= (int)$lastDay) { | ||||
|                 return $lastDay - $today; | ||||
|             } | ||||
|             if ((int)$day >= (int)$today) { | ||||
|                 return $day - $today; | ||||
|             } else { | ||||
|                 return $lastDay - $today + $day; | ||||
|             } | ||||
|         if ((int)$day >= (int)$today) { | ||||
|             return $day - $today; | ||||
|         } else { | ||||
|             return $lastDay - $today + $day; | ||||
|         } | ||||
|         if ((int)config('v2board.reset_traffic_method') === 3 || | ||||
|             (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 3)) | ||||
|         { | ||||
|             $nextYear = strtotime(date("Y-01-01", strtotime('+1 year'))); | ||||
|             return (int)(($nextYear - time()) / 86400); | ||||
|     } | ||||
|  | ||||
|     private function calcResetDayByYearFirstDay(): int | ||||
|     { | ||||
|         $nextYear = strtotime(date("Y-01-01", strtotime('+1 year'))); | ||||
|         return (int)(($nextYear - time()) / 86400); | ||||
|     } | ||||
|  | ||||
|     private function calcResetDayByYearExpiredAt(int $expiredAt): int | ||||
|     { | ||||
|         $md = date('m-d', $expiredAt); | ||||
|         $nowYear = strtotime(date("Y-{$md}")); | ||||
|         $nextYear = strtotime('+1 year', $nowYear); | ||||
|         return (int)(($nextYear - time()) / 86400); | ||||
|     } | ||||
|  | ||||
|     public function getResetDay(User $user) | ||||
|     { | ||||
|         if (!isset($user->plan)) { | ||||
|             $user->plan = Plan::find($user->plan_id); | ||||
|         } | ||||
|         if ((int)config('v2board.reset_traffic_method') === 4 || | ||||
|             (isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 4)) | ||||
|         { | ||||
|             $md = date('m-d', $user->expired_at); | ||||
|             $nowYear = strtotime(date("Y-{$md}")); | ||||
|             $nextYear = strtotime('+1 year', $nowYear); | ||||
|             return (int)(($nextYear - time()) / 86400); | ||||
|         if ($user->expired_at <= time() || $user->expired_at === NULL) return null; | ||||
|         // if reset method is not reset | ||||
|         if ($user->plan->reset_traffic_method === 2) return null; | ||||
|         switch (true) { | ||||
|             case ($user->plan->reset_traffic_method === NULL): { | ||||
|                 $resetTrafficMethod = config('v2board.reset_traffic_method', 0); | ||||
|                 switch ((int)$resetTrafficMethod) { | ||||
|                     // month first day | ||||
|                     case 0: | ||||
|                         return $this->calcResetDayByMonthFirstDay(); | ||||
|                     // expire day | ||||
|                     case 1: | ||||
|                         return $this->calcResetDayByExpireDay($user->expired_at); | ||||
|                     // no action | ||||
|                     case 2: | ||||
|                         return null; | ||||
|                     // year first day | ||||
|                     case 3: | ||||
|                         return $this->calcResetDayByYearFirstDay(); | ||||
|                     // year expire day | ||||
|                     case 4: | ||||
|                         return $this->calcResetDayByYearExpiredAt($user->expired_at); | ||||
|                 } | ||||
|                 break; | ||||
|             } | ||||
|             case ($user->plan->reset_traffic_method === 0): { | ||||
|                 return $this->calcResetDayByMonthFirstDay(); | ||||
|             } | ||||
|             case ($user->plan->reset_traffic_method === 1): { | ||||
|                 return $this->calcResetDayByExpireDay($user->expired_at); | ||||
|             } | ||||
|             case ($user->plan->reset_traffic_method === 2): { | ||||
|                 return null; | ||||
|             } | ||||
|             case ($user->plan->reset_traffic_method === 3): { | ||||
|                 return $this->calcResetDayByYearFirstDay(); | ||||
|             } | ||||
|             case ($user->plan->reset_traffic_method === 4): { | ||||
|                 return $this->calcResetDayByYearExpiredAt($user->expired_at); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| @@ -130,7 +170,7 @@ class UserService | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol) | ||||
|     public function trafficFetch(int $u, int $d, int $userId, array $server, string $protocol) | ||||
|     { | ||||
|         TrafficFetchJob::dispatch($u, $d, $userId, $server, $protocol); | ||||
|         StatServerJob::dispatch($u, $d, $server, $protocol, 'd'); | ||||
|   | ||||
| @@ -19,7 +19,8 @@ class CacheKey | ||||
|         'TEMP_TOKEN' => '临时令牌', | ||||
|         'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒', | ||||
|         'SCHEDULE_LAST_CHECK_AT' => '计划任务最后检查时间', | ||||
|         'REGISTER_IP_RATE_LIMIT' => '注册频率限制' | ||||
|         'REGISTER_IP_RATE_LIMIT' => '注册频率限制', | ||||
|         'LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP' => '最后一次发送登入链接时间' | ||||
|     ]; | ||||
|  | ||||
|     public static function get(string $key, $uniqueValue) | ||||
|   | ||||
| @@ -2,11 +2,6 @@ | ||||
|  | ||||
| namespace App\Utils; | ||||
|  | ||||
| use App\Models\ServerV2ray; | ||||
| use App\Models\ServerShadowsocks; | ||||
| use App\Models\ServerTrojan; | ||||
| use App\Models\User; | ||||
|  | ||||
| class Helper | ||||
| { | ||||
|     public static function guid($format = false) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user