1
.gitignore
vendored
@ -18,3 +18,4 @@ composer.phar
|
||||
composer.lock
|
||||
yarn.lock
|
||||
docker-compose.yml
|
||||
.DS_Store
|
||||
|
16
.styleci.yml
@ -1,16 +0,0 @@
|
||||
php:
|
||||
preset: laravel
|
||||
enabled:
|
||||
- alpha_ordered_imports
|
||||
disabled:
|
||||
- length_ordered_imports
|
||||
- unused_use
|
||||
finder:
|
||||
not-name:
|
||||
- index.php
|
||||
- server.php
|
||||
js:
|
||||
finder:
|
||||
not-name:
|
||||
- webpack.mix.js
|
||||
css: true
|
@ -42,7 +42,9 @@ class CheckOrder extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$orders = Order::get();
|
||||
ini_set('memory_limit', -1);
|
||||
$orders = Order::whereIn('status', [0, 1])
|
||||
->get();
|
||||
foreach ($orders as $item) {
|
||||
$orderService = new OrderService($item);
|
||||
switch ($item->status) {
|
||||
|
65
app/Console/Commands/CheckServer.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use App\Services\TelegramService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class CheckServer extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'check:server';
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
$this->checkOffline();
|
||||
}
|
||||
|
||||
private function checkOffline()
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAllServers();
|
||||
foreach ($servers as $server) {
|
||||
if ($server['parent_id']) continue;
|
||||
if ($server['last_check_at'] && (time() - $server['last_check_at']) > 1800) {
|
||||
$telegramService = new TelegramService();
|
||||
$message = sprintf(
|
||||
"节点掉线通知\r\n----\r\n节点名称:%s\r\n节点地址:%s\r\n",
|
||||
$server['name'],
|
||||
$server['host']
|
||||
);
|
||||
$telegramService->sendMessageWithAdmin($message);
|
||||
Cache::forget(CacheKey::get(sprintf("SERVER_%s_LAST_CHECK_AT", strtoupper($server['type'])), $server->id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ResetTraffic extends Command
|
||||
{
|
||||
@ -41,6 +42,7 @@ class ResetTraffic extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::beginTransaction();
|
||||
$resetTrafficMethod = config('v2board.reset_traffic_method', 0);
|
||||
switch ((int)$resetTrafficMethod) {
|
||||
// 1 a month
|
||||
@ -52,6 +54,7 @@ class ResetTraffic extends Command
|
||||
$this->resetByExpireDay();
|
||||
break;
|
||||
}
|
||||
DB::commit();
|
||||
}
|
||||
|
||||
private function resetByMonthFirstDay():void
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\PaymentService;
|
||||
use App\Models\Order;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class Test extends Command
|
||||
@ -38,7 +38,5 @@ class Test extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$paymentService = new PaymentService('MGate');
|
||||
var_dump($paymentService->form());
|
||||
}
|
||||
}
|
||||
|
@ -55,8 +55,7 @@ class V2boardStatistics extends Command
|
||||
->whereNotIn('status', [0, 2]);
|
||||
$orderCount = $builder->count();
|
||||
$orderAmount = $builder->sum('total_amount');
|
||||
$builder = $builder->where('commission_balance', '!=', 0)
|
||||
->where('commission_status', 0);
|
||||
$builder = $builder->where('commission_balance', '!=', 0);
|
||||
$commissionCount = $builder->count();
|
||||
$commissionAmount = $builder->sum('commission_balance');
|
||||
$data = [
|
||||
|
@ -25,7 +25,7 @@ class Kernel extends ConsoleKernel
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// v2board
|
||||
$schedule->command('v2board:statistics')->daily();
|
||||
$schedule->command('v2board:statistics')->dailyAt('0:10');
|
||||
// check
|
||||
$schedule->command('check:order')->everyMinute();
|
||||
$schedule->command('check:commission')->everyMinute();
|
||||
|
@ -21,6 +21,17 @@ class ConfigController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function getThemeTemplate()
|
||||
{
|
||||
$path = public_path('theme/');
|
||||
$files = array_map(function ($item) use ($path) {
|
||||
return str_replace($path, '', $item);
|
||||
}, glob($path . '*'));
|
||||
return response([
|
||||
'data' => $files
|
||||
]);
|
||||
}
|
||||
|
||||
public function setTelegramWebhook(Request $request)
|
||||
{
|
||||
$telegramService = new TelegramService($request->input('telegram_bot_token'));
|
||||
@ -72,8 +83,10 @@ class ConfigController extends Controller
|
||||
'subscribe' => [
|
||||
'plan_change_enable' => (int)config('v2board.plan_change_enable', 1),
|
||||
'reset_traffic_method' => (int)config('v2board.reset_traffic_method', 0),
|
||||
'renew_reset_traffic_enable' => (int)config('v2board.renew_reset_traffic_enable', 0),
|
||||
'surplus_enable' => (int)config('v2board.surplus_enable', 1)
|
||||
'surplus_enable' => (int)config('v2board.surplus_enable', 1),
|
||||
'new_order_event_id' => (int)config('v2board.new_order_event_id', 0),
|
||||
'renew_order_event_id' => (int)config('v2board.renew_order_event_id', 0),
|
||||
'change_order_event_id' => (int)config('v2board.change_order_event_id', 0),
|
||||
],
|
||||
'pay' => [
|
||||
// alipay
|
||||
@ -107,6 +120,7 @@ class ConfigController extends Controller
|
||||
'epay_key' => config('v2board.epay_key'),
|
||||
],
|
||||
'frontend' => [
|
||||
'frontend_theme' => config('v2board.frontend_theme', 'v2board'),
|
||||
'frontend_theme_sidebar' => config('v2board.frontend_theme_sidebar', 'light'),
|
||||
'frontend_theme_header' => config('v2board.frontend_theme_header', 'dark'),
|
||||
'frontend_theme_color' => config('v2board.frontend_theme_color', 'default'),
|
||||
|
@ -77,12 +77,16 @@ class KnowledgeController extends Controller
|
||||
public function sort(KnowledgeSort $request)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($request->input('knowledge_ids') as $k => $v) {
|
||||
if (!Knowledge::find($v)->update(['sort' => $k + 1])) {
|
||||
$knowledge = Knowledge::find($v);
|
||||
$knowledge->timestamps = false;
|
||||
$knowledge->update(['sort' => $k + 1]);
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return response([
|
||||
'data' => true
|
||||
|
@ -52,8 +52,8 @@ class PlanController extends Controller
|
||||
// update user group id and transfer
|
||||
try {
|
||||
User::where('plan_id', $plan->id)->update([
|
||||
'group_id' => $plan->group_id,
|
||||
'transfer_enable' => $plan->transfer_enable * 1073741824
|
||||
'group_id' => $params['group_id'],
|
||||
'transfer_enable' => $params['transfer_enable'] * 1073741824
|
||||
]);
|
||||
$plan->update($params);
|
||||
} catch (\Exception $e) {
|
||||
|
@ -15,16 +15,8 @@ class ManageController extends Controller
|
||||
public function getNodes(Request $request)
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
$servers = array_merge(
|
||||
$serverService->getShadowsocksServers(),
|
||||
$serverService->getV2rayServers(),
|
||||
$serverService->getTrojanServers()
|
||||
);
|
||||
$serverService->mergeData($servers);
|
||||
$tmp = array_column($servers, 'sort');
|
||||
array_multisort($tmp, SORT_ASC, $servers);
|
||||
return response([
|
||||
'data' => $servers
|
||||
'data' => $serverService->getAllServers()
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,10 @@ use App\Http\Requests\Admin\UserGenerate;
|
||||
use App\Http\Requests\Admin\UserSendMail;
|
||||
use App\Http\Requests\Admin\UserUpdate;
|
||||
use App\Jobs\SendEmailJob;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -81,8 +81,12 @@ class UserController extends Controller
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = User::find($request->input('id'));
|
||||
if ($user->invite_user_id) {
|
||||
$user['invite_user'] = User::find($user->invite_user_id);
|
||||
}
|
||||
return response([
|
||||
'data' => User::find($request->input('id'))
|
||||
'data' => $user
|
||||
]);
|
||||
}
|
||||
|
||||
@ -109,6 +113,14 @@ class UserController extends Controller
|
||||
}
|
||||
$params['group_id'] = $plan->group_id;
|
||||
}
|
||||
if ($request->input('invite_user_email')) {
|
||||
$inviteUser = User::where('email', $request->input('invite_user_email'))->first();
|
||||
if ($inviteUser) {
|
||||
$params['invite_user_id'] = $inviteUser->id;
|
||||
}
|
||||
} else {
|
||||
$params['invite_user_id'] = null;
|
||||
}
|
||||
|
||||
try {
|
||||
$user->update($params);
|
||||
@ -265,30 +277,4 @@ class UserController extends Controller
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function setInviteUser(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'user_id' => 'required|integer',
|
||||
'invite_user' => 'required',
|
||||
], [
|
||||
'user_id.required' => '用户ID不能为空',
|
||||
'user_id.integer' => '用户ID参数有误',
|
||||
'invite_user.required' => '邀请人不能为空'
|
||||
]);
|
||||
|
||||
$user = User::find($request->input('user_id'));
|
||||
if (!$user) abort(500, '用户不存在');
|
||||
if (strpos($request->input('invite_user'), '@') !== -1) {
|
||||
$inviteUser = User::where('email', $request->input('invite_user'))->first();
|
||||
} else {
|
||||
$inviteUser = User::find($request->input('invite_user'));
|
||||
}
|
||||
if (!$inviteUser) abort(500, '邀请人不存在');
|
||||
$user->invite_user_id = $inviteUser->id;
|
||||
|
||||
return response([
|
||||
'data' => $user->save()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use App\Services\UserService;
|
||||
use App\Utils\Clash;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class AppController extends Controller
|
||||
@ -25,21 +26,27 @@ class AppController extends Controller
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAvailableServers($user);
|
||||
}
|
||||
$config = Yaml::parseFile(base_path() . '/resources/rules/app.clash.yaml');
|
||||
$defaultConfig = base_path() . '/resources/rules/app.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.app.clash.yaml';
|
||||
if (File::exists($customConfig)) {
|
||||
$config = Yaml::parseFile($customConfig);
|
||||
} else {
|
||||
$config = Yaml::parseFile($defaultConfig);
|
||||
}
|
||||
$proxy = [];
|
||||
$proxies = [];
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($proxy, Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, Clash::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxy, Protocols\Clash::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
}
|
||||
@ -84,62 +91,4 @@ class AppController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function config(Request $request)
|
||||
{
|
||||
if (empty($request->input('server_id'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$user = $request->user;
|
||||
if ($user->expired_at < time() && $user->expired_at !== NULL) {
|
||||
abort(500, '订阅计划已过期');
|
||||
}
|
||||
$server = Server::where('show', 1)
|
||||
->where('id', $request->input('server_id'))
|
||||
->first();
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
$json = json_decode(self::CLIENT_CONFIG);
|
||||
//socks
|
||||
$json->inbound->port = (int)self::SOCKS_PORT;
|
||||
//http
|
||||
$json->inboundDetour[0]->port = (int)self::HTTP_PORT;
|
||||
//other
|
||||
$json->outbound->settings->vnext[0]->address = (string)$server->host;
|
||||
$json->outbound->settings->vnext[0]->port = (int)$server->port;
|
||||
$json->outbound->settings->vnext[0]->users[0]->id = (string)$user->uuid;
|
||||
$json->outbound->settings->vnext[0]->users[0]->alterId = (int)$server->alter_id;
|
||||
$json->outbound->settings->vnext[0]->remark = (string)$server->name;
|
||||
$json->outbound->streamSettings->network = $server->network;
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp':
|
||||
$json->outbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'kcp':
|
||||
$json->outbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'ws':
|
||||
$json->outbound->streamSettings->wsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'http':
|
||||
$json->outbound->streamSettings->httpSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'domainsocket':
|
||||
$json->outbound->streamSettings->dsSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'quic':
|
||||
$json->outbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($request->input('is_global')) {
|
||||
$json->routing->settings->rules[0]->outboundTag = 'proxy';
|
||||
}
|
||||
if ($server->tls) {
|
||||
$json->outbound->streamSettings->security = "tls";
|
||||
}
|
||||
die(json_encode($json, JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
@ -2,18 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Client;
|
||||
|
||||
use App\Http\Controllers\Client\Protocols\V2rayN;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\ServerService;
|
||||
use App\Utils\Clash;
|
||||
use App\Utils\QuantumultX;
|
||||
use App\Utils\Shadowrocket;
|
||||
use App\Utils\Surge;
|
||||
use App\Utils\Surfboard;
|
||||
use App\Utils\URLSchemes;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use App\Utils\Helper;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use App\Services\UserService;
|
||||
|
||||
class ClientController extends Controller
|
||||
@ -32,251 +24,18 @@ class ClientController extends Controller
|
||||
$serverService = new ServerService();
|
||||
$servers = $serverService->getAvailableServers($user);
|
||||
if ($flag) {
|
||||
if (strpos($flag, 'quantumult%20x') !== false) {
|
||||
die($this->quantumultX($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'quantumult') !== false) {
|
||||
die($this->quantumult($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'clash') !== false) {
|
||||
die($this->clash($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'surfboard') !== false) {
|
||||
die($this->surfboard($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'surge') !== false) {
|
||||
die($this->surge($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'shadowrocket') !== false) {
|
||||
die($this->shadowrocket($user, $servers));
|
||||
}
|
||||
if (strpos($flag, 'shadowsocks') !== false) {
|
||||
die($this->shaodowsocksSIP008($user, $servers));
|
||||
foreach (glob(app_path('Http//Controllers//Client//Protocols') . '/*.php') as $file) {
|
||||
$file = 'App\\Http\\Controllers\\Client\\Protocols\\' . basename($file, '.php');
|
||||
$class = new $file($user, $servers);
|
||||
if (strpos($flag, $class->flag) !== false) {
|
||||
die($class->handle());
|
||||
}
|
||||
}
|
||||
die($this->origin($user, $servers));
|
||||
}
|
||||
// todo 1.5.3 remove
|
||||
$class = new V2rayN($user, $servers);
|
||||
die($class->handle());
|
||||
die('该客户端暂不支持进行订阅');
|
||||
}
|
||||
}
|
||||
// TODO: Ready to stop support
|
||||
private function quantumult($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
header('subscription-userinfo: upload=' . $user['u'] . '; download=' . $user['d'] . ';total=' . $user['transfer_enable']);
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$str = '';
|
||||
$str .= $item['name'] . '= vmess, ' . $item['host'] . ', ' . $item['port'] . ', chacha20-ietf-poly1305, "' . $user['uuid'] . '", over-tls=' . ($item['tls'] ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
|
||||
if ($item['network'] === 'ws') {
|
||||
$str .= ', obfs=ws';
|
||||
if ($item['networkSettings']) {
|
||||
$wsSettings = json_decode($item['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $str .= ', obfs-path="' . $wsSettings['path'] . '"';
|
||||
if (isset($wsSettings['headers']['Host'])) $str .= ', obfs-header="Host:' . $wsSettings['headers']['Host'] . '"';
|
||||
}
|
||||
}
|
||||
$uri .= "vmess://" . base64_encode($str) . "\r\n";
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function shadowrocket($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
//display remaining traffic and expire date
|
||||
$upload = round($user['u'] / (1024*1024*1024), 2);
|
||||
$download = round($user['d'] / (1024*1024*1024), 2);
|
||||
$totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
|
||||
$expiredDate = date('Y-m-d', $user['expired_at']);
|
||||
$uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= Shadowrocket::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= Shadowrocket::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= Shadowrocket::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function quantumultX($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= QuantumultX::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= QuantumultX::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= QuantumultX::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function origin($user, $servers = [])
|
||||
{
|
||||
$uri = '';
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= URLSchemes::buildShadowsocks($item, $user);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= URLSchemes::buildVmess($item, $user);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= URLSchemes::buildTrojan($item, $user);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
private function shaodowsocksSIP008($user, $servers = [])
|
||||
{
|
||||
$configs = [];
|
||||
$subs = [];
|
||||
$subs['servers'] = [];
|
||||
$subs['bytes_used'] = '';
|
||||
$subs['bytes_remaining'] = '';
|
||||
|
||||
$bytesUsed = $user['u'] + $user['d'];
|
||||
$bytesRemaining = $user['transfer_enable'] - $bytesUsed;
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($configs, URLSchemes::buildShadowsocksSIP008($item, $user));
|
||||
}
|
||||
}
|
||||
|
||||
$subs['version'] = 1;
|
||||
$subs['bytes_used'] = $bytesUsed;
|
||||
$subs['bytes_remaining'] = $bytesRemaining;
|
||||
$subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
|
||||
|
||||
return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
private function surge($user, $servers = [])
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
// [Proxy]
|
||||
$proxies .= Surge::buildTrojan($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function surfboard($user, $servers = [])
|
||||
{
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
private function clash($user, $servers = [])
|
||||
{
|
||||
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = Yaml::parseFile($customConfig);
|
||||
} else {
|
||||
$config = Yaml::parseFile($defaultConfig);
|
||||
}
|
||||
$proxy = [];
|
||||
$proxies = [];
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($proxy, Clash::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, Clash::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, Clash::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
}
|
||||
|
||||
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
|
||||
foreach ($config['proxy-groups'] as $k => $v) {
|
||||
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
}
|
||||
|
93
app/Http/Controllers/Client/Protocols/AnXray.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class AnXray
|
||||
{
|
||||
public $flag = 'anxray';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($uuid, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$uuid}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildShadowsocksSIP008($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $uuid,
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"encryption" => "none",
|
||||
"type" => urlencode($server['network']),
|
||||
"security" => $server['tls'] ? "tls" : "",
|
||||
"sni" => $server['tls'] ? urlencode(json_decode($server['tlsSettings'], true)['serverName']) : ""
|
||||
];
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = urlencode($wsSettings['path']);
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = urlencode($wsSettings['headers']['Host']);
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['serviceName'])) $config['serviceName'] = urlencode($grpcSettings['serviceName']);
|
||||
}
|
||||
return "vmess://" . $uuid . "@" . $server['host'] . ":" . $server['port'] . "?" . http_build_query($config) . "#" . urlencode($server['name']) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($uuid, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$uuid}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
}
|
@ -1,10 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class Clash
|
||||
{
|
||||
public $flag = 'clash';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = Yaml::parseFile($customConfig);
|
||||
} else {
|
||||
$config = Yaml::parseFile($defaultConfig);
|
||||
}
|
||||
$proxy = [];
|
||||
$proxies = [];
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($proxy, self::buildShadowsocks($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
array_push($proxy, self::buildVmess($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
array_push($proxy, self::buildTrojan($user['uuid'], $item));
|
||||
array_push($proxies, $item['name']);
|
||||
}
|
||||
}
|
||||
|
||||
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
|
||||
foreach ($config['proxy-groups'] as $k => $v) {
|
||||
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($uuid, $server)
|
||||
{
|
||||
$array = [];
|
||||
@ -50,6 +101,14 @@ class Clash
|
||||
$array['ws-headers'] = ['Host' => $wsSettings['headers']['Host']];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'grpc') {
|
||||
$array['network'] = 'grpc';
|
||||
if ($server['networkSettings']) {
|
||||
$grpcObject = json_decode($server['networkSettings'], true);
|
||||
$array['grpc-opts'] = [];
|
||||
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
96
app/Http/Controllers/Client/Protocols/Passwall.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class Passwall
|
||||
{
|
||||
public $flag = 'passwall';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class QuantumultX
|
||||
{
|
||||
public $flag = 'quantumult%20x';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
96
app/Http/Controllers/Client/Protocols/SSRPlus.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class SSRPlus
|
||||
{
|
||||
public $flag = 'ssrplus';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Shadowrocket
|
||||
{
|
||||
public $flag = 'shadowrocket';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$uri = '';
|
||||
//display remaining traffic and expire date
|
||||
$upload = round($user['u'] / (1024*1024*1024), 2);
|
||||
$download = round($user['d'] / (1024*1024*1024), 2);
|
||||
$totalTraffic = round($user['transfer_enable'] / (1024*1024*1024), 2);
|
||||
$expiredDate = date('Y-m-d', $user['expired_at']);
|
||||
$uri .= "STATUS=🚀↑:{$upload}GB,↓:{$download}GB,TOT:{$totalTraffic}GB💡Expires:{$expiredDate}\r\n";
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
@ -44,6 +80,19 @@ class Shadowrocket
|
||||
$config['obfsParam'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
}
|
||||
if ($server['network'] === 'grpc') {
|
||||
$config['obfs'] = "grpc";
|
||||
if ($server['networkSettings']) {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['serviceName']) && !empty($grpcSettings['serviceName']))
|
||||
$config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
if (isset($tlsSettings)) {
|
||||
$config['host'] = $tlsSettings['serverName'];
|
||||
} else {
|
||||
$config['host'] = $server['host'];
|
||||
}
|
||||
}
|
||||
$query = http_build_query($config, '', '&', PHP_QUERY_RFC3986);
|
||||
$uri = "vmess://{$userinfo}?{$query}";
|
||||
$uri .= "\r\n";
|
57
app/Http/Controllers/Client/Protocols/Shadowsocks.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Shadowsocks
|
||||
{
|
||||
public $flag = 'shadowsocks';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$configs = [];
|
||||
$subs = [];
|
||||
$subs['servers'] = [];
|
||||
$subs['bytes_used'] = '';
|
||||
$subs['bytes_remaining'] = '';
|
||||
|
||||
$bytesUsed = $user['u'] + $user['d'];
|
||||
$bytesRemaining = $user['transfer_enable'] - $bytesUsed;
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
array_push($configs, self::SIP008($item, $user));
|
||||
}
|
||||
}
|
||||
|
||||
$subs['version'] = 1;
|
||||
$subs['bytes_used'] = $bytesUsed;
|
||||
$subs['bytes_remaining'] = $bytesRemaining;
|
||||
$subs['servers'] = array_merge($subs['servers'] ? $subs['servers'] : [], $configs);
|
||||
|
||||
return json_encode($subs, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT);
|
||||
}
|
||||
|
||||
public static function SIP008($server, $user)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $user['uuid'],
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
}
|
@ -1,10 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class Surfboard
|
||||
{
|
||||
public $flag = 'surfboard';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surfboard.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surfboard.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
@ -1,10 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class Surge
|
||||
{
|
||||
public $flag = 'surge';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
|
||||
$proxies = '';
|
||||
$proxyGroup = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildVmess($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
// [Proxy]
|
||||
$proxies .= self::buildTrojan($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
}
|
||||
|
||||
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
|
||||
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
|
||||
if (\File::exists($customConfig)) {
|
||||
$config = file_get_contents("$customConfig");
|
||||
} else {
|
||||
$config = file_get_contents("$defaultConfig");
|
||||
}
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
}
|
||||
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$config = [
|
96
app/Http/Controllers/Client/Protocols/V2rayN.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class V2rayN
|
||||
{
|
||||
public $flag = 'v2rayn';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
}
|
97
app/Http/Controllers/Client/Protocols/V2rayNG.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
|
||||
class V2rayNG
|
||||
{
|
||||
public $flag = 'v2rayng';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
public function __construct($user, $servers)
|
||||
{
|
||||
$this->user = $user;
|
||||
$this->servers = $servers;
|
||||
}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$uri = '';
|
||||
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'v2ray') {
|
||||
$uri .= self::buildVmess($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
$uri .= self::buildShadowsocks($user['uuid'], $item);
|
||||
}
|
||||
if ($item['type'] === 'trojan') {
|
||||
$uri .= self::buildTrojan($user['uuid'], $item);
|
||||
}
|
||||
}
|
||||
return base64_encode($uri);
|
||||
}
|
||||
|
||||
public static function buildShadowsocks($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$password}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildVmess($uuid, $server)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $uuid,
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : "",
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
if ((string)$server['network'] === 'grpc') {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($grpcSettings['path'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$password}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -11,8 +11,26 @@ class CommController extends Controller
|
||||
{
|
||||
return response([
|
||||
'data' => [
|
||||
'tos_url' => config('v2board.tos_url')
|
||||
'tos_url' => config('v2board.tos_url'),
|
||||
'is_email_verify' => (int)config('v2board.email_verify', 0) ? 1 : 0,
|
||||
'is_invite_force' => (int)config('v2board.invite_force', 0) ? 1 : 0,
|
||||
'email_whitelist_suffix' => (int)config('v2board.email_whitelist_enable', 0)
|
||||
? $this->getEmailSuffix()
|
||||
: 0,
|
||||
'is_recaptcha' => (int)config('v2board.recaptcha_enable', 0) ? 1 : 0,
|
||||
'recaptcha_site_key' => config('v2board.recaptcha_site_key'),
|
||||
'app_description' => config('v2board.app_description'),
|
||||
'app_url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function getEmailSuffix()
|
||||
{
|
||||
$suffix = config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT);
|
||||
if (!is_array($suffix)) {
|
||||
return preg_split('/,/', $suffix);
|
||||
}
|
||||
return $suffix;
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class PaymentController extends Controller
|
||||
if (!$this->handle($verify['trade_no'], $verify['callback_no'])) {
|
||||
abort(500, 'handle error');
|
||||
}
|
||||
die('success');
|
||||
die(isset($paymentService->customResult) ? $paymentService->customResult : 'success');
|
||||
} catch (\Exception $e) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
|
@ -193,6 +193,7 @@ class TelegramController extends Controller
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
$telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
|
||||
$telegramService->sendMessageWithAdmin("#`{$ticketId}` 的工单已由 {$user->email} 进行回复", true);
|
||||
}
|
||||
|
||||
|
||||
|
@ -24,7 +24,7 @@ class AuthController extends Controller
|
||||
$recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
|
||||
$recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
|
||||
if (!$recaptchaResp->isSuccess()) {
|
||||
abort(500, '验证码有误');
|
||||
abort(500, __('Invalid code is incorrect'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_whitelist_enable', 0)) {
|
||||
@ -32,36 +32,36 @@ class AuthController extends Controller
|
||||
$request->input('email'),
|
||||
config('v2board.email_whitelist_suffix', Dict::EMAIL_WHITELIST_SUFFIX_DEFAULT))
|
||||
) {
|
||||
abort(500, '邮箱后缀不处于白名单中');
|
||||
abort(500, __('Email suffix is not in the Whitelist'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_gmail_limit_enable', 0)) {
|
||||
$prefix = explode('@', $request->input('email'))[0];
|
||||
if (strpos($prefix, '.') !== false || strpos($prefix, '+') !== false) {
|
||||
abort(500, '不支持Gmail别名邮箱');
|
||||
abort(500, __('Gmail alias is not supported'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.stop_register', 0)) {
|
||||
abort(500, '本站已关闭注册');
|
||||
abort(500, __('Registration has closed'));
|
||||
}
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
if (empty($request->input('invite_code'))) {
|
||||
abort(500, '必须使用邀请码才可以注册');
|
||||
abort(500, __('You must use the invitation code to register'));
|
||||
}
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
if (empty($request->input('email_code'))) {
|
||||
abort(500, '邮箱验证码不能为空');
|
||||
abort(500, __('Email verification code cannot be empty'));
|
||||
}
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
abort(500, __('Incorrect email verification code'));
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
$password = $request->input('password');
|
||||
$exist = User::where('email', $email)->first();
|
||||
if ($exist) {
|
||||
abort(500, '邮箱已存在系统中');
|
||||
abort(500, __('Email already exists'));
|
||||
}
|
||||
$user = new User();
|
||||
$user->email = $email;
|
||||
@ -74,7 +74,7 @@ class AuthController extends Controller
|
||||
->first();
|
||||
if (!$inviteCode) {
|
||||
if ((int)config('v2board.invite_force', 0)) {
|
||||
abort(500, '邀请码无效');
|
||||
abort(500, __('Invalid invitation code'));
|
||||
}
|
||||
} else {
|
||||
$user->invite_user_id = $inviteCode->user_id ? $inviteCode->user_id : null;
|
||||
@ -97,7 +97,7 @@ class AuthController extends Controller
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
abort(500, '注册失败');
|
||||
abort(500, __('Register failed'));
|
||||
}
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
@ -116,18 +116,18 @@ class AuthController extends Controller
|
||||
|
||||
$user = User::where('email', $email)->first();
|
||||
if (!$user) {
|
||||
abort(500, '用户名或密码错误');
|
||||
abort(500, __('Incorrect email or password'));
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$password,
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, '用户名或密码错误');
|
||||
abort(500, __('Incorrect email or password'));
|
||||
}
|
||||
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
abort(500, __('Your account has been suspended'));
|
||||
}
|
||||
|
||||
$data = [
|
||||
@ -165,14 +165,14 @@ class AuthController extends Controller
|
||||
$key = CacheKey::get('TEMP_TOKEN', $request->input('verify'));
|
||||
$userId = Cache::get($key);
|
||||
if (!$userId) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
$user = User::find($userId);
|
||||
if (!$user) {
|
||||
abort(500, '用户不存在');
|
||||
abort(500, __('The user does not '));
|
||||
}
|
||||
if ($user->banned) {
|
||||
abort(500, '该账户已被停止使用');
|
||||
abort(500, __('Your account has been suspended'));
|
||||
}
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
@ -190,7 +190,7 @@ class AuthController extends Controller
|
||||
{
|
||||
$user = User::where('token', $request->input('token'))->first();
|
||||
if (!$user) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
@ -204,11 +204,12 @@ 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'));
|
||||
$user = User::where('email', $authData[0])
|
||||
->where('password', $authData[1])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, '令牌有误');
|
||||
abort(500, __('Token error'));
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
@ -241,16 +242,16 @@ class AuthController extends Controller
|
||||
public function forget(AuthForget $request)
|
||||
{
|
||||
if (Cache::get(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email'))) !== $request->input('email_code')) {
|
||||
abort(500, '邮箱验证码有误');
|
||||
abort(500, __('Incorrect email verification code'));
|
||||
}
|
||||
$user = User::where('email', $request->input('email'))->first();
|
||||
if (!$user) {
|
||||
abort(500, '该邮箱不存在系统中');
|
||||
abort(500, __('This email is not registered in the system'));
|
||||
}
|
||||
$user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, '重置失败');
|
||||
abort(500, __('Reset failed'));
|
||||
}
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
return response([
|
||||
|
@ -17,6 +17,7 @@ use ReCaptcha\ReCaptcha;
|
||||
|
||||
class CommController extends Controller
|
||||
{
|
||||
// TODO: remove on 1.5.5
|
||||
public function config()
|
||||
{
|
||||
return response([
|
||||
@ -47,15 +48,15 @@ class CommController extends Controller
|
||||
$recaptcha = new ReCaptcha(config('v2board.recaptcha_key'));
|
||||
$recaptchaResp = $recaptcha->verify($request->input('recaptcha_data'));
|
||||
if (!$recaptchaResp->isSuccess()) {
|
||||
abort(500, '验证码有误');
|
||||
abort(500, __('Invalid code is incorrect'));
|
||||
}
|
||||
}
|
||||
$email = $request->input('email');
|
||||
if (Cache::get(CacheKey::get('LAST_SEND_EMAIL_VERIFY_TIMESTAMP', $email))) {
|
||||
abort(500, '验证码已发送,请过一会再请求');
|
||||
abort(500, __('Email verification code has been sent, please request again later'));
|
||||
}
|
||||
$code = rand(100000, 999999);
|
||||
$subject = config('v2board.app_name', 'V2Board') . '邮箱验证码';
|
||||
$subject = config('v2board.app_name', 'V2Board') . __('Email verification code');
|
||||
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $email,
|
||||
|
@ -11,25 +11,25 @@ class CouponController extends Controller
|
||||
public function check(Request $request)
|
||||
{
|
||||
if (empty($request->input('code'))) {
|
||||
abort(500, __('user.coupon.check.coupon_not_empty'));
|
||||
abort(500, __('Coupon cannot be empty'));
|
||||
}
|
||||
$coupon = Coupon::where('code', $request->input('code'))->first();
|
||||
if (!$coupon) {
|
||||
abort(500, __('user.coupon.check.coupon_invalid'));
|
||||
abort(500, __('Invalid coupon'));
|
||||
}
|
||||
if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
|
||||
abort(500, __('user.coupon.check.coupon_not_available_by_number'));
|
||||
abort(500, __('This coupon is no longer available'));
|
||||
}
|
||||
if (time() < $coupon->started_at) {
|
||||
abort(500, __('user.coupon.check.coupon_not_available_by_time'));
|
||||
abort(500, __('This coupon has not yet started'));
|
||||
}
|
||||
if (time() > $coupon->ended_at) {
|
||||
abort(500, __('user.coupon.check.coupon_expired'));
|
||||
abort(500, __('This coupon has expired'));
|
||||
}
|
||||
if ($coupon->limit_plan_ids) {
|
||||
$limitPlanIds = json_decode($coupon->limit_plan_ids);
|
||||
if (!in_array($request->input('plan_id'), $limitPlanIds)) {
|
||||
abort(500, __('user.coupon.check.coupon_limit_plan'));
|
||||
abort(500, __('The coupon code cannot be used for this subscription'));
|
||||
}
|
||||
}
|
||||
return response([
|
||||
|
@ -14,7 +14,7 @@ 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)) {
|
||||
abort(500, __('user.invite.save.invite_create_limit'));
|
||||
abort(500, __('The maximum number of creations has been reached'));
|
||||
}
|
||||
$inviteCode = new InviteCode();
|
||||
$inviteCode->user_id = $request->session()->get('id');
|
||||
|
@ -17,18 +17,23 @@ class KnowledgeController extends Controller
|
||||
->where('show', 1)
|
||||
->first()
|
||||
->toArray();
|
||||
if (!$knowledge) abort(500, __('user.knowledge.fetch.knowledge_not_exist'));
|
||||
if (!$knowledge) abort(500, __('Article does not exist'));
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$userService = new UserService();
|
||||
if ($userService->isAvailable($user)) {
|
||||
$appleId = config('v2board.apple_id');
|
||||
$appleIdPassword = config('v2board.apple_id_password');
|
||||
} else {
|
||||
$appleId = __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$appleIdPassword = __('user.knowledge.fetch.apple_id_must_be_plan');
|
||||
$appleId = __('No active subscription. Unable to use our provided Apple ID');
|
||||
$appleIdPassword = __('No active subscription. Unable to use our provided Apple ID');
|
||||
$this->formatAccessData($knowledge['body']);
|
||||
}
|
||||
$subscribeUrl = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$subscribeUrl = config('v2board.app_url', env('APP_URL'));
|
||||
$subscribeUrls = explode(',', config('v2board.subscribe_url'));
|
||||
if ($subscribeUrls) {
|
||||
$subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
|
||||
}
|
||||
$subscribeUrl = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
|
||||
$knowledge['body'] = str_replace('{{siteName}}', config('v2board.app_name', 'V2Board'), $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{appleId}}', $appleId, $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{appleIdPassword}}', $appleIdPassword, $knowledge['body']);
|
||||
@ -63,7 +68,7 @@ class KnowledgeController extends Controller
|
||||
function getBetween($input, $start, $end){$substr = substr($input, strlen($start)+strpos($input, $start),(strlen($input) - strpos($input, $end))*(-1));return $substr;}
|
||||
$accessData = getBetween($body, '<!--access start-->', '<!--access end-->');
|
||||
if ($accessData) {
|
||||
$body = str_replace($accessData, '<div class="v2board-no-access">'. __('user.knowledge.formatAccessData.no_access') .'</div>', $body);
|
||||
$body = str_replace($accessData, '<div class="v2board-no-access">'. __('You must have a valid subscription to view content in this area') .'</div>', $body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -52,12 +52,12 @@ class OrderController extends Controller
|
||||
->where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.details.order_not_exist'));
|
||||
abort(500, __('Order does not exist or has been paid'));
|
||||
}
|
||||
$order['plan'] = Plan::find($order->plan_id);
|
||||
$order['try_out_plan_id'] = (int)config('v2board.try_out_plan_id');
|
||||
if (!$order['plan']) {
|
||||
abort(500, __('user.order.details.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $order
|
||||
@ -68,38 +68,38 @@ class OrderController extends Controller
|
||||
{
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($request->session()->get('id'))) {
|
||||
abort(500, __('user.order.save.exist_open_order'));
|
||||
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'));
|
||||
|
||||
if (!$plan) {
|
||||
abort(500, __('user.order.save.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
|
||||
if ((!$plan->show && !$plan->renew) || (!$plan->show && $user->plan_id !== $plan->id)) {
|
||||
if ($request->input('cycle') !== 'reset_price') {
|
||||
abort(500, __('user.order.save.plan_stop_sell'));
|
||||
abort(500, __('This subscription has been sold out, please choose another subscription'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->renew && $user->plan_id == $plan->id && $request->input('cycle') !== 'reset_price') {
|
||||
abort(500, __('user.order.save.plan_stop_renew'));
|
||||
abort(500, __('This subscription cannot be renewed, please change to another subscription'));
|
||||
}
|
||||
|
||||
if ($plan[$request->input('cycle')] === NULL) {
|
||||
abort(500, __('user.order.save.plan_stop'));
|
||||
abort(500, __('This payment cycle cannot be purchased, please choose another cycle'));
|
||||
}
|
||||
|
||||
if ($request->input('cycle') === 'reset_price') {
|
||||
if ($user->expired_at <= time() || !$user->plan_id) {
|
||||
abort(500, __('user.order.save.plan_exist_not_buy_package'));
|
||||
abort(500, __('Subscription has expired or no active subscription, unable to purchase Data Reset Package'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$plan->show && $plan->renew && !$userService->isAvailable($user)) {
|
||||
abort(500, __('user.order.save.plan_expired'));
|
||||
abort(500, __('This subscription has expired, please change to another subscription'));
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
@ -115,7 +115,7 @@ class OrderController extends Controller
|
||||
$couponService = new CouponService($request->input('coupon_code'));
|
||||
if (!$couponService->use($order)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.coupon_use_failed'));
|
||||
abort(500, __('Coupon failed'));
|
||||
}
|
||||
$order->coupon_id = $couponService->getId();
|
||||
}
|
||||
@ -130,14 +130,14 @@ class OrderController extends Controller
|
||||
if ($remainingBalance > 0) {
|
||||
if (!$userService->addBalance($order->user_id, - $order->total_amount)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.insufficient_balance'));
|
||||
abort(500, __('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $order->total_amount;
|
||||
$order->total_amount = 0;
|
||||
} else {
|
||||
if (!$userService->addBalance($order->user_id, - $user->balance)) {
|
||||
DB::rollBack();
|
||||
abort(500, __('user.order.save.insufficient_balance'));
|
||||
abort(500, __('Insufficient balance'));
|
||||
}
|
||||
$order->balance_amount = $user->balance;
|
||||
$order->total_amount = $order->total_amount - $user->balance;
|
||||
@ -146,7 +146,7 @@ class OrderController extends Controller
|
||||
|
||||
if (!$order->save()) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.order.save.order_create_failed'));
|
||||
abort(500, __('Failed to create order'));
|
||||
}
|
||||
|
||||
DB::commit();
|
||||
@ -165,7 +165,7 @@ class OrderController extends Controller
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.checkout.order_not_exist_or_paid'));
|
||||
abort(500, __('Order does not exist or has been paid'));
|
||||
}
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
@ -178,7 +178,7 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
$payment = Payment::find($method);
|
||||
if (!$payment || $payment->enable !== 1) abort(500, __('user.order.checkout.pay_method_not_use'));
|
||||
if (!$payment || $payment->enable !== 1) abort(500, __('Payment method is not available'));
|
||||
$paymentService = new PaymentService($payment->payment, $payment->id);
|
||||
$result = $paymentService->pay([
|
||||
'trade_no' => $tradeNo,
|
||||
@ -200,7 +200,7 @@ class OrderController extends Controller
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.check.order_not_exist'));
|
||||
abort(500, __('Order does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $order->status
|
||||
@ -224,20 +224,20 @@ class OrderController extends Controller
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, __('user.order.cancel.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, __('user.order.cancel.order_not_exist'));
|
||||
abort(500, __('Order does not exist'));
|
||||
}
|
||||
if ($order->status !== 0) {
|
||||
abort(500, __('user.order.cancel.only_cancel_pending_order'));
|
||||
abort(500, __('You can only cancel pending orders'));
|
||||
}
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
abort(500, __('user.order.cancel.cancel_failed'));
|
||||
abort(500, __('Cancel failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
|
@ -14,7 +14,7 @@ class PlanController extends Controller
|
||||
$plan = Plan::where('id', $request->input('id'))
|
||||
->first();
|
||||
if (!$plan) {
|
||||
abort(500, __('user.plan.fetch.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
return response([
|
||||
'data' => $plan
|
||||
|
@ -3,7 +3,9 @@
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Services\TelegramService;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TelegramController extends Controller
|
||||
{
|
||||
@ -17,4 +19,9 @@ class TelegramController extends Controller
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
public function unbind(Request $request)
|
||||
{
|
||||
$user = User::where('user_id', $request->session()->get('id'))->first();
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class TicketController extends Controller
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.fetch.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
$ticket['message'] = TicketMessage::where('ticket_id', $ticket->id)->get();
|
||||
for ($i = 0; $i < count($ticket['message']); $i++) {
|
||||
@ -56,7 +56,7 @@ class TicketController extends Controller
|
||||
{
|
||||
DB::beginTransaction();
|
||||
if ((int)Ticket::where('status', 0)->where('user_id', $request->session()->get('id'))->count()) {
|
||||
abort(500, __('user.ticket.save.exist_other_open_ticket'));
|
||||
abort(500, __('There are other unresolved tickets'));
|
||||
}
|
||||
$ticket = Ticket::create(array_merge($request->only([
|
||||
'subject',
|
||||
@ -67,7 +67,7 @@ class TicketController extends Controller
|
||||
]));
|
||||
if (!$ticket) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.save.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->session()->get('id'),
|
||||
@ -76,7 +76,7 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticketMessage) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.save.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
@ -88,22 +88,22 @@ class TicketController extends Controller
|
||||
public function reply(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, __('user.ticket.reply.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
if (empty($request->input('message'))) {
|
||||
abort(500, __('user.ticket.reply.message_not_empty'));
|
||||
abort(500, __('Message cannot be empty'));
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.reply.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
if ($ticket->status) {
|
||||
abort(500, __('user.ticket.reply.ticket_close_not_reply'));
|
||||
abort(500, __('The ticket is closed and cannot be replied'));
|
||||
}
|
||||
if ($request->session()->get('id') == $this->getLastMessage($ticket->id)->user_id) {
|
||||
abort(500, __('user.ticket.reply.wait_reply'));
|
||||
abort(500, __('Please wait for the technical enginneer to reply'));
|
||||
}
|
||||
DB::beginTransaction();
|
||||
$ticketMessage = TicketMessage::create([
|
||||
@ -114,7 +114,7 @@ class TicketController extends Controller
|
||||
$ticket->last_reply_user_id = $request->session()->get('id');
|
||||
if (!$ticketMessage || !$ticket->save()) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.reply.ticket_reply_failed'));
|
||||
abort(500, __('Ticket reply failed'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
@ -127,17 +127,17 @@ class TicketController extends Controller
|
||||
public function close(Request $request)
|
||||
{
|
||||
if (empty($request->input('id'))) {
|
||||
abort(500, __('user.ticket.close.params_wrong'));
|
||||
abort(500, __('Invalid parameter'));
|
||||
}
|
||||
$ticket = Ticket::where('id', $request->input('id'))
|
||||
->where('user_id', $request->session()->get('id'))
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
abort(500, __('user.ticket.close.ticket_not_exist'));
|
||||
abort(500, __('Ticket does not exist'));
|
||||
}
|
||||
$ticket->status = 1;
|
||||
if (!$ticket->save()) {
|
||||
abort(500, __('user.ticket.close.close_failed'));
|
||||
abort(500, __('Close failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
@ -163,15 +163,15 @@ class TicketController extends Controller
|
||||
Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT
|
||||
)
|
||||
)) {
|
||||
abort(500, __('user.ticket.withdraw.not_support_withdraw_method'));
|
||||
abort(500, __('Unsupported withdrawal method'));
|
||||
}
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$limit = config('v2board.commission_withdraw_limit', 100);
|
||||
if ($limit > ($user->commission_balance / 100)) {
|
||||
abort(500, __('user.ticket.withdraw.system_require_withdraw_limit', ['limit' => $limit]));
|
||||
abort(500, __('The current required minimum withdrawal commission is', ['limit' => $limit]));
|
||||
}
|
||||
DB::beginTransaction();
|
||||
$subject = __('user.ticket.withdraw.ticket_subject');
|
||||
$subject = __('[Commission Withdrawal Request] This ticket is opened by the system');
|
||||
$ticket = Ticket::create([
|
||||
'subject' => $subject,
|
||||
'level' => 2,
|
||||
@ -180,12 +180,12 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticket) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
$message = __('user.ticket.withdraw.ticket_message', [
|
||||
'method' => $request->input('withdraw_method'),
|
||||
'account' => $request->input('withdraw_account')
|
||||
]);
|
||||
$message = sprintf("%s\r\n%s",
|
||||
__('Withdrawal method') . ":" . $request->input('withdraw_method'),
|
||||
__('Withdrawal account') . ":" . $request->input('withdraw_account')
|
||||
);
|
||||
$ticketMessage = TicketMessage::create([
|
||||
'user_id' => $request->session()->get('id'),
|
||||
'ticket_id' => $ticket->id,
|
||||
@ -193,7 +193,7 @@ class TicketController extends Controller
|
||||
]);
|
||||
if (!$ticketMessage) {
|
||||
DB::rollback();
|
||||
abort(500, __('user.ticket.withdraw.ticket_create_failed'));
|
||||
abort(500, __('Failed to open ticket'));
|
||||
}
|
||||
DB::commit();
|
||||
$this->sendNotify($ticket, $ticketMessage);
|
||||
|
@ -29,19 +29,19 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.changePassword.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$request->input('old_password'),
|
||||
$user->password)
|
||||
) {
|
||||
abort(500, __('user.user.changePassword.old_password_wrong'));
|
||||
abort(500, __('The old password is wrong'));
|
||||
}
|
||||
$user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.changePassword.save_failed'));
|
||||
abort(500, __('Save failed'));
|
||||
}
|
||||
$request->session()->flush();
|
||||
return response([
|
||||
@ -70,7 +70,7 @@ class UserController extends Controller
|
||||
])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.info.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
$user['avatar_url'] = 'https://cdn.v2ex.com/gravatar/' . md5($user->email) . '?s=64&d=identicon';
|
||||
return response([
|
||||
@ -110,15 +110,20 @@ class UserController extends Controller
|
||||
])
|
||||
->first();
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.getSubscribe.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if ($user->plan_id) {
|
||||
$user['plan'] = Plan::find($user->plan_id);
|
||||
if (!$user['plan']) {
|
||||
abort(500, __('user.user.getSubscribe.plan_not_exist'));
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
}
|
||||
$user['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$subscribeUrl = config('v2board.app_url', env('APP_URL'));
|
||||
$subscribeUrls = explode(',', config('v2board.subscribe_url'));
|
||||
if ($subscribeUrls) {
|
||||
$subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
|
||||
}
|
||||
$user['subscribe_url'] = "{$subscribeUrl}/api/v1/client/subscribe?token={$user['token']}";
|
||||
$user['reset_day'] = $this->getResetDay($user);
|
||||
return response([
|
||||
'data' => $user
|
||||
@ -129,12 +134,12 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.resetSecurity.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
$user->uuid = Helper::guid(true);
|
||||
$user->token = Helper::guid();
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.resetSecurity.reset_failed'));
|
||||
abort(500, __('Reset failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user->token
|
||||
@ -150,12 +155,12 @@ class UserController extends Controller
|
||||
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.update.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
try {
|
||||
$user->update($updateData);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, __('user.user.update.save_failed'));
|
||||
abort(500, __('Save failed'));
|
||||
}
|
||||
|
||||
return response([
|
||||
@ -167,15 +172,15 @@ class UserController extends Controller
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('user.user.transfer.user_not_exist'));
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
if ($request->input('transfer_amount') > $user->commission_balance) {
|
||||
abort(500, __('user.user.transfer.insufficient_commission_balance'));
|
||||
abort(500, __('Insufficient commission balance'));
|
||||
}
|
||||
$user->commission_balance = $user->commission_balance - $request->input('transfer_amount');
|
||||
$user->balance = $user->balance + $request->input('transfer_amount');
|
||||
if (!$user->save()) {
|
||||
abort(500, __('user.user.transfer.transfer_failed'));
|
||||
abort(500, __('Transfer failed'));
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
|
@ -17,20 +17,14 @@ class User
|
||||
{
|
||||
if ($request->input('auth_data')) {
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入');
|
||||
$user = \App\Models\User::where('password', $authData[1])
|
||||
->where('email', $authData[0])
|
||||
->first();
|
||||
if ($user) {
|
||||
if (!$user) abort(403, '鉴权失败,请重新登入');
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
}
|
||||
}
|
||||
// if ($request->input('lang')) {
|
||||
// $request->session()->put('lang', $request->input('lang'));
|
||||
// }
|
||||
// if ($request->session()->get('lang')) {
|
||||
// App::setLocale($request->session()->get('lang'));
|
||||
// }
|
||||
if (!$request->session()->get('id')) {
|
||||
abort(403, '未登录或登陆已过期');
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class ConfigSave extends FormRequest
|
||||
'app_name' => '',
|
||||
'app_description' => '',
|
||||
'app_url' => 'nullable|url',
|
||||
'subscribe_url' => 'nullable|url',
|
||||
'subscribe_url' => 'nullable',
|
||||
'try_out_enable' => 'in:0,1',
|
||||
'try_out_plan_id' => 'integer',
|
||||
'try_out_hour' => 'numeric',
|
||||
@ -45,8 +45,10 @@ class ConfigSave extends FormRequest
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1',
|
||||
'renew_reset_traffic_enable' => 'in:0,1',
|
||||
'surplus_enable' => 'in:0,1',
|
||||
'new_order_event_id' => 'in:0,1',
|
||||
'renew_order_event_id' => 'in:0,1',
|
||||
'change_order_event_id' => 'in:0,1',
|
||||
// server
|
||||
'server_token' => 'nullable|min:16',
|
||||
'server_license' => 'nullable',
|
||||
@ -83,6 +85,7 @@ class ConfigSave extends FormRequest
|
||||
'epay_pid' => '',
|
||||
'epay_key' => '',
|
||||
// frontend
|
||||
'frontend_theme' => '',
|
||||
'frontend_theme_sidebar' => 'in:dark,light',
|
||||
'frontend_theme_header' => 'in:dark,light',
|
||||
'frontend_theme_color' => 'in:default,darkblue,black',
|
||||
|
@ -25,7 +25,7 @@ class ServerV2raySave extends FormRequest
|
||||
'tags' => 'nullable|array',
|
||||
'rate' => 'required|numeric',
|
||||
'alter_id' => 'required|integer',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
|
||||
'networkSettings' => '',
|
||||
'ruleSettings' => '',
|
||||
'tlsSettings' => '',
|
||||
|
@ -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',
|
||||
'filter.*.key' => 'required|in:id,email,transfer_enable,d,expired_at,uuid,token,invite_by_email,invite_user_id,plan_id,banned,remarks',
|
||||
'filter.*.condition' => 'required|in:>,<,=,>=,<=,模糊,!=',
|
||||
'filter.*.value' => 'required'
|
||||
];
|
||||
|
@ -27,6 +27,7 @@ class UserUpdate extends FormRequest
|
||||
'u' => 'integer',
|
||||
'd' => 'integer',
|
||||
'balance' => 'integer',
|
||||
'commission_type' => 'integer',
|
||||
'commission_balance' => 'integer',
|
||||
'remarks' => 'nullable'
|
||||
];
|
||||
|
@ -23,11 +23,11 @@ class AuthForget extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数',
|
||||
'email_code.required' => '邮箱验证码不能为空'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits'),
|
||||
'email_code.required' => __('Email verification code cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class AuthLogin extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class AuthRegister extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确',
|
||||
'password.required' => '密码不能为空',
|
||||
'password.min' => '密码必须大于8位数'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect'),
|
||||
'password.required' => __('Password can not be empty'),
|
||||
'password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ class CommSendEmailVerify extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'email.required' => '邮箱不能为空',
|
||||
'email.email' => '邮箱格式不正确'
|
||||
'email.required' => __('Email can not be empty'),
|
||||
'email.email' => __('Email format is incorrect')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class OrderSave extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'plan_id.required' => '套餐ID不能为空',
|
||||
'cycle.required' => '套餐周期不能为空',
|
||||
'cycle.in' => '套餐周期有误'
|
||||
'plan_id.required' => __('Plan ID cannot be empty'),
|
||||
'cycle.required' => __('Plan cycle cannot be empty'),
|
||||
'cycle.in' => __('Wrong plan cycle')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,10 +23,10 @@ class TicketSave extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'subject.required' => '工单主题不能为空',
|
||||
'level.required' => '工单级别不能为空',
|
||||
'level.in' => '工单级别格式不正确',
|
||||
'message.required' => '消息不能为空'
|
||||
'subject.required' => __('Ticket subject cannot be empty'),
|
||||
'level.required' => __('Ticket level cannot be empty'),
|
||||
'level.in' => __('Incorrect ticket level format'),
|
||||
'message.required' => __('Message cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,8 @@ class TicketWithdraw extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'withdraw_method.required' => '提现方式不能为空',
|
||||
'withdraw_method.in' => '提现方式不支持',
|
||||
'withdraw_account.required' => '提现账号不能为空'
|
||||
'withdraw_method.required' => __('The withdrawal method cannot be empty'),
|
||||
'withdraw_account.required' => __('The withdrawal account cannot be empty')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,9 +22,9 @@ class UserChangePassword extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'old_password.required' => '旧密码不能为空',
|
||||
'new_password.required' => '新密码不能为空',
|
||||
'new_password.min' => '密码必须大于8位数'
|
||||
'old_password.required' => __('Old password cannot be empty'),
|
||||
'new_password.required' => __('New password cannot be empty'),
|
||||
'new_password.min' => __('Password must be greater than 8 digits')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -21,9 +21,9 @@ class UserTransfer extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'transfer_amount.required' => '划转金额不能为空',
|
||||
'transfer_amount.integer' => __('user.user.transfer.params_wrong'),
|
||||
'transfer_amount.min' => __('user.user.transfer.params_wrong')
|
||||
'transfer_amount.required' => __('The transfer amount cannot be empty'),
|
||||
'transfer_amount.integer' => __('The transfer amount parameter is wrong'),
|
||||
'transfer_amount.min' => __('The transfer amount parameter is wrong')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@ class UserUpdate extends FormRequest
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'show.in' => '过期提醒格式不正确',
|
||||
'renew.in' => '流量提醒格式不正确'
|
||||
'show.in' => __('Incorrect format of expiration reminder'),
|
||||
'renew.in' => __('Incorrect traffic alert format')
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ class AdminRoute
|
||||
$router->get ('/config/fetch', 'Admin\\ConfigController@fetch');
|
||||
$router->post('/config/save', 'Admin\\ConfigController@save');
|
||||
$router->get ('/config/getEmailTemplate', 'Admin\\ConfigController@getEmailTemplate');
|
||||
$router->get ('/config/getThemeTemplate', 'Admin\\ConfigController@getThemeTemplate');
|
||||
$router->post('/config/setTelegramWebhook', 'Admin\\ConfigController@setTelegramWebhook');
|
||||
// Plan
|
||||
$router->get ('/plan/fetch', 'Admin\\PlanController@fetch');
|
||||
|
84
app/Payments/WechatPayNative.php
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Payments;
|
||||
|
||||
use Omnipay\Omnipay;
|
||||
use Omnipay\WechatPay\Helper;
|
||||
|
||||
class WechatPayNative {
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->customResult = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
|
||||
}
|
||||
|
||||
public function form()
|
||||
{
|
||||
return [
|
||||
'app_id' => [
|
||||
'label' => 'APPID',
|
||||
'description' => '绑定微信支付商户的APPID',
|
||||
'type' => 'input',
|
||||
],
|
||||
'mch_id' => [
|
||||
'label' => '商户号',
|
||||
'description' => '微信支付商户号',
|
||||
'type' => 'input',
|
||||
],
|
||||
'api_key' => [
|
||||
'label' => 'APIKEY(v1)',
|
||||
'description' => '',
|
||||
'type' => 'input',
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function pay($order)
|
||||
{
|
||||
$gateway = Omnipay::create('WechatPay_Native');
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setMchId($this->config['mch_id']);
|
||||
$gateway->setApiKey($this->config['api_key']);
|
||||
$gateway->setNotifyUrl($order['notify_url']);
|
||||
|
||||
$params = [
|
||||
'body' => $order['trade_no'],
|
||||
'out_trade_no' => $order['trade_no'],
|
||||
'total_fee' => $order['total_amount'],
|
||||
'spbill_create_ip' => '0.0.0.0',
|
||||
'fee_type' => 'CNY'
|
||||
];
|
||||
|
||||
$request = $gateway->purchase($params);
|
||||
$response = $request->send();
|
||||
$response = $response->getData();
|
||||
if ($response['return_code'] !== 'SUCCESS') {
|
||||
abort(500, $response['return_msg']);
|
||||
}
|
||||
return [
|
||||
'type' => 0,
|
||||
'data' => $response['code_url']
|
||||
];
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
{
|
||||
$data = Helper::xml2array(file_get_contents('php://input'));
|
||||
$gateway = Omnipay::create('WechatPay');
|
||||
$gateway->setAppId($this->config['app_id']);
|
||||
$gateway->setMchId($this->config['mch_id']);
|
||||
$gateway->setApiKey($this->config['api_key']);
|
||||
$response = $gateway->completePurchase([
|
||||
'request_params' => file_get_contents('php://input')
|
||||
])->send();
|
||||
|
||||
if (!$response->isPaid()) {
|
||||
die('FAIL');
|
||||
}
|
||||
|
||||
return [
|
||||
'trade_no' => $data['out_trade_no'],
|
||||
'callback_no' => $data['transaction_id']
|
||||
];
|
||||
}
|
||||
}
|
@ -23,6 +23,6 @@ class AppServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
$this->app['view']->addNamespace('theme', public_path() . '/theme');
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class OrderService
|
||||
'three_year_price' => 36
|
||||
];
|
||||
public $order;
|
||||
public $user;
|
||||
|
||||
public function __construct(Order $order)
|
||||
{
|
||||
@ -27,11 +28,11 @@ class OrderService
|
||||
public function open()
|
||||
{
|
||||
$order = $this->order;
|
||||
$user = User::find($order->user_id);
|
||||
$this->user = User::find($order->user_id);
|
||||
$plan = Plan::find($order->plan_id);
|
||||
|
||||
if ($order->refund_amount) {
|
||||
$user->balance = $user->balance + $order->refund_amount;
|
||||
$this->user->balance = $this->user->balance + $order->refund_amount;
|
||||
}
|
||||
DB::beginTransaction();
|
||||
if ($order->surplus_order_ids) {
|
||||
@ -46,18 +47,28 @@ class OrderService
|
||||
}
|
||||
switch ((string)$order->cycle) {
|
||||
case 'onetime_price':
|
||||
$this->buyByOneTime($user, $plan);
|
||||
$this->buyByOneTime($plan);
|
||||
break;
|
||||
case 'reset_price':
|
||||
$this->buyByResetTraffic($user);
|
||||
$this->buyByResetTraffic();
|
||||
break;
|
||||
default:
|
||||
$this->buyByCycle($order, $user, $plan);
|
||||
$this->buyByCycle($order, $plan);
|
||||
}
|
||||
|
||||
if ((int)config('v2board.renew_reset_traffic_enable', 0)) $this->buyByResetTraffic($user);
|
||||
switch ((int)$order->type) {
|
||||
case 1:
|
||||
$this->openEvent(config('v2board.new_order_event_id', 0));
|
||||
break;
|
||||
case 2:
|
||||
$this->openEvent(config('v2board.renew_order_event_id', 0));
|
||||
break;
|
||||
case 3:
|
||||
$this->openEvent(config('v2board.change_order_event_id', 0));
|
||||
break;
|
||||
}
|
||||
|
||||
if (!$user->save()) {
|
||||
if (!$this->user->save()) {
|
||||
DB::rollBack();
|
||||
abort(500, '开通失败');
|
||||
}
|
||||
@ -121,13 +132,26 @@ class OrderService
|
||||
$order->total_amount = $order->total_amount - $order->discount_amount;
|
||||
}
|
||||
|
||||
public function setInvite(User $user)
|
||||
public function setInvite(User $user):void
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($user->invite_user_id && $order->total_amount > 0) {
|
||||
$order->invite_user_id = $user->invite_user_id;
|
||||
$isCommission = false;
|
||||
switch ((int)$user->commission_type) {
|
||||
case 0:
|
||||
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
|
||||
if (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user))) {
|
||||
$isCommission = (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user)));
|
||||
break;
|
||||
case 1:
|
||||
$isCommission = true;
|
||||
break;
|
||||
case 2:
|
||||
$isCommission = !$this->haveValidOrder($user);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($isCommission) {
|
||||
$inviter = User::find($user->invite_user_id);
|
||||
if ($inviter && $inviter->commission_rate) {
|
||||
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
|
||||
@ -191,7 +215,7 @@ class OrderService
|
||||
if ($item->cycle === 'onetime_price') continue;
|
||||
if ($this->orderIsUsed($item)) continue;
|
||||
$orderSurplusMonth = $orderSurplusMonth + self::STR_TO_TIME[$item->cycle];
|
||||
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount']);
|
||||
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount'] + $item['surplus_amount'] - $item['refund_amount']);
|
||||
}
|
||||
if (!$orderSurplusMonth || !$orderSurplusAmount) return;
|
||||
$monthUnitPrice = $orderSurplusAmount / $orderSurplusMonth;
|
||||
@ -220,35 +244,35 @@ class OrderService
|
||||
}
|
||||
|
||||
|
||||
private function buyByResetTraffic(User $user)
|
||||
private function buyByResetTraffic()
|
||||
{
|
||||
$user->u = 0;
|
||||
$user->d = 0;
|
||||
$this->user->u = 0;
|
||||
$this->user->d = 0;
|
||||
}
|
||||
|
||||
private function buyByCycle(Order $order, User $user, Plan $plan)
|
||||
private function buyByCycle(Order $order, Plan $plan)
|
||||
{
|
||||
// change plan process
|
||||
if ((int)$order->type === 3) {
|
||||
$user->expired_at = time();
|
||||
$this->user->expired_at = time();
|
||||
}
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$this->user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
// 从一次性转换到循环
|
||||
if ($user->expired_at === NULL) $this->buyByResetTraffic($user);
|
||||
if ($this->user->expired_at === NULL) $this->buyByResetTraffic();
|
||||
// 新购
|
||||
if ($order->type === 1) $this->buyByResetTraffic($user);
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = $this->getTime($order->cycle, $user->expired_at);
|
||||
if ($order->type === 1) $this->buyByResetTraffic();
|
||||
$this->user->plan_id = $plan->id;
|
||||
$this->user->group_id = $plan->group_id;
|
||||
$this->user->expired_at = $this->getTime($order->cycle, $this->user->expired_at);
|
||||
}
|
||||
|
||||
private function buyByOneTime(User $user, Plan $plan)
|
||||
private function buyByOneTime(Plan $plan)
|
||||
{
|
||||
$this->buyByResetTraffic($user);
|
||||
$user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$user->plan_id = $plan->id;
|
||||
$user->group_id = $plan->group_id;
|
||||
$user->expired_at = NULL;
|
||||
$this->buyByResetTraffic();
|
||||
$this->user->transfer_enable = $plan->transfer_enable * 1073741824;
|
||||
$this->user->plan_id = $plan->id;
|
||||
$this->user->group_id = $plan->group_id;
|
||||
$this->user->expired_at = NULL;
|
||||
}
|
||||
|
||||
private function getTime($str, $timestamp)
|
||||
@ -271,4 +295,15 @@ class OrderService
|
||||
return strtotime('+36 month', $timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
private function openEvent($eventId)
|
||||
{
|
||||
switch ((int) $eventId) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
$this->buyByResetTraffic();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,12 @@ use App\Models\Payment;
|
||||
|
||||
class PaymentService
|
||||
{
|
||||
public $method;
|
||||
public $customResult;
|
||||
protected $class;
|
||||
protected $config;
|
||||
protected $payment;
|
||||
|
||||
public function __construct($method, $id = NULL, $uuid = NULL)
|
||||
{
|
||||
$this->method = $method;
|
||||
@ -22,6 +28,7 @@ class PaymentService
|
||||
$this->config['uuid'] = $payment['uuid'];
|
||||
};
|
||||
$this->payment = new $this->class($this->config);
|
||||
if (isset($this->payment->customResult)) $this->customResult = $this->payment->customResult;
|
||||
}
|
||||
|
||||
public function notify($params)
|
||||
|
@ -8,8 +8,6 @@ use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Utils\CacheKey;
|
||||
use App\Utils\Helper;
|
||||
use App\Utils\URLSchemes;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class ServerService
|
||||
@ -29,7 +27,6 @@ class ServerService
|
||||
$v2ray[$i]['type'] = 'v2ray';
|
||||
$groupId = json_decode($v2ray[$i]['group_id']);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
$v2ray[$i]['link'] = URLSchemes::buildVmess($v2ray[$i], $user);
|
||||
if ($v2ray[$i]['parent_id']) {
|
||||
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
|
||||
} else {
|
||||
@ -54,7 +51,6 @@ class ServerService
|
||||
for ($i = 0; $i < count($trojan); $i++) {
|
||||
$trojan[$i]['type'] = 'trojan';
|
||||
$groupId = json_decode($trojan[$i]['group_id']);
|
||||
$trojan[$i]['link'] = URLSchemes::buildTrojan($trojan[$i], $user);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
if ($trojan[$i]['parent_id']) {
|
||||
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
|
||||
@ -78,7 +74,6 @@ class ServerService
|
||||
for ($i = 0; $i < count($shadowsocks); $i++) {
|
||||
$shadowsocks[$i]['type'] = 'shadowsocks';
|
||||
$groupId = json_decode($shadowsocks[$i]['group_id']);
|
||||
$shadowsocks[$i]['link'] = URLSchemes::buildShadowsocks($shadowsocks[$i], $user);
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
if ($shadowsocks[$i]['parent_id']) {
|
||||
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
|
||||
@ -196,6 +191,9 @@ class ServerService
|
||||
case 'quic':
|
||||
$json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
case 'grpc':
|
||||
$json->inbound->streamSettings->grpcSettings = json_decode($server->networkSettings);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -358,4 +356,17 @@ class ServerService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAllServers()
|
||||
{
|
||||
$servers = array_merge(
|
||||
$this->getShadowsocksServers(),
|
||||
$this->getV2rayServers(),
|
||||
$this->getTrojanServers()
|
||||
);
|
||||
$this->mergeData($servers);
|
||||
$tmp = array_column($servers, 'sort');
|
||||
array_multisort($tmp, SORT_ASC, $servers);
|
||||
return $servers;
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,12 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\InviteCode;
|
||||
use App\Models\Order;
|
||||
use App\Models\Server;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class UserService
|
||||
{
|
||||
@ -52,7 +55,7 @@ class UserService
|
||||
|
||||
public function addBalance(int $userId, int $balance):bool
|
||||
{
|
||||
$user = User::find($userId);
|
||||
$user = User::lockForUpdate()->find($userId);
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,68 +0,0 @@
|
||||
<?php
|
||||
namespace App\Utils;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\User;
|
||||
|
||||
class URLSchemes
|
||||
{
|
||||
public static function buildShadowsocks($server, User $user)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$str = str_replace(
|
||||
['+', '/', '='],
|
||||
['-', '_', ''],
|
||||
base64_encode("{$server['cipher']}:{$user['uuid']}")
|
||||
);
|
||||
return "ss://{$str}@{$server['host']}:{$server['port']}#{$name}\r\n";
|
||||
}
|
||||
|
||||
public static function buildShadowsocksSIP008($server, User $user)
|
||||
{
|
||||
$config = [
|
||||
"id" => $server['id'],
|
||||
"remarks" => $server['name'],
|
||||
"server" => $server['host'],
|
||||
"server_port" => $server['port'],
|
||||
"password" => $user['uuid'],
|
||||
"method" => $server['cipher']
|
||||
];
|
||||
return $config;
|
||||
}
|
||||
|
||||
public static function buildVmess($server, User $user)
|
||||
{
|
||||
$config = [
|
||||
"v" => "2",
|
||||
"ps" => $server['name'],
|
||||
"add" => $server['host'],
|
||||
"port" => (string)$server['port'],
|
||||
"id" => $user['uuid'],
|
||||
"aid" => (string)$server['alter_id'],
|
||||
"net" => $server['network'],
|
||||
"type" => "none",
|
||||
"host" => "",
|
||||
"path" => "",
|
||||
"tls" => $server['tls'] ? "tls" : ""
|
||||
];
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
if (isset($wsSettings['path'])) $config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host'])) $config['host'] = $wsSettings['headers']['Host'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
||||
public static function buildTrojan($server, User $user)
|
||||
{
|
||||
$name = rawurlencode($server['name']);
|
||||
$query = http_build_query([
|
||||
'allowInsecure' => $server['allow_insecure'],
|
||||
'peer' => $server['server_name'],
|
||||
'sni' => $server['server_name']
|
||||
]);
|
||||
$uri = "trojan://{$user['uuid']}@{$server['host']}:{$server['port']}?{$query}#{$name}";
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
"laravel/framework": "^6.0",
|
||||
"laravel/tinker": "^1.0",
|
||||
"lokielse/omnipay-alipay": "3.0.6",
|
||||
"lokielse/omnipay-wechatpay": "^3.0",
|
||||
"php-curl-class/php-curl-class": "^8.6",
|
||||
"stripe/stripe-php": "^7.36.1",
|
||||
"symfony/yaml": "^4.3"
|
||||
@ -64,11 +65,5 @@
|
||||
"post-create-project-cmd": [
|
||||
"@php artisan key:generate --ansi"
|
||||
]
|
||||
},
|
||||
"repositories": {
|
||||
"packagist": {
|
||||
"type": "composer",
|
||||
"url": "https://mirrors.aliyun.com/composer/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,5 +236,5 @@ return [
|
||||
| The only modification by laravel config
|
||||
|
|
||||
*/
|
||||
'version' => '1.5.1.1621339182281'
|
||||
'version' => '1.5.2.1626426358299'
|
||||
];
|
||||
|
@ -321,6 +321,7 @@ CREATE TABLE `v2_user` (
|
||||
`password_algo` char(10) DEFAULT NULL,
|
||||
`balance` int(11) NOT NULL DEFAULT '0',
|
||||
`discount` int(11) DEFAULT NULL,
|
||||
`commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime',
|
||||
`commission_rate` int(11) DEFAULT NULL,
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`t` int(11) NOT NULL DEFAULT '0',
|
||||
@ -347,4 +348,4 @@ CREATE TABLE `v2_user` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2021-05-06 16:14:04
|
||||
-- 2021-07-13 13:50:52
|
||||
|
@ -411,3 +411,17 @@ ALTER TABLE `v2_order`
|
||||
|
||||
ALTER TABLE `v2_payment`
|
||||
ADD `uuid` char(32) NOT NULL AFTER `id`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD UNIQUE `email_deleted_at` (`email`, `deleted_at`),
|
||||
DROP INDEX `email`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
DROP `deleted_at`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD UNIQUE `email` (`email`),
|
||||
DROP INDEX `email_deleted_at`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `commission_type` tinyint NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime' AFTER `discount`;
|
||||
|
21
package.json
@ -1,21 +0,0 @@
|
||||
{
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "npm run development",
|
||||
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"watch": "npm run development -- --watch",
|
||||
"watch-poll": "npm run watch -- --watch-poll",
|
||||
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
|
||||
"prod": "npm run production",
|
||||
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"axios": "^0.19",
|
||||
"cross-env": "^5.1",
|
||||
"laravel-mix": "^4.0.7",
|
||||
"lodash": "^4.17.13",
|
||||
"resolve-url-loader": "^2.3.1",
|
||||
"sass": "^1.15.2",
|
||||
"sass-loader": "^7.1.0"
|
||||
}
|
||||
}
|
2
public/assets/admin/umi.css
vendored
2
public/assets/admin/umi.js
vendored
1
public/assets/user/components.async.js
vendored
1
public/assets/user/umi.js
vendored
1
public/theme/v2board/assets/components.async.js
vendored
Normal file
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 237 KiB |
Before Width: | Height: | Size: 680 KiB After Width: | Height: | Size: 680 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 832 KiB After Width: | Height: | Size: 832 KiB |