mirror of
				https://github.com/v2board/v2board.git
				synced 2025-10-31 01:11:46 +08:00 
			
		
		
		
	1.7.0
1.7.0
This commit is contained in:
		| @@ -80,15 +80,14 @@ class CheckCommission extends Command | ||||
|  | ||||
|     public function payHandle($inviteUserId, Order $order) | ||||
|     { | ||||
|         $level = 3; | ||||
|         if ((int)config('v2board.commission_distribution_enable', 0)) { | ||||
|             $level = 3; | ||||
|             $commissionShareLevels = [ | ||||
|                 0 => (int)config('v2board.commission_distribution_l1'), | ||||
|                 1 => (int)config('v2board.commission_distribution_l2'), | ||||
|                 2 => (int)config('v2board.commission_distribution_l3') | ||||
|             ]; | ||||
|         } else { | ||||
|             $level = 3; | ||||
|             $commissionShareLevels = [ | ||||
|                 0 => 100 | ||||
|             ]; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace App\Console\Commands; | ||||
|  | ||||
| use App\Utils\Helper; | ||||
| use Illuminate\Console\Command; | ||||
|  | ||||
| class Test extends Command | ||||
|   | ||||
| @@ -89,16 +89,17 @@ class V2boardInstall extends Command | ||||
|             while (!$email) { | ||||
|                 $email = $this->ask('请输入管理员邮箱?'); | ||||
|             } | ||||
|             $password = ''; | ||||
|             while (!$password) { | ||||
|                 $password = $this->ask('请输入管理员密码?'); | ||||
|             } | ||||
|             $password = Helper::guid(false); | ||||
|             if (!$this->registerAdmin($email, $password)) { | ||||
|                 abort(500, '管理员账号注册失败,请重试'); | ||||
|             } | ||||
|  | ||||
|             $this->info('一切就绪'); | ||||
|             $this->info('访问 http(s)://你的站点/admin 进入管理面板'); | ||||
|             $this->info("管理员邮箱:{$email}"); | ||||
|             $this->info("管理员密码:{$password}"); | ||||
|  | ||||
|             $defaultSecurePath = crc32(config('app.key')); | ||||
|             $this->info("访问 http(s)://你的站点/{$defaultSecurePath} 进入管理面板,你可以用户中心修改你的密码。"); | ||||
|         } catch (\Exception $e) { | ||||
|             $this->error($e->getMessage()); | ||||
|         } | ||||
|   | ||||
| @@ -53,10 +53,10 @@ class V2boardStatistics extends Command | ||||
|             ->whereNotIn('status', [0, 2]); | ||||
|         $orderCount = $orderBuilder->count(); | ||||
|         $orderAmount = $orderBuilder->sum('total_amount'); | ||||
|         $commissionBuilder = Order::where('created_at', '>=', $startAt) | ||||
|         $commissionLogBuilder = CommissionLog::where('created_at', '>=', $startAt) | ||||
|             ->where('created_at', '<', $endAt); | ||||
|         $commissionCount = $commissionBuilder->count(); | ||||
|         $commissionAmount = $commissionBuilder->sum('actual_commission_balance'); | ||||
|         $commissionCount = $commissionLogBuilder->count(); | ||||
|         $commissionAmount = $commissionLogBuilder->sum('get_amount'); | ||||
|         $data = [ | ||||
|             'order_count' => $orderCount, | ||||
|             'order_amount' => $orderAmount, | ||||
|   | ||||
| @@ -107,7 +107,8 @@ class ConfigController extends Controller | ||||
|                 'currency_symbol' => config('v2board.currency_symbol', '¥'), | ||||
|                 'register_limit_by_ip_enable' => (int)config('v2board.register_limit_by_ip_enable', 0), | ||||
|                 'register_limit_count' => config('v2board.register_limit_count', 3), | ||||
|                 'register_limit_expire' => config('v2board.register_limit_expire', 60) | ||||
|                 'register_limit_expire' => config('v2board.register_limit_expire', 60), | ||||
|                 'secure_path' => config('v2board.secure_path', config('v2board.frontend_admin_path', crc32(config('app.key')))) | ||||
|             ], | ||||
|             'subscribe' => [ | ||||
|                 'plan_change_enable' => (int)config('v2board.plan_change_enable', 1), | ||||
| @@ -124,14 +125,11 @@ class ConfigController extends Controller | ||||
|                 'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'), | ||||
|                 'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'), | ||||
|                 'frontend_background_url' => config('v2board.frontend_background_url'), | ||||
|                 'frontend_admin_path' => config('v2board.frontend_admin_path', 'admin') | ||||
|             ], | ||||
|             'server' => [ | ||||
|                 'server_token' => config('v2board.server_token'), | ||||
|                 'server_license' => config('v2board.server_license'), | ||||
|                 'server_log_enable' => config('v2board.server_log_enable', 0), | ||||
|                 'server_v2ray_domain' => config('v2board.server_v2ray_domain'), | ||||
|                 'server_v2ray_protocol' => config('v2board.server_v2ray_protocol'), | ||||
|                 'server_pull_interval' => config('v2board.server_pull_interval', 60), | ||||
|                 'server_push_interval' => config('v2board.server_push_interval', 60), | ||||
|             ], | ||||
|             'email' => [ | ||||
|                 'email_template' => config('v2board.email_template', 'default'), | ||||
|   | ||||
| @@ -41,10 +41,13 @@ class PlanController extends Controller | ||||
|             DB::beginTransaction(); | ||||
|             // update user group id and transfer | ||||
|             try { | ||||
|                 User::where('plan_id', $plan->id)->update([ | ||||
|                     'group_id' => $params['group_id'], | ||||
|                     'transfer_enable' => $params['transfer_enable'] * 1073741824 | ||||
|                 ]); | ||||
|                 if ($request->input('force_update')) { | ||||
|                     User::where('plan_id', $plan->id)->update([ | ||||
|                         'group_id' => $params['group_id'], | ||||
|                         'transfer_enable' => $params['transfer_enable'] * 1073741824, | ||||
|                         'speed_limit' => $params['speed_limit'] | ||||
|                     ]); | ||||
|                 } | ||||
|                 $plan->update($params); | ||||
|             } catch (\Exception $e) { | ||||
|                 DB::rollBack(); | ||||
|   | ||||
							
								
								
									
										57
									
								
								app/Http/Controllers/Admin/Server/RouteController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								app/Http/Controllers/Admin/Server/RouteController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Http\Controllers\Admin\Server; | ||||
|  | ||||
| use App\Http\Requests\Admin\ServerShadowsocksSave; | ||||
| use App\Http\Requests\Admin\ServerShadowsocksUpdate; | ||||
| use App\Models\ServerRoute; | ||||
| use App\Models\ServerShadowsocks; | ||||
| use App\Services\ServerService; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Http\Controllers\Controller; | ||||
|  | ||||
| class RouteController extends Controller | ||||
| { | ||||
|     public function fetch(Request $request) | ||||
|     { | ||||
|         $routes = ServerRoute::get(); | ||||
|         return [ | ||||
|             'data' => $routes | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function save(Request $request) | ||||
|     { | ||||
|         $params = $request->validate([ | ||||
|             'remarks' => 'required', | ||||
|             'match' => 'required', | ||||
|             'action' => 'required', | ||||
|             'action_value' => 'nullable' | ||||
|         ]); | ||||
|         if ($request->input('id')) { | ||||
|             try { | ||||
|                 $route = ServerRoute::find($request->input('id')); | ||||
|                 $route->update($params); | ||||
|                 return [ | ||||
|                     'data' => true | ||||
|                 ]; | ||||
|             } catch (\Exception $e) { | ||||
|                 abort(500, '保存失败'); | ||||
|             } | ||||
|         } | ||||
|         if (!ServerRoute::create($params)) abort(500, '创建失败'); | ||||
|         return [ | ||||
|             'data' => true | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     public function drop(Request $request) | ||||
|     { | ||||
|         $route = ServerRoute::find($request->input('id')); | ||||
|         if (!$route) abort(500, '路由不存在'); | ||||
|         if (!$route->delete()) abort(500, '删除失败'); | ||||
|         return [ | ||||
|             'data' => true | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace App\Http\Controllers\Admin; | ||||
|  | ||||
| use App\Models\CommissionLog; | ||||
| use App\Models\ServerShadowsocks; | ||||
| use App\Models\ServerTrojan; | ||||
| use App\Models\StatUser; | ||||
| @@ -47,14 +48,12 @@ class StatController extends Controller | ||||
|                     ->where('created_at', '<', strtotime(date('Y-m-1'))) | ||||
|                     ->whereNotIn('status', [0, 2]) | ||||
|                     ->sum('total_amount'), | ||||
|                 'commission_month_payout' => Order::where('actual_commission_balance' ,'!=', NULL) | ||||
|                     ->where('created_at', '>=', strtotime(date('Y-m-1'))) | ||||
|                 'commission_month_payout' => CommissionLog::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')))) | ||||
|                     ->sum('get_amount'), | ||||
|                 'commission_last_month_payout' => CommissionLog::where('created_at', '>=', strtotime('-1 month', strtotime(date('Y-m-1')))) | ||||
|                     ->where('created_at', '<', strtotime(date('Y-m-1'))) | ||||
|                     ->sum('actual_commission_balance'), | ||||
|                     ->sum('get_amount'), | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
| @@ -100,7 +99,7 @@ class StatController extends Controller | ||||
|     { | ||||
|         $servers = [ | ||||
|             'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(), | ||||
|             'vmess' => ServerV2ray::where('parent_id', null)->get()->toArray(), | ||||
|             'v2ray' => ServerV2ray::where('parent_id', null)->get()->toArray(), | ||||
|             'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray() | ||||
|         ]; | ||||
|         $startAt = strtotime('-1 day', strtotime(date('Y-m-d'))); | ||||
|   | ||||
| @@ -33,7 +33,14 @@ class AppController extends Controller | ||||
|         $proxies = []; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], [ | ||||
|                     'aes-128-gcm', | ||||
|                     'aes-192-gcm', | ||||
|                     'aes-256-gcm', | ||||
|                     'chacha20-ietf-poly1305' | ||||
|                 ]) | ||||
|             ) { | ||||
|                 array_push($proxy, Protocols\Clash::buildShadowsocks($user['uuid'], $item)); | ||||
|                 array_push($proxies, $item['name']); | ||||
|             } | ||||
|   | ||||
| @@ -13,9 +13,7 @@ class ClientController extends Controller | ||||
|     public function subscribe(Request $request) | ||||
|     { | ||||
|         $flag = $request->input('flag') | ||||
|             ?? (isset($_SERVER['HTTP_USER_AGENT']) | ||||
|                 ? $_SERVER['HTTP_USER_AGENT'] | ||||
|                 : ''); | ||||
|             ?? ($_SERVER['HTTP_USER_AGENT'] ?? ''); | ||||
|         $flag = strtolower($flag); | ||||
|         $user = $request->user; | ||||
|         // account not expired and is not banned. | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
|  | ||||
| namespace App\Http\Controllers\Client\Protocols; | ||||
|  | ||||
| use App\Utils\Dict; | ||||
| use phpDocumentor\Reflection\Types\Self_; | ||||
| use Symfony\Component\Yaml\Yaml; | ||||
|  | ||||
| class Clash | ||||
| @@ -36,7 +38,14 @@ class Clash | ||||
|         $proxies = []; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], [ | ||||
|                     'aes-128-gcm', | ||||
|                     'aes-192-gcm', | ||||
|                     'aes-256-gcm', | ||||
|                     'chacha20-ietf-poly1305' | ||||
|                 ]) | ||||
|             ) { | ||||
|                 array_push($proxy, self::buildShadowsocks($user['uuid'], $item)); | ||||
|                 array_push($proxies, $item['name']); | ||||
|             } | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
|  | ||||
| namespace App\Http\Controllers\Client\Protocols; | ||||
|  | ||||
| use App\Utils\Helper; | ||||
|  | ||||
| class Shadowrocket | ||||
| { | ||||
|     public $flag = 'shadowrocket'; | ||||
| @@ -43,6 +45,16 @@ class Shadowrocket | ||||
|  | ||||
|     public static function buildShadowsocks($password, $server) | ||||
|     { | ||||
|         if ($server['cipher'] === '2022-blake3-aes-128-gcm') { | ||||
|             $serverKey = Helper::getShadowsocksServerKey($server['created_at'], 16); | ||||
|             $userKey = Helper::uuidToBase64($password, 16); | ||||
|             $password = "{$serverKey}:{$userKey}"; | ||||
|         } | ||||
|         if ($server['cipher'] === '2022-blake3-aes-256-gcm') { | ||||
|             $serverKey = Helper::getShadowsocksServerKey($server['created_at'], 32); | ||||
|             $userKey = Helper::uuidToBase64($password, 32); | ||||
|             $password = "{$serverKey}:{$userKey}"; | ||||
|         } | ||||
|         $name = rawurlencode($server['name']); | ||||
|         $str = str_replace( | ||||
|             ['+', '/', '='], | ||||
|   | ||||
| @@ -29,7 +29,9 @@ class Shadowsocks | ||||
|         $bytesRemaining = $user['transfer_enable'] - $bytesUsed; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], ['aes-128-gcm', 'aes-256-gcm', 'aes-192-gcm']) | ||||
|             ) { | ||||
|                 array_push($configs, self::SIP008($item, $user)); | ||||
|             } | ||||
|         } | ||||
|   | ||||
| @@ -36,7 +36,14 @@ class Stash | ||||
|         $proxies = []; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], [ | ||||
|                     'aes-128-gcm', | ||||
|                     'aes-192-gcm', | ||||
|                     'aes-256-gcm', | ||||
|                     'chacha20-ietf-poly1305' | ||||
|                 ]) | ||||
|             ) { | ||||
|                 array_push($proxy, self::buildShadowsocks($user['uuid'], $item)); | ||||
|                 array_push($proxies, $item['name']); | ||||
|             } | ||||
|   | ||||
| @@ -28,7 +28,14 @@ class Surfboard | ||||
|         $proxyGroup = ''; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], [ | ||||
|                     'aes-128-gcm', | ||||
|                     'aes-192-gcm', | ||||
|                     'aes-256-gcm', | ||||
|                     'chacha20-ietf-poly1305' | ||||
|                 ]) | ||||
|             ) { | ||||
|                 // [Proxy] | ||||
|                 $proxies .= self::buildShadowsocks($user['uuid'], $item); | ||||
|                 // [Proxy Group] | ||||
|   | ||||
| @@ -28,7 +28,14 @@ class Surge | ||||
|         $proxyGroup = ''; | ||||
|  | ||||
|         foreach ($servers as $item) { | ||||
|             if ($item['type'] === 'shadowsocks') { | ||||
|             if ($item['type'] === 'shadowsocks' | ||||
|                 && in_array($item['cipher'], [ | ||||
|                     'aes-128-gcm', | ||||
|                     'aes-192-gcm', | ||||
|                     'aes-256-gcm', | ||||
|                     'chacha20-ietf-poly1305' | ||||
|                 ]) | ||||
|             ) { | ||||
|                 // [Proxy] | ||||
|                 $proxies .= self::buildShadowsocks($user['uuid'], $item); | ||||
|                 // [Proxy Group] | ||||
|   | ||||
| @@ -23,6 +23,19 @@ class CommController extends Controller | ||||
|                 'app_description' => config('v2board.app_description'), | ||||
|                 'app_url' => config('v2board.app_url'), | ||||
|                 'logo' => config('v2board.logo'), | ||||
|  | ||||
|                 // TODO:REMOVE:1.7.0 | ||||
|  | ||||
|                 'tosUrl' => config('v2board.tos_url'), | ||||
|                 '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'), | ||||
|             ] | ||||
|         ]); | ||||
|     } | ||||
|   | ||||
| @@ -7,6 +7,7 @@ use App\Http\Requests\Passport\AuthRegister; | ||||
| use App\Http\Requests\Passport\AuthForget; | ||||
| use App\Http\Requests\Passport\AuthLogin; | ||||
| use App\Jobs\SendEmailJob; | ||||
| use App\Services\AuthService; | ||||
| use Illuminate\Http\Request; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| use App\Models\Plan; | ||||
| @@ -16,6 +17,7 @@ use App\Utils\Helper; | ||||
| use App\Utils\Dict; | ||||
| use App\Utils\CacheKey; | ||||
| use ReCaptcha\ReCaptcha; | ||||
| use Firebase\JWT\JWT; | ||||
|  | ||||
| class AuthController extends Controller | ||||
| { | ||||
| @@ -77,7 +79,9 @@ class AuthController extends Controller | ||||
|         if ((int)config('v2board.register_limit_by_ip_enable', 0)) { | ||||
|             $registerCountByIP = Cache::get(CacheKey::get('REGISTER_IP_RATE_LIMIT', $request->ip())) ?? 0; | ||||
|             if ((int)$registerCountByIP >= (int)config('v2board.register_limit_count', 3)) { | ||||
|                 abort(500, __('Register frequently, please try again after 1 hour')); | ||||
|                 abort(500, __('Register frequently, please try again after :minute minute', [ | ||||
|                     'minute' => config('v2board.register_limit_expire', 60) | ||||
|                 ])); | ||||
|             } | ||||
|         } | ||||
|         if ((int)config('v2board.recaptcha_enable', 0)) { | ||||
| @@ -163,11 +167,6 @@ class AuthController extends Controller | ||||
|             Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))); | ||||
|         } | ||||
|  | ||||
|         $data = [ | ||||
|             'token' => $user->token, | ||||
|             'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|         ]; | ||||
|  | ||||
|         $user->last_login_at = time(); | ||||
|         $user->save(); | ||||
|  | ||||
| @@ -178,8 +177,11 @@ class AuthController extends Controller | ||||
|                 (int)config('v2board.register_limit_expire', 60) * 60 | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $authService = new AuthService($user); | ||||
|  | ||||
|         return response()->json([ | ||||
|             'data' => $data | ||||
|             'data' => $authService->generateAuthData('register') | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| @@ -188,6 +190,12 @@ class AuthController extends Controller | ||||
|         $email = $request->input('email'); | ||||
|         $password = $request->input('password'); | ||||
|  | ||||
|         $passwordErrorCount = (int)Cache::get(CacheKey::get('PASSWORD_ERROR_LIMIT', $email), 0); | ||||
|  | ||||
|         if ($passwordErrorCount >= 5) { | ||||
|             abort(500, __('There are too many password errors, please try again after 30 minutes.')); | ||||
|         } | ||||
|  | ||||
|         $user = User::where('email', $email)->first(); | ||||
|         if (!$user) { | ||||
|             abort(500, __('Incorrect email or password')); | ||||
| @@ -198,6 +206,11 @@ class AuthController extends Controller | ||||
|             $password, | ||||
|             $user->password) | ||||
|         ) { | ||||
|             Cache::put( | ||||
|                 CacheKey::get('PASSWORD_ERROR_LIMIT', $email), | ||||
|                 (int)$passwordErrorCount + 1, | ||||
|                 30 * 60 | ||||
|             ); | ||||
|             abort(500, __('Incorrect email or password')); | ||||
|         } | ||||
|  | ||||
| @@ -205,14 +218,9 @@ class AuthController extends Controller | ||||
|             abort(500, __('Your account has been suspended')); | ||||
|         } | ||||
|  | ||||
|         $data = [ | ||||
|             'token' => $user->token, | ||||
|             'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|         ]; | ||||
|  | ||||
|         if ($user->is_admin) $data['is_admin'] = true; | ||||
|         $authService = new AuthService($user); | ||||
|         return response([ | ||||
|             'data' => $data | ||||
|             'data' => $authService->generateAuthData('login') | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
| @@ -241,49 +249,25 @@ class AuthController extends Controller | ||||
|             if ($user->banned) { | ||||
|                 abort(500, __('Your account has been suspended')); | ||||
|             } | ||||
|             $data = [ | ||||
|                 'token' => $user->token, | ||||
|                 'auth_data' => base64_encode("{$user->email}:{$user->password}") | ||||
|             ]; | ||||
|             Cache::forget($key); | ||||
|             $authService = new AuthService($user); | ||||
|             return response([ | ||||
|                 'data' => $data | ||||
|                 'data' => $authService->generateAuthData('token') | ||||
|             ]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function getTempToken(Request $request) | ||||
|     { | ||||
|         $user = User::where('token', $request->input('token'))->first(); | ||||
|         if (!$user) { | ||||
|             abort(500, __('Token error')); | ||||
|         } | ||||
|  | ||||
|         $code = Helper::guid(); | ||||
|         $key = CacheKey::get('TEMP_TOKEN', $code); | ||||
|         Cache::put($key, $user->id, 60); | ||||
|         return response([ | ||||
|             'data' => $code | ||||
|         ]); | ||||
|     } | ||||
|  | ||||
|     public function getQuickLoginUrl(Request $request) | ||||
|     { | ||||
|         $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(); | ||||
|         if (!$user) { | ||||
|             abort(500, __('Token error')); | ||||
|         } | ||||
|         $user = AuthService::decryptAuthData($authorization); | ||||
|         if (!$user) abort(403, '未登录或登陆已过期'); | ||||
|  | ||||
|         $code = Helper::guid(); | ||||
|         $key = CacheKey::get('TEMP_TOKEN', $code); | ||||
|         Cache::put($key, $user->id, 60); | ||||
|         Cache::put($key, $user['id'], 60); | ||||
|         $redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard'); | ||||
|         if (config('v2board.app_url')) { | ||||
|             $url = config('v2board.app_url') . $redirect; | ||||
|   | ||||
| @@ -84,7 +84,7 @@ class DeepbworkController extends Controller | ||||
|         foreach ($data as $item) { | ||||
|             $u = $item['u']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server->toArray(), 'vmess'); | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $server->toArray(), 'v2ray'); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|   | ||||
| @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Server; | ||||
| use App\Services\ServerService; | ||||
| use App\Services\UserService; | ||||
| use App\Utils\CacheKey; | ||||
| use App\Utils\Helper; | ||||
| use Illuminate\Http\Request; | ||||
| use App\Http\Controllers\Controller; | ||||
| use App\Models\ServerShadowsocks; | ||||
| @@ -12,12 +13,12 @@ use App\Models\ServerV2ray; | ||||
| use App\Models\ServerTrojan; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
| 
 | ||||
| class VProxyController extends Controller | ||||
| class UniProxyController extends Controller | ||||
| { | ||||
|     private $nodeType; | ||||
|     private $nodeInfo; | ||||
|     private $nodeId; | ||||
|     private $token; | ||||
|     private $serverService; | ||||
| 
 | ||||
|     public function __construct(Request $request) | ||||
|     { | ||||
| @@ -28,25 +29,11 @@ class VProxyController extends Controller | ||||
|         if ($token !== config('v2board.server_token')) { | ||||
|             abort(500, 'token is error'); | ||||
|         } | ||||
|         $this->token = $token; | ||||
|         $this->nodeType = $request->input('node_type'); | ||||
|         $this->nodeId = $request->input('node_id'); | ||||
|         switch ($this->nodeType) { | ||||
|             case 'v2ray': | ||||
|                 $this->nodeInfo = ServerV2ray::find($this->nodeId); | ||||
|                 break; | ||||
|             case 'shadowsocks': | ||||
|                 $this->nodeInfo = ServerShadowsocks::find($this->nodeId); | ||||
|                 break; | ||||
|             case 'trojan': | ||||
|                 $this->nodeInfo = ServerTrojan::find($this->nodeId); | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|         if (!$this->nodeInfo) { | ||||
|             abort(500, 'server not found'); | ||||
|         } | ||||
|         $this->serverService = new ServerService(); | ||||
|         $this->nodeInfo = $this->serverService->getServer($this->nodeId, $this->nodeType); | ||||
|         if (!$this->nodeInfo) abort(500, 'server is not exist'); | ||||
|     } | ||||
| 
 | ||||
|     // 后端获取用户
 | ||||
| @@ -54,21 +41,11 @@ class VProxyController extends Controller | ||||
|     { | ||||
|         ini_set('memory_limit', -1); | ||||
|         Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_LAST_CHECK_AT', $this->nodeInfo->id), time(), 3600); | ||||
|         $serverService = new ServerService(); | ||||
|         $users = $serverService->getAvailableUsers($this->nodeInfo->group_id); | ||||
|         $users = $this->serverService->getAvailableUsers($this->nodeInfo->group_id); | ||||
|         $users = $users->toArray(); | ||||
| 
 | ||||
|         $response['users'] = $users; | ||||
| 
 | ||||
|         switch ($this->nodeType) { | ||||
|             case 'shadowsocks': | ||||
|                 $response['server'] = [ | ||||
|                     'cipher' => $this->nodeInfo->cipher, | ||||
|                     'server_port' => $this->nodeInfo->server_port | ||||
|                 ]; | ||||
|                 break; | ||||
|         } | ||||
| 
 | ||||
|         $eTag = sha1(json_encode($response)); | ||||
|         if (strpos($request->header('If-None-Match'), $eTag) !== false ) { | ||||
|             abort(304); | ||||
| @@ -78,17 +55,17 @@ class VProxyController extends Controller | ||||
|     } | ||||
| 
 | ||||
|     // 后端提交数据
 | ||||
|     public function submit(Request $request) | ||||
|     public function push(Request $request) | ||||
|     { | ||||
|         $data = file_get_contents('php://input'); | ||||
|         $data = json_decode($data, true); | ||||
|         Cache::put(CacheKey::get('SERVER_' . strtoupper($this->nodeType) . '_ONLINE_USER', $this->nodeInfo->id), count($data), 3600); | ||||
|         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']; | ||||
|             $d = $item['d']; | ||||
|             $userService->trafficFetch($u, $d, $item['user_id'], $this->nodeInfo->toArray(), $this->nodeType); | ||||
|         foreach (array_keys($data) as $k) { | ||||
|             $u = $data[$k][0]; | ||||
|             $d = $data[$k][1]; | ||||
|             $userService->trafficFetch($u, $d, $k, $this->nodeInfo->toArray(), $this->nodeType); | ||||
|         } | ||||
| 
 | ||||
|         return response([ | ||||
| @@ -101,28 +78,48 @@ class VProxyController extends Controller | ||||
|     { | ||||
|         switch ($this->nodeType) { | ||||
|             case 'shadowsocks': | ||||
|                 die(json_encode([ | ||||
|                 $response = [ | ||||
|                     'server_port' => $this->nodeInfo->server_port, | ||||
|                     'cipher' => $this->nodeInfo->cipher, | ||||
|                     'obfs' => $this->nodeInfo->obfs, | ||||
|                     'obfs_settings' => $this->nodeInfo->obfs_settings | ||||
|                 ], JSON_UNESCAPED_UNICODE)); | ||||
|                 ]; | ||||
| 
 | ||||
|                 if ($this->nodeInfo->cipher === '2022-blake3-aes-128-gcm') { | ||||
|                     $response['server_key'] = Helper::getShadowsocksServerKey($this->nodeInfo->created_at, 16); | ||||
|                 } | ||||
|                 if ($this->nodeInfo->cipher === '2022-blake3-aes-256-gcm') { | ||||
|                     $response['server_key'] = Helper::getShadowsocksServerKey($this->nodeInfo->created_at, 32); | ||||
|                 } | ||||
|                 break; | ||||
|             case 'v2ray': | ||||
|                 die(json_encode([ | ||||
|                 $response = [ | ||||
|                     'server_port' => $this->nodeInfo->server_port, | ||||
|                     'network' => $this->nodeInfo->network, | ||||
|                     'cipher' => $this->nodeInfo->cipher, | ||||
|                     'networkSettings' => $this->nodeInfo->networkSettings, | ||||
|                     'tls' => $this->nodeInfo->tls | ||||
|                 ], JSON_UNESCAPED_UNICODE)); | ||||
|                 ]; | ||||
|                 break; | ||||
|             case 'trojan': | ||||
|                 die(json_encode([ | ||||
|                 $response = [ | ||||
|                     'host' => $this->nodeInfo->host, | ||||
|                     'server_port' => $this->nodeInfo->server_port | ||||
|                 ], JSON_UNESCAPED_UNICODE)); | ||||
|                     'server_port' => $this->nodeInfo->server_port, | ||||
|                     'server_name' => $this->nodeInfo->server_name | ||||
|                 ]; | ||||
|                 break; | ||||
|         } | ||||
|         $response['base_config'] = [ | ||||
|             'push_interval' => config('v2board.server_push_interval', 60), | ||||
|             'pull_interval' => config('v2board.server_pull_interval', 60) | ||||
|         ]; | ||||
|         if ($this->nodeInfo['route_id']) { | ||||
|             $response['routes'] = $this->serverService->getRoutes($this->nodeInfo['route_id']); | ||||
|         } | ||||
|         $eTag = sha1(json_encode($response)); | ||||
|         if (strpos($request->header('If-None-Match'), $eTag) !== false ) { | ||||
|             abort(304); | ||||
|         } | ||||
| 
 | ||||
|         return response($response)->header('ETag', "\"{$eTag}\""); | ||||
|     } | ||||
| } | ||||
| @@ -58,19 +58,21 @@ class InviteController extends Controller | ||||
|         if ($user->commission_rate) { | ||||
|             $commission_rate = $user->commission_rate; | ||||
|         } | ||||
|         $uncheck_commission_balance = (int)Order::where('status', 3) | ||||
|             ->where('commission_status', 0) | ||||
|             ->where('invite_user_id', $request->user['id']) | ||||
|             ->sum('commission_balance'); | ||||
|         if (config('v2board.commission_distribution_enable', 0)) { | ||||
|             $uncheck_commission_balance = $uncheck_commission_balance * (config('v2board.commission_distribution_l1') / 100); | ||||
|         } | ||||
|         $stat = [ | ||||
|             //已注册用户数 | ||||
|             (int)User::where('invite_user_id', $request->user['id'])->count(), | ||||
|             //有效的佣金 | ||||
|             (int)Order::where('status', 3) | ||||
|                 ->where('commission_status', 2) | ||||
|                 ->where('invite_user_id', $request->user['id']) | ||||
|                 ->sum('commission_balance'), | ||||
|             (int)CommissionLog::where('invite_user_id', $request->user['id']) | ||||
|                 ->sum('get_amount'), | ||||
|             //确认中的佣金 | ||||
|             (int)Order::where('status', 3) | ||||
|                 ->where('commission_status', 0) | ||||
|                 ->where('invite_user_id', $request->user['id']) | ||||
|                 ->sum('commission_balance'), | ||||
|             $uncheck_commission_balance, | ||||
|             //佣金比例 | ||||
|             (int)$commission_rate, | ||||
|             //可用佣金 | ||||
|   | ||||
| @@ -85,7 +85,7 @@ class OrderController extends Controller | ||||
|             abort(500, __('Subscription plan does not exist')); | ||||
|         } | ||||
|  | ||||
|         if (!$planService->haveCapacity() && $request->input('period') !== 'reset_price') { | ||||
|         if ($user->plan_id !== $plan->id && !$planService->haveCapacity() && $request->input('period') !== 'reset_price') { | ||||
|             abort(500, __('Current product is sold out')); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -26,8 +26,14 @@ class ServerController extends Controller | ||||
|             $serverService = new ServerService(); | ||||
|             $servers = $serverService->getAvailableServers($user); | ||||
|         } | ||||
|  | ||||
|         $eTag = sha1(json_encode(array_column($servers, 'updated_at'))); | ||||
|         if (strpos($request->header('If-None-Match'), $eTag) !== false ) { | ||||
|             abort(304); | ||||
|         } | ||||
|  | ||||
|         return response([ | ||||
|             'data' => $servers | ||||
|         ]); | ||||
|         ])->header('ETag', "\"{$eTag}\""); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -113,7 +113,8 @@ class UserController extends Controller | ||||
|                 'u', | ||||
|                 'd', | ||||
|                 'transfer_enable', | ||||
|                 'email' | ||||
|                 'email', | ||||
|                 'uuid' | ||||
|             ]) | ||||
|             ->first(); | ||||
|         if (!$user) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use App\Services\AuthService; | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| @@ -19,24 +20,10 @@ class Admin | ||||
|         $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); | ||||
|         } | ||||
|         $user = AuthService::decryptAuthData($authorization); | ||||
|         if (!$user || !$user['is_admin']) abort(403, '未登录或登陆已过期'); | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|             'user' => $user | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
|  | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use App\Services\AuthService; | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class Staff | ||||
| { | ||||
| @@ -19,24 +19,10 @@ class Staff | ||||
|         $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); | ||||
|         } | ||||
|         $user = AuthService::decryptAuthData($authorization); | ||||
|         if (!$user || !$user['is_staff']) abort(403, '未登录或登陆已过期'); | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|             'user' => $user | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| namespace App\Http\Middleware; | ||||
|  | ||||
| use App\Services\AuthService; | ||||
| use Closure; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| @@ -19,23 +20,10 @@ class User | ||||
|         $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, '鉴权失败,请重新登入'); | ||||
|             Cache::put($authorization, $user->toArray(), 3600); | ||||
|         } | ||||
|         $user = AuthService::decryptAuthData($authorization); | ||||
|         if (!$user) abort(403, '未登录或登陆已过期'); | ||||
|         $request->merge([ | ||||
|             'user' => Cache::get($authorization) | ||||
|             'user' => $user | ||||
|         ]); | ||||
|         return $next($request); | ||||
|     } | ||||
|   | ||||
| @@ -46,6 +46,7 @@ class ConfigSave extends FormRequest | ||||
|         'register_limit_by_ip_enable' => 'in:0,1', | ||||
|         'register_limit_count' => 'integer', | ||||
|         'register_limit_expire' => 'integer', | ||||
|         'secure_path' => '', | ||||
|         // subscribe | ||||
|         'plan_change_enable' => 'in:0,1', | ||||
|         'reset_traffic_method' => 'in:0,1,2,3,4', | ||||
| @@ -56,17 +57,14 @@ class ConfigSave extends FormRequest | ||||
|         'show_info_to_server_enable' => 'in:0,1', | ||||
|         // server | ||||
|         'server_token' => 'nullable|min:16', | ||||
|         'server_license' => 'nullable', | ||||
|         'server_log_enable' => 'in:0,1', | ||||
|         'server_v2ray_domain' => '', | ||||
|         'server_v2ray_protocol' => '', | ||||
|         'server_pull_interval' => 'integer', | ||||
|         'server_push_interval' => 'integer', | ||||
|         // frontend | ||||
|         'frontend_theme' => '', | ||||
|         'frontend_theme_sidebar' => 'in:dark,light', | ||||
|         'frontend_theme_header' => 'in:dark,light', | ||||
|         'frontend_theme_color' => 'in:default,darkblue,black,green', | ||||
|         'frontend_theme_sidebar' => 'nullable|in:dark,light', | ||||
|         'frontend_theme_header' => 'nullable|in:dark,light', | ||||
|         'frontend_theme_color' => 'nullable|in:default,darkblue,black,green', | ||||
|         'frontend_background_url' => 'nullable|url', | ||||
|         'frontend_admin_path' => '', | ||||
|         // email | ||||
|         'email_template' => '', | ||||
|         'email_host' => '', | ||||
|   | ||||
| @@ -27,7 +27,8 @@ class PlanSave extends FormRequest | ||||
|             'onetime_price' => 'nullable|integer', | ||||
|             'reset_price' => 'nullable|integer', | ||||
|             'reset_traffic_method' => 'nullable|integer|in:0,1,2,3,4', | ||||
|             'capacity_limit' => 'nullable|integer' | ||||
|             'capacity_limit' => 'nullable|integer', | ||||
|             'speed_limit' => 'nullable|integer' | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @@ -49,7 +50,8 @@ class PlanSave extends FormRequest | ||||
|             'reset_price.integer' => '流量重置包金额有误', | ||||
|             'reset_traffic_method.integer' => '流量重置方式格式有误', | ||||
|             'reset_traffic_method.in' => '流量重置方式格式有误', | ||||
|             'capacity_limit.integer' => '容纳用户量限制格式有误' | ||||
|             'capacity_limit.integer' => '容纳用户量限制格式有误', | ||||
|             'speed_limit.integer' => '限速格式有误' | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -18,10 +18,11 @@ class ServerShadowsocksSave extends FormRequest | ||||
|             'name' => 'required', | ||||
|             'group_id' => 'required|array', | ||||
|             'parent_id' => 'nullable|integer', | ||||
|             'route_id' => 'nullable|array', | ||||
|             'host' => 'required', | ||||
|             'port' => 'required', | ||||
|             'server_port' => 'required', | ||||
|             'cipher' => 'required|in:aes-128-gcm,aes-256-gcm,chacha20-ietf-poly1305', | ||||
|             'cipher' => 'required|in:aes-128-gcm,aes-192-gcm,aes-256-gcm,chacha20-ietf-poly1305,2022-blake3-aes-128-gcm,2022-blake3-aes-256-gcm', | ||||
|             'obfs' => 'nullable|in:http', | ||||
|             'obfs_settings' => 'nullable|array', | ||||
|             'tags' => 'nullable|array', | ||||
| @@ -35,6 +36,7 @@ class ServerShadowsocksSave extends FormRequest | ||||
|             'name.required' => '节点名称不能为空', | ||||
|             'group_id.required' => '权限组不能为空', | ||||
|             'group_id.array' => '权限组格式不正确', | ||||
|             'route_id.array' => '路由组格式不正确', | ||||
|             'parent_id.integer' => '父节点格式不正确', | ||||
|             'host.required' => '节点地址不能为空', | ||||
|             'port.required' => '连接端口不能为空', | ||||
|   | ||||
| @@ -17,6 +17,7 @@ class ServerTrojanSave extends FormRequest | ||||
|             'show' => '', | ||||
|             'name' => 'required', | ||||
|             'group_id' => 'required|array', | ||||
|             'route_id' => 'nullable|array', | ||||
|             'parent_id' => 'nullable|integer', | ||||
|             'host' => 'required', | ||||
|             'port' => 'required', | ||||
| @@ -34,6 +35,7 @@ class ServerTrojanSave extends FormRequest | ||||
|             'name.required' => '节点名称不能为空', | ||||
|             'group_id.required' => '权限组不能为空', | ||||
|             'group_id.array' => '权限组格式不正确', | ||||
|             'route_id.array' => '路由组格式不正确', | ||||
|             'parent_id.integer' => '父节点格式不正确', | ||||
|             'host.required' => '节点地址不能为空', | ||||
|             'port.required' => '连接端口不能为空', | ||||
|   | ||||
| @@ -17,6 +17,7 @@ class ServerV2raySave extends FormRequest | ||||
|             'show' => '', | ||||
|             'name' => 'required', | ||||
|             'group_id' => 'required|array', | ||||
|             'route_id' => 'nullable|array', | ||||
|             'parent_id' => 'nullable|integer', | ||||
|             'host' => 'required', | ||||
|             'port' => 'required', | ||||
| @@ -38,6 +39,7 @@ class ServerV2raySave extends FormRequest | ||||
|             'name.required' => '节点名称不能为空', | ||||
|             'group_id.required' => '权限组不能为空', | ||||
|             'group_id.array' => '权限组格式不正确', | ||||
|             'route_id.array' => '路由组格式不正确', | ||||
|             'parent_id.integer' => '父ID格式不正确', | ||||
|             'host.required' => '节点地址不能为空', | ||||
|             'port.required' => '连接端口不能为空', | ||||
|   | ||||
| @@ -14,7 +14,7 @@ class UserFetch extends FormRequest | ||||
|     public function rules() | ||||
|     { | ||||
|         return [ | ||||
|             'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks', | ||||
|             'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks,is_admin', | ||||
|             'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=', | ||||
|             'filter.*.value' => 'required' | ||||
|         ]; | ||||
|   | ||||
| @@ -29,7 +29,8 @@ class UserUpdate extends FormRequest | ||||
|             'balance' => 'integer', | ||||
|             'commission_type' => 'integer', | ||||
|             'commission_balance' => 'integer', | ||||
|             'remarks' => 'nullable' | ||||
|             'remarks' => 'nullable', | ||||
|             'speed_limit' => 'nullable|integer' | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
| @@ -59,7 +60,8 @@ class UserUpdate extends FormRequest | ||||
|             'd.integer' => '下行流量格式不正确', | ||||
|             'balance.integer' => '余额格式不正确', | ||||
|             'commission_balance.integer' => '佣金格式不正确', | ||||
|             'password.min' => '密码长度最小8位' | ||||
|             'password.min' => '密码长度最小8位', | ||||
|             'speed_limit.integer' => '限速格式不正确' | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ class AdminRoute | ||||
|     public function map(Registrar $router) | ||||
|     { | ||||
|         $router->group([ | ||||
|             'prefix' => 'admin', | ||||
|             'prefix' => config('v2board.secure_path', config('v2board.frontend_admin_path', 'admin')), | ||||
|             'middleware' => 'admin' | ||||
|         ], function ($router) { | ||||
|             // Config | ||||
| @@ -28,6 +28,9 @@ class AdminRoute | ||||
|             $router->get ('/server/group/fetch', 'Admin\\Server\\GroupController@fetch'); | ||||
|             $router->post('/server/group/save', 'Admin\\Server\\GroupController@save'); | ||||
|             $router->post('/server/group/drop', 'Admin\\Server\\GroupController@drop'); | ||||
|             $router->get ('/server/route/fetch', 'Admin\\Server\\RouteController@fetch'); | ||||
|             $router->post('/server/route/save', 'Admin\\Server\\RouteController@save'); | ||||
|             $router->post('/server/route/drop', 'Admin\\Server\\RouteController@drop'); | ||||
|             $router->get ('/server/manage/getNodes', 'Admin\\Server\\ManageController@getNodes'); | ||||
|             $router->post('/server/manage/sort', 'Admin\\Server\\ManageController@sort'); | ||||
|             $router->group([ | ||||
|   | ||||
| @@ -14,7 +14,6 @@ class ClientRoute | ||||
|             // Client | ||||
|             $router->get('/subscribe', 'Client\\ClientController@subscribe'); | ||||
|             // App | ||||
|             $router->get('/app/config', 'Client\\AppController@config'); | ||||
|             $router->get('/app/getConfig', 'Client\\AppController@getConfig'); | ||||
|             $router->get('/app/getVersion', 'Client\\AppController@getVersion'); | ||||
|         }); | ||||
|   | ||||
| @@ -20,6 +20,7 @@ class PassportRoute | ||||
|             // Comm | ||||
|             $router->post('/comm/sendEmailVerify', 'Passport\\CommController@sendEmailVerify'); | ||||
|             $router->post('/comm/pv', 'Passport\\CommController@pv'); | ||||
|             $router->get ('/comm/config', 'Guest\\CommController@config');                                              // TODO:REMOVE:1.7.0 | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										16
									
								
								app/Models/ServerRoute.php
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										16
									
								
								app/Models/ServerRoute.php
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Models; | ||||
|  | ||||
| use Illuminate\Database\Eloquent\Model; | ||||
|  | ||||
| class ServerRoute extends Model | ||||
| { | ||||
|     protected $table = 'v2_server_route'; | ||||
|     protected $dateFormat = 'U'; | ||||
|     protected $guarded = ['id']; | ||||
|     protected $casts = [ | ||||
|         'created_at' => 'timestamp', | ||||
|         'updated_at' => 'timestamp', | ||||
|     ]; | ||||
| } | ||||
| @@ -13,6 +13,7 @@ class ServerShadowsocks extends Model | ||||
|         'created_at' => 'timestamp', | ||||
|         'updated_at' => 'timestamp', | ||||
|         'group_id' => 'array', | ||||
|         'route_id' => 'array', | ||||
|         'tags' => 'array', | ||||
|         'obfs_settings' => 'array' | ||||
|     ]; | ||||
|   | ||||
| @@ -13,6 +13,7 @@ class ServerTrojan extends Model | ||||
|         'created_at' => 'timestamp', | ||||
|         'updated_at' => 'timestamp', | ||||
|         'group_id' => 'array', | ||||
|         'route_id' => 'array', | ||||
|         'tags' => 'array' | ||||
|     ]; | ||||
| } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ class ServerV2ray extends Model | ||||
|         'created_at' => 'timestamp', | ||||
|         'updated_at' => 'timestamp', | ||||
|         'group_id' => 'array', | ||||
|         'route_id' => 'array', | ||||
|         'tlsSettings' => 'array', | ||||
|         'networkSettings' => 'array', | ||||
|         'dnsSettings' => 'array', | ||||
|   | ||||
| @@ -32,6 +32,11 @@ class MGate { | ||||
|                 'label' => 'AppSecret', | ||||
|                 'description' => '', | ||||
|                 'type' => 'input', | ||||
|             ], | ||||
|             'mgate_source_currency' => [ | ||||
|                 'label' => '源货币', | ||||
|                 'description' => '默认CNY', | ||||
|                 'type' => 'input' | ||||
|             ] | ||||
|         ]; | ||||
|     } | ||||
| @@ -44,6 +49,9 @@ class MGate { | ||||
|             'notify_url' => $order['notify_url'], | ||||
|             'return_url' => $order['return_url'] | ||||
|         ]; | ||||
|         if (isset($this->config['mgate_source_currency'])) { | ||||
|             $params['source_currency'] = $this->config['mgate_source_currency']; | ||||
|         } | ||||
|         $params['app_id'] = $this->config['mgate_app_id']; | ||||
|         ksort($params); | ||||
|         $str = http_build_query($params) . $this->config['mgate_app_secret']; | ||||
|   | ||||
							
								
								
									
										54
									
								
								app/Services/AuthService.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								app/Services/AuthService.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | ||||
| <?php | ||||
|  | ||||
| namespace App\Services; | ||||
|  | ||||
| use Firebase\JWT\JWT; | ||||
| use Firebase\JWT\Key; | ||||
| use App\Models\User; | ||||
| use Illuminate\Support\Facades\Cache; | ||||
|  | ||||
| class AuthService | ||||
| { | ||||
|     private $user; | ||||
|  | ||||
|     public function __construct($user) | ||||
|     { | ||||
|         $this->user = $user; | ||||
|     } | ||||
|  | ||||
|     public function generateAuthData($utm) | ||||
|     { | ||||
|         return [ | ||||
|             'token' => $this->user->token, | ||||
|             'is_admin' => $this->user->is_admin, | ||||
|             'auth_data' => JWT::encode([ | ||||
|                 'expired_at' => time() + 3600, | ||||
|                 'id' => $this->user->id, | ||||
|                 'utm' => $utm, | ||||
|             ], config('app.key'), 'HS256') | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static function decryptAuthData($jwt) | ||||
|     { | ||||
|         try { | ||||
|             if (!Cache::has($jwt)) { | ||||
|                 $data = (array)JWT::decode($jwt, new Key(config('app.key'), 'HS256')); | ||||
|                 if ($data['expired_at'] < time()) return false; | ||||
|                 $user = User::select([ | ||||
|                     'id', | ||||
|                     'email', | ||||
|                     'is_admin', | ||||
|                     'is_staff' | ||||
|                 ]) | ||||
|                     ->find($data['id']); | ||||
|                 if (!$user) return false; | ||||
|                 Cache::put($jwt, $user->toArray(), 3600); | ||||
|             } | ||||
|             return Cache::get($jwt); | ||||
|         } catch (\Exception $e) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -71,6 +71,8 @@ class OrderService | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         $this->setSpeedLimit($plan->speed_limit); | ||||
|  | ||||
|         if (!$this->user->save()) { | ||||
|             DB::rollBack(); | ||||
|             abort(500, '开通失败'); | ||||
| @@ -253,6 +255,11 @@ class OrderService | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private function setSpeedLimit($speedLimit) | ||||
|     { | ||||
|         $this->user->speed_limit = $speedLimit; | ||||
|     } | ||||
|  | ||||
|     private function buyByResetTraffic() | ||||
|     { | ||||
|         $this->user->u = 0; | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| namespace App\Services; | ||||
|  | ||||
| use App\Models\ServerLog; | ||||
| use App\Models\ServerRoute; | ||||
| use App\Models\ServerShadowsocks; | ||||
| use App\Models\User; | ||||
| use App\Models\ServerV2ray; | ||||
| @@ -100,10 +101,13 @@ class ServerService | ||||
|         ); | ||||
|         $tmp = array_column($servers, 'sort'); | ||||
|         array_multisort($tmp, SORT_ASC, $servers); | ||||
|         $servers = array_map(function ($server) { | ||||
|             $server['port'] = (int)$server['port']; | ||||
|             return $server; | ||||
|         }, $servers); | ||||
|         return $servers; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function getAvailableUsers($groupId) | ||||
|     { | ||||
|         return User::whereIn('group_id', $groupId) | ||||
| @@ -179,7 +183,7 @@ class ServerService | ||||
|         return $server->toArray(); | ||||
|     } | ||||
|  | ||||
|     public function mergeData(&$servers) | ||||
|     private function mergeData(&$servers) | ||||
|     { | ||||
|         foreach ($servers as $k => $v) { | ||||
|             $serverType = strtoupper($servers[$k]['type']); | ||||
| @@ -213,4 +217,23 @@ class ServerService | ||||
|         array_multisort($tmp, SORT_ASC, $servers); | ||||
|         return $servers; | ||||
|     } | ||||
|  | ||||
|     public function getRoutes(array $routeIds) | ||||
|     { | ||||
|         return ServerRoute::select(['id', 'match', 'action', 'action_value'])->whereIn('id', $routeIds)->get(); | ||||
|     } | ||||
|  | ||||
|     public function getServer($serverId, $serverType) | ||||
|     { | ||||
|         switch ($serverType) { | ||||
|             case 'v2ray': | ||||
|                 return ServerV2ray::find($serverId); | ||||
|             case 'shadowsocks': | ||||
|                 return ServerShadowsocks::find($serverId); | ||||
|             case 'trojan': | ||||
|                 return ServerTrojan::find($serverId); | ||||
|             default: | ||||
|                 return false; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -20,7 +20,8 @@ class CacheKey | ||||
|         'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒', | ||||
|         'SCHEDULE_LAST_CHECK_AT' => '计划任务最后检查时间', | ||||
|         'REGISTER_IP_RATE_LIMIT' => '注册频率限制', | ||||
|         'LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP' => '最后一次发送登入链接时间' | ||||
|         'LAST_SEND_LOGIN_WITH_MAIL_LINK_TIMESTAMP' => '最后一次发送登入链接时间', | ||||
|         'PASSWORD_ERROR_LIMIT' => '密码错误次数限制' | ||||
|     ]; | ||||
|  | ||||
|     public static function get(string $key, $uniqueValue) | ||||
|   | ||||
| @@ -4,6 +4,16 @@ namespace App\Utils; | ||||
|  | ||||
| class Helper | ||||
| { | ||||
|     public static function uuidToBase64($uuid, $length) | ||||
|     { | ||||
|         return base64_encode(substr($uuid, 0, $length)); | ||||
|     } | ||||
|  | ||||
|     public static function getShadowsocksServerKey($timestamp, $length) | ||||
|     { | ||||
|         return base64_encode(substr(md5($timestamp), 0, $length)); | ||||
|     } | ||||
|  | ||||
|     public static function guid($format = false) | ||||
|     { | ||||
|         if (function_exists('com_create_guid') === true) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user