mirror of
https://github.com/v2board/v2board.git
synced 2025-06-23 02:29:57 +08:00
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\CommissionLog;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
@ -59,27 +60,66 @@ class CheckCommission extends Command
|
||||
|
||||
public function autoPayCommission()
|
||||
{
|
||||
$order = Order::where('commission_status', 1)
|
||||
$orders = Order::where('commission_status', 1)
|
||||
->where('invite_user_id', '!=', NULL)
|
||||
->get();
|
||||
foreach ($order as $item) {
|
||||
$inviter = User::find($item->invite_user_id);
|
||||
if (!$inviter) continue;
|
||||
if ((int)config('v2board.withdraw_close_enable', 0)) {
|
||||
$inviter->balance = $inviter->balance + $item->commission_balance;
|
||||
} else {
|
||||
$inviter->commission_balance = $inviter->commission_balance + $item->commission_balance;
|
||||
}
|
||||
foreach ($orders as $order) {
|
||||
DB::beginTransaction();
|
||||
if ($inviter->save()) {
|
||||
$item->commission_status = 2;
|
||||
if (!$item->save()) {
|
||||
if (!$this->payHandle($order->invite_user_id, $order)) {
|
||||
DB::rollBack();
|
||||
continue;
|
||||
}
|
||||
$order->commission_status = 2;
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
continue;
|
||||
}
|
||||
DB::commit();
|
||||
}
|
||||
}
|
||||
|
||||
public function payHandle($inviteUserId, Order $order)
|
||||
{
|
||||
if ((int)config('v2board.commission_distribution_enable', 0)) {
|
||||
$level = 3;
|
||||
$commissionShareLevels = [
|
||||
0 => (int)config('v2board.commission_distribution_l1'),
|
||||
1 => (int)config('v2board.commission_distribution_l2'),
|
||||
2 => (int)config('v2board.commission_distribution_l3')
|
||||
];
|
||||
} else {
|
||||
$level = 3;
|
||||
$commissionShareLevels = [
|
||||
0 => 100
|
||||
];
|
||||
}
|
||||
for ($l = 0; $l < $level; $l++) {
|
||||
$inviter = User::find($inviteUserId);
|
||||
if (!$inviter) continue;
|
||||
if (!isset($commissionShareLevels[$l])) continue;
|
||||
$commissionBalance = $order->commission_balance * ($commissionShareLevels[$l] / 100);
|
||||
if ((int)config('v2board.withdraw_close_enable', 0)) {
|
||||
$inviter->balance = $inviter->balance + $commissionBalance;
|
||||
} else {
|
||||
$inviter->commission_balance = $inviter->commission_balance + $commissionBalance;
|
||||
}
|
||||
if (!$inviter->save()) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
if (!CommissionLog::create([
|
||||
'invite_user_id' => $inviteUserId,
|
||||
'user_id' => $order->user_id,
|
||||
'trade_no' => $order->trade_no,
|
||||
'order_amount' => $order->total_amount,
|
||||
'get_amount' => $commissionBalance
|
||||
])) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
$inviteUserId = $inviter->invite_user_id;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Jobs\OrderHandleJob;
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\Order;
|
||||
@ -45,20 +46,8 @@ class CheckOrder extends Command
|
||||
ini_set('memory_limit', -1);
|
||||
$orders = Order::whereIn('status', [0, 1])
|
||||
->get();
|
||||
foreach ($orders as $item) {
|
||||
$orderService = new OrderService($item);
|
||||
switch ($item->status) {
|
||||
// cancel
|
||||
case 0:
|
||||
if (strtotime($item->created_at) <= (time() - 1800)) {
|
||||
$orderService->cancel();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
$orderService->open();
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($orders as $order) {
|
||||
OrderHandleJob::dispatch($order->trade_no);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Plan;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -42,24 +43,45 @@ class ResetTraffic extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
DB::beginTransaction();
|
||||
ini_set('memory_limit', -1);
|
||||
foreach (Plan::get() as $plan) {
|
||||
switch ($plan->reset_traffic_method) {
|
||||
case null: {
|
||||
$resetTrafficMethod = config('v2board.reset_traffic_method', 0);
|
||||
switch ((int)$resetTrafficMethod) {
|
||||
// 1 a month
|
||||
// month first day
|
||||
case 0:
|
||||
$this->resetByMonthFirstDay();
|
||||
$this->resetByMonthFirstDay($this->builder);
|
||||
break;
|
||||
// expire day
|
||||
case 1:
|
||||
$this->resetByExpireDay();
|
||||
$this->resetByExpireDay($this->builder);
|
||||
break;
|
||||
// no action
|
||||
case 2:
|
||||
break;
|
||||
}
|
||||
DB::commit();
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
$builder = $this->builder->where('plan_id', $plan->id);
|
||||
$this->resetByMonthFirstDay($builder);
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
$builder = $this->builder->where('plan_id', $plan->id);
|
||||
$this->resetByExpireDay($builder);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function resetByMonthFirstDay():void
|
||||
private function resetByMonthFirstDay($builder):void
|
||||
{
|
||||
$builder = $this->builder;
|
||||
if ((string)date('d') === '01') {
|
||||
$builder->update([
|
||||
'u' => 0,
|
||||
@ -68,9 +90,8 @@ class ResetTraffic extends Command
|
||||
}
|
||||
}
|
||||
|
||||
private function resetByExpireDay():void
|
||||
private function resetByExpireDay($builder):void
|
||||
{
|
||||
$builder = $this->builder;
|
||||
$lastDay = date('d', strtotime('last day of +0 months'));
|
||||
$users = [];
|
||||
foreach ($builder->get() as $item) {
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Services\MailService;
|
||||
use Illuminate\Console\Command;
|
||||
use App\Models\User;
|
||||
use App\Models\MailLog;
|
||||
@ -41,23 +42,9 @@ class SendRemindMail extends Command
|
||||
public function handle()
|
||||
{
|
||||
$users = User::all();
|
||||
$mailService = new MailService();
|
||||
foreach ($users as $user) {
|
||||
if ($user->remind_expire) $this->remindExpire($user);
|
||||
}
|
||||
}
|
||||
|
||||
private function remindExpire($user)
|
||||
{
|
||||
if ($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time()) {
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的服务即将到期',
|
||||
'template_name' => 'remindExpire',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
if ($user->remind_expire) $mailService->remindExpire($user);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,10 @@
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Models\User;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Console\Command;
|
||||
use Matriphe\Larinfo;
|
||||
|
||||
class Test extends Command
|
||||
{
|
||||
|
@ -47,14 +47,13 @@ class V2boardInstall extends Command
|
||||
$this->info(" \ \ / / __) | _ \ / _ \ / _` | '__/ _` | ");
|
||||
$this->info(" \ V / / __/| |_) | (_) | (_| | | | (_| | ");
|
||||
$this->info(" \_/ |_____|____/ \___/ \__,_|_| \__,_| ");
|
||||
if (\File::exists(base_path() . '/.lock')) {
|
||||
if (\File::exists(base_path() . '/.env')) {
|
||||
abort(500, 'V2board 已安装,如需重新安装请删除目录下.lock文件');
|
||||
}
|
||||
if (!\File::exists(base_path() . '/.env')) {
|
||||
|
||||
if (!copy(base_path() . '/.env.example', base_path() . '/.env')) {
|
||||
abort(500, '复制环境文件失败,请检查目录权限');
|
||||
}
|
||||
}
|
||||
$this->saveToEnv([
|
||||
'APP_KEY' => 'base64:' . base64_encode(Encrypter::generateKey('AES-256-CBC')),
|
||||
'DB_HOST' => $this->ask('请输入数据库地址(默认:localhost)', 'localhost'),
|
||||
|
@ -50,8 +50,8 @@ class V2boardStatistics extends Command
|
||||
{
|
||||
$endAt = strtotime(date('Y-m-d'));
|
||||
$startAt = strtotime('-1 day', $endAt);
|
||||
$builder = Order::where('created_at', '>=', $startAt)
|
||||
->where('created_at', '<', $endAt)
|
||||
$builder = Order::where('paid_at', '>=', $startAt)
|
||||
->where('paid_at', '<', $endAt)
|
||||
->whereNotIn('status', [0, 2]);
|
||||
$orderCount = $builder->count();
|
||||
$orderAmount = $builder->sum('total_amount');
|
||||
|
@ -51,11 +51,12 @@ class V2boardUpdate extends Command
|
||||
}
|
||||
$this->info('正在导入数据库请稍等...');
|
||||
foreach ($sql as $item) {
|
||||
if (!$item) continue;
|
||||
try {
|
||||
DB::select(DB::raw($item));
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
}
|
||||
$this->info('更新完毕');
|
||||
$this->info('更新完毕,请重新启动队列服务。');
|
||||
}
|
||||
}
|
||||
|
4
app/Console/Kernel.php
Executable file → Normal file
4
app/Console/Kernel.php
Executable file → Normal file
@ -31,9 +31,11 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->command('check:commission')->everyMinute();
|
||||
// reset
|
||||
$schedule->command('reset:traffic')->daily();
|
||||
$schedule->command('reset:serverLog')->quarterly();
|
||||
$schedule->command('reset:serverLog')->quarterly()->at('0:15');
|
||||
// send
|
||||
$schedule->command('send:remindMail')->dailyAt('11:30');
|
||||
// horizon metrics
|
||||
$schedule->command('horizon:snapshot')->everyFiveMinutes();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Throwable;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
@ -29,10 +29,12 @@ class Handler extends ExceptionHandler
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
public function report(Throwable $exception)
|
||||
{
|
||||
parent::report($exception);
|
||||
}
|
||||
@ -41,15 +43,13 @@ class Handler extends ExceptionHandler
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $exception
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param \Throwable $exception
|
||||
* @return \Symfony\Component\HttpFoundation\Response
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function render($request, Exception $exception)
|
||||
public function render($request, Throwable $exception)
|
||||
{
|
||||
if($exception instanceof \Illuminate\Http\Exceptions\ThrottleRequestsException) {
|
||||
abort(429, '请求频繁,请稍后再试');
|
||||
}
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -60,7 +60,11 @@ class ConfigController extends Controller
|
||||
'commission_auto_check_enable' => config('v2board.commission_auto_check_enable', 1),
|
||||
'commission_withdraw_limit' => config('v2board.commission_withdraw_limit', 100),
|
||||
'commission_withdraw_method' => config('v2board.commission_withdraw_method', Dict::WITHDRAW_METHOD_WHITELIST_DEFAULT),
|
||||
'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0)
|
||||
'withdraw_close_enable' => config('v2board.withdraw_close_enable', 0),
|
||||
'commission_distribution_enable' => config('v2board.commission_distribution_enable', 0),
|
||||
'commission_distribution_l1' => config('v2board.commission_distribution_l1'),
|
||||
'commission_distribution_l2' => config('v2board.commission_distribution_l2'),
|
||||
'commission_distribution_l3' => config('v2board.commission_distribution_l3')
|
||||
],
|
||||
'site' => [
|
||||
'safe_mode_enable' => (int)config('v2board.safe_mode_enable', 0),
|
||||
@ -136,9 +140,6 @@ class ConfigController extends Controller
|
||||
'server_v2ray_domain' => config('v2board.server_v2ray_domain'),
|
||||
'server_v2ray_protocol' => config('v2board.server_v2ray_protocol'),
|
||||
],
|
||||
'tutorial' => [
|
||||
'apple_id' => config('v2board.apple_id')
|
||||
],
|
||||
'email' => [
|
||||
'email_template' => config('v2board.email_template', 'default'),
|
||||
'email_host' => config('v2board.email_host'),
|
||||
@ -166,7 +167,7 @@ class ConfigController extends Controller
|
||||
|
||||
public function save(ConfigSave $request)
|
||||
{
|
||||
$data = $request->input();
|
||||
$data = $request->validated();
|
||||
$array = \Config::get('v2board');
|
||||
foreach ($data as $k => $v) {
|
||||
if (!in_array($k, array_keys($request->validated()))) {
|
||||
|
@ -24,42 +24,12 @@ class CouponController extends Controller
|
||||
$total = $builder->count();
|
||||
$coupons = $builder->forPage($current, $pageSize)
|
||||
->get();
|
||||
|
||||
foreach ($coupons as $k => $v) {
|
||||
if ($coupons[$k]['limit_plan_ids']) $coupons[$k]['limit_plan_ids'] = json_decode($coupons[$k]['limit_plan_ids']);
|
||||
}
|
||||
return response([
|
||||
'data' => $coupons,
|
||||
'total' => $total
|
||||
]);
|
||||
}
|
||||
|
||||
public function save(CouponSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
if (isset($params['limit_plan_ids'])) {
|
||||
$params['limit_plan_ids'] = json_encode($params['limit_plan_ids']);
|
||||
}
|
||||
if (!$request->input('id')) {
|
||||
if (!isset($params['code'])) {
|
||||
$params['code'] = Helper::randomChar(8);
|
||||
}
|
||||
if (!Coupon::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Coupon::find($request->input('id'))->update($params);
|
||||
} catch (\Exception $e) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
}
|
||||
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function generate(CouponGenerate $request)
|
||||
{
|
||||
if ($request->input('generate_count')) {
|
||||
@ -68,9 +38,6 @@ class CouponController extends Controller
|
||||
}
|
||||
|
||||
$params = $request->validated();
|
||||
if (isset($params['limit_plan_ids'])) {
|
||||
$params['limit_plan_ids'] = json_encode($params['limit_plan_ids']);
|
||||
}
|
||||
if (!$request->input('id')) {
|
||||
if (!isset($params['code'])) {
|
||||
$params['code'] = Helper::randomChar(8);
|
||||
@ -95,10 +62,8 @@ class CouponController extends Controller
|
||||
{
|
||||
$coupons = [];
|
||||
$coupon = $request->validated();
|
||||
if (isset($coupon['limit_plan_ids'])) {
|
||||
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
|
||||
}
|
||||
$coupon['created_at'] = $coupon['updated_at'] = time();
|
||||
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
|
||||
unset($coupon['generate_count']);
|
||||
for ($i = 0;$i < $request->input('generate_count');$i++) {
|
||||
$coupon['code'] = Helper::randomChar(8);
|
||||
@ -118,7 +83,7 @@ class CouponController extends Controller
|
||||
$endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
|
||||
$limitUse = $coupon['limit_use'] ?? '不限制';
|
||||
$createTime = date('Y-m-d H:i:s', $coupon['created_at']);
|
||||
$limitPlanIds = $coupon['limit_plan_ids'] ?? '不限制';
|
||||
$limitPlanIds = implode("/", json_decode($coupon['limit_plan_ids'], true)) ?? '不限制';
|
||||
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
|
||||
}
|
||||
echo $data;
|
||||
|
@ -6,6 +6,7 @@ use App\Http\Requests\Admin\OrderAssign;
|
||||
use App\Http\Requests\Admin\OrderUpdate;
|
||||
use App\Http\Requests\Admin\OrderFetch;
|
||||
use App\Services\OrderService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Helper;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
@ -63,20 +64,33 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(OrderUpdate $request)
|
||||
public function paid(Request $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'status',
|
||||
'commission_status'
|
||||
]);
|
||||
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
|
||||
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->paid('manual_operation')) {
|
||||
abort(500, '更新失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function cancel(Request $request)
|
||||
{
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
if ($order->status !== 0) abort(500, '只能对待支付的订单进行操作');
|
||||
|
||||
if (isset($params['status']) && (int)$params['status'] === 2) {
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->cancel()) {
|
||||
abort(500, '更新失败');
|
||||
@ -86,6 +100,18 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(OrderUpdate $request)
|
||||
{
|
||||
$params = $request->only([
|
||||
'commission_status'
|
||||
]);
|
||||
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在');
|
||||
}
|
||||
|
||||
try {
|
||||
$order->update($params);
|
||||
} catch (\Exception $e) {
|
||||
@ -97,26 +123,6 @@ class OrderController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function repair(Request $request)
|
||||
{
|
||||
if (empty($request->input('trade_no'))) {
|
||||
abort(500, '参数错误');
|
||||
}
|
||||
$order = Order::where('trade_no', $request->input('trade_no'))
|
||||
->where('status', 0)
|
||||
->first();
|
||||
if (!$order) {
|
||||
abort(500, '订单不存在或订单已支付');
|
||||
}
|
||||
$order->status = 1;
|
||||
if (!$order->save()) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
return response([
|
||||
'data' => true
|
||||
]);
|
||||
}
|
||||
|
||||
public function assign(OrderAssign $request)
|
||||
{
|
||||
$plan = Plan::find($request->input('plan_id'));
|
||||
@ -130,6 +136,11 @@ class OrderController extends Controller
|
||||
abort(500, '该订阅不存在');
|
||||
}
|
||||
|
||||
$userService = new UserService();
|
||||
if ($userService->isNotCompleteOrderByUserId($user->id)) {
|
||||
abort(500, '该用户还有待支付的订单,无法分配');
|
||||
}
|
||||
|
||||
DB::beginTransaction();
|
||||
$order = new Order();
|
||||
$orderService = new OrderService($order);
|
||||
|
@ -42,6 +42,9 @@ class PaymentController extends Controller
|
||||
|
||||
public function save(Request $request)
|
||||
{
|
||||
if (!config('v2board.app_url')) {
|
||||
abort(500, '请在站点配置中配置站点地址');
|
||||
}
|
||||
if ($request->input('id')) {
|
||||
$payment = Payment::find($request->input('id'));
|
||||
if (!$payment) abort(500, '支付方式不存在');
|
||||
@ -58,7 +61,7 @@ class PaymentController extends Controller
|
||||
'name' => $request->input('name'),
|
||||
'payment' => $request->input('payment'),
|
||||
'config' => $request->input('config'),
|
||||
'uuid' => Helper::guid()
|
||||
'uuid' => Helper::randomChar(8)
|
||||
])) {
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
@ -50,10 +50,9 @@ class GroupController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
$servers = Server::all();
|
||||
$servers = ServerV2ray::all();
|
||||
foreach ($servers as $server) {
|
||||
$groupId = json_decode($server->group_id);
|
||||
if (in_array($request->input('id'), $groupId)) {
|
||||
if (in_array($request->input('id'), $server->group_id)) {
|
||||
abort(500, '该组已被节点所使用,无法删除');
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Http\Controllers\Admin\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Services\ServerService;
|
||||
@ -32,7 +32,7 @@ class ManageController extends Controller
|
||||
}
|
||||
break;
|
||||
case 'v2ray':
|
||||
if (!Server::find($v['value'])->update(['sort' => $k + 1])) {
|
||||
if (!ServerV2ray::find($v['value'])->update(['sort' => $k + 1])) {
|
||||
DB::rollBack();
|
||||
abort(500, '保存失败');
|
||||
}
|
||||
|
@ -14,11 +14,6 @@ class ShadowsocksController extends Controller
|
||||
public function save(ServerShadowsocksSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
$params['group_id'] = json_encode($params['group_id']);
|
||||
if (isset($params['tags'])) {
|
||||
$params['tags'] = json_encode($params['tags']);
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = ServerShadowsocks::find($request->input('id'));
|
||||
if (!$server) {
|
||||
|
@ -14,11 +14,6 @@ class TrojanController extends Controller
|
||||
public function save(ServerTrojanSave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
$params['group_id'] = json_encode($params['group_id']);
|
||||
if (isset($params['tags'])) {
|
||||
$params['tags'] = json_encode($params['tags']);
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = ServerTrojan::find($request->input('id'));
|
||||
if (!$server) {
|
||||
|
@ -7,44 +7,16 @@ use App\Http\Requests\Admin\ServerV2rayUpdate;
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
|
||||
class V2rayController extends Controller
|
||||
{
|
||||
public function save(ServerV2raySave $request)
|
||||
{
|
||||
$params = $request->validated();
|
||||
$params['group_id'] = json_encode($params['group_id']);
|
||||
if (isset($params['tags'])) {
|
||||
$params['tags'] = json_encode($params['tags']);
|
||||
}
|
||||
|
||||
if (isset($params['dnsSettings'])) {
|
||||
if (!is_object(json_decode($params['dnsSettings']))) {
|
||||
abort(500, 'DNS规则配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['ruleSettings'])) {
|
||||
if (!is_object(json_decode($params['ruleSettings']))) {
|
||||
abort(500, '审计规则配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['networkSettings'])) {
|
||||
if (!is_object(json_decode($params['networkSettings']))) {
|
||||
abort(500, '传输协议配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($params['tlsSettings'])) {
|
||||
if (!is_object(json_decode($params['tlsSettings']))) {
|
||||
abort(500, 'TLS配置格式不正确');
|
||||
}
|
||||
}
|
||||
|
||||
if ($request->input('id')) {
|
||||
$server = Server::find($request->input('id'));
|
||||
$server = ServerV2ray::find($request->input('id'));
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
@ -58,7 +30,7 @@ class V2rayController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
if (!Server::create($params)) {
|
||||
if (!ServerV2ray::create($params)) {
|
||||
abort(500, '创建失败');
|
||||
}
|
||||
|
||||
@ -70,7 +42,7 @@ class V2rayController extends Controller
|
||||
public function drop(Request $request)
|
||||
{
|
||||
if ($request->input('id')) {
|
||||
$server = Server::find($request->input('id'));
|
||||
$server = ServerV2ray::find($request->input('id'));
|
||||
if (!$server) {
|
||||
abort(500, '节点ID不存在');
|
||||
}
|
||||
@ -86,7 +58,7 @@ class V2rayController extends Controller
|
||||
'show',
|
||||
]);
|
||||
|
||||
$server = Server::find($request->input('id'));
|
||||
$server = ServerV2ray::find($request->input('id'));
|
||||
|
||||
if (!$server) {
|
||||
abort(500, '该服务器不存在');
|
||||
@ -104,12 +76,12 @@ class V2rayController extends Controller
|
||||
|
||||
public function copy(Request $request)
|
||||
{
|
||||
$server = Server::find($request->input('id'));
|
||||
$server = ServerV2ray::find($request->input('id'));
|
||||
$server->show = 0;
|
||||
if (!$server) {
|
||||
abort(500, '服务器不存在');
|
||||
}
|
||||
if (!Server::create($server->toArray())) {
|
||||
if (!ServerV2ray::create($server->toArray())) {
|
||||
abort(500, '复制失败');
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use App\Services\ServerService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\ServerGroup;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
use App\Models\Ticket;
|
||||
@ -91,7 +91,7 @@ class StatController extends Controller
|
||||
{
|
||||
$servers = [
|
||||
'shadowsocks' => ServerShadowsocks::where('parent_id', null)->get()->toArray(),
|
||||
'vmess' => Server::where('parent_id', null)->get()->toArray(),
|
||||
'vmess' => ServerV2ray::where('parent_id', null)->get()->toArray(),
|
||||
'trojan' => ServerTrojan::where('parent_id', null)->get()->toArray()
|
||||
];
|
||||
$timestamp = strtotime('-1 day', strtotime(date('Y-m-d')));
|
||||
|
@ -68,7 +68,7 @@ class UserController extends Controller
|
||||
$res[$i]['plan_name'] = $plan[$k]['name'];
|
||||
}
|
||||
}
|
||||
$res[$i]['subscribe_url'] = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $res[$i]['token'];
|
||||
$res[$i]['subscribe_url'] = Helper::getSubscribeHost() . '/api/v1/client/subscribe?token=' . $res[$i]['token'];
|
||||
}
|
||||
return response([
|
||||
'data' => $res,
|
||||
|
@ -7,7 +7,7 @@ use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\Clash;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
|
@ -4,7 +4,7 @@ namespace App\Http\Controllers\Client\Protocols;
|
||||
|
||||
class AnXray
|
||||
{
|
||||
public $flag = 'anxray';
|
||||
public $flag = 'axxray';
|
||||
private $servers;
|
||||
private $user;
|
||||
|
||||
@ -64,15 +64,21 @@ class AnXray
|
||||
"encryption" => "none",
|
||||
"type" => urlencode($server['network']),
|
||||
"security" => $server['tls'] ? "tls" : "",
|
||||
"sni" => $server['tls'] ? urlencode(json_decode($server['tlsSettings'], true)['serverName']) : ""
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = urlencode($tlsSettings['serverName']);
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
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);
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
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";
|
||||
|
@ -20,7 +20,10 @@ class Clash
|
||||
{
|
||||
$servers = $this->servers;
|
||||
$user = $this->user;
|
||||
$appName = config('v2board.app_name', 'V2Board');
|
||||
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
|
||||
header('profile-update-interval: 24');
|
||||
header("content-disposition: filename={$appName}");
|
||||
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
|
||||
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
|
||||
if (\File::exists($customConfig)) {
|
||||
@ -51,6 +54,11 @@ class Clash
|
||||
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
|
||||
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
|
||||
}
|
||||
// Force the current subscription domain to be a direct rule
|
||||
$subsDomain = $_SERVER['SERVER_NAME'];
|
||||
$subsDomainRule = "DOMAIN,{$subsDomain},DIRECT";
|
||||
array_unshift($config['rules'], $subsDomainRule);
|
||||
|
||||
$yaml = Yaml::dump($config);
|
||||
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);
|
||||
return $yaml;
|
||||
@ -84,7 +92,7 @@ class Clash
|
||||
if ($server['tls']) {
|
||||
$array['tls'] = true;
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
$array['skip-cert-verify'] = ($tlsSettings['allowInsecure'] ? true : false);
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
@ -94,7 +102,7 @@ class Clash
|
||||
if ($server['network'] === 'ws') {
|
||||
$array['network'] = 'ws';
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
$array['ws-path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
|
||||
@ -104,7 +112,7 @@ class Clash
|
||||
if ($server['network'] === 'grpc') {
|
||||
$array['network'] = 'grpc';
|
||||
if ($server['networkSettings']) {
|
||||
$grpcObject = json_decode($server['networkSettings'], true);
|
||||
$grpcObject = $server['networkSettings'];
|
||||
$array['grpc-opts'] = [];
|
||||
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
|
||||
}
|
||||
|
@ -63,19 +63,19 @@ class Passwall
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
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'];
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ class QuantumultX
|
||||
if ($server['network'] === 'tcp')
|
||||
array_push($config, 'obfs=over-tls');
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
array_push($config, 'tls-verification=' . ($tlsSettings['allowInsecure'] ? 'false' : 'true'));
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
@ -79,7 +79,7 @@ class QuantumultX
|
||||
else
|
||||
array_push($config, 'obfs=ws');
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
array_push($config, "obfs-uri={$wsSettings['path']}");
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']) && !isset($host))
|
||||
|
@ -63,19 +63,19 @@ class SSRPlus
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
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'];
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class Shadowrocket
|
||||
if ($server['tls']) {
|
||||
$config['tls'] = 1;
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
$config['allowInsecure'] = (int)$tlsSettings['allowInsecure'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
@ -73,7 +73,7 @@ class Shadowrocket
|
||||
if ($server['network'] === 'ws') {
|
||||
$config['obfs'] = "websocket";
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
$config['path'] = $wsSettings['path'];
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
|
||||
@ -83,7 +83,7 @@ class Shadowrocket
|
||||
if ($server['network'] === 'grpc') {
|
||||
$config['obfs'] = "grpc";
|
||||
if ($server['networkSettings']) {
|
||||
$grpcSettings = json_decode($server['networkSettings'], true);
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
if (isset($grpcSettings['serviceName']) && !empty($grpcSettings['serviceName']))
|
||||
$config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
|
@ -26,13 +26,19 @@ class Surfboard
|
||||
foreach ($servers as $item) {
|
||||
if ($item['type'] === 'shadowsocks') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildShadowsocks($user['uuid'], $item);
|
||||
$proxies .= self::buildShadowsocks($user['uuid'], $item);
|
||||
// [Proxy Group]
|
||||
$proxyGroup .= $item['name'] . ', ';
|
||||
}
|
||||
if ($item['type'] === 'v2ray') {
|
||||
// [Proxy]
|
||||
$proxies .= Surfboard::buildVmess($user['uuid'], $item);
|
||||
$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'] . ', ';
|
||||
}
|
||||
@ -48,8 +54,10 @@ class Surfboard
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$subsDomain = $_SERVER['SERVER_NAME'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$subs_domain', $subsDomain, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
@ -88,7 +96,7 @@ class Surfboard
|
||||
if ($server['tls']) {
|
||||
array_push($config, 'tls=true');
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
@ -98,7 +106,7 @@ class Surfboard
|
||||
if ($server['network'] === 'ws') {
|
||||
array_push($config, 'ws=true');
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
array_push($config, "ws-path={$wsSettings['path']}");
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
|
||||
@ -110,4 +118,24 @@ class Surfboard
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
|
||||
public static function buildTrojan($password, $server)
|
||||
{
|
||||
$config = [
|
||||
"{$server['name']}=trojan",
|
||||
"{$server['host']}",
|
||||
"{$server['port']}",
|
||||
"password={$password}",
|
||||
$server['server_name'] ? "sni={$server['server_name']}" : "",
|
||||
'tfo=true',
|
||||
'udp-relay=true'
|
||||
];
|
||||
if (!empty($server['allow_insecure'])) {
|
||||
array_push($config, $server['allow_insecure'] ? 'skip-cert-verify=true' : 'skip-cert-verify=false');
|
||||
}
|
||||
$config = array_filter($config);
|
||||
$uri = implode(',', $config);
|
||||
$uri .= "\r\n";
|
||||
return $uri;
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,10 @@ class Surge
|
||||
|
||||
// Subscription link
|
||||
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
|
||||
$subsDomain = $_SERVER['SERVER_NAME'];
|
||||
|
||||
$config = str_replace('$subs_link', $subsURL, $config);
|
||||
$config = str_replace('$subs_domain', $subsDomain, $config);
|
||||
$config = str_replace('$proxies', $proxies, $config);
|
||||
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
|
||||
return $config;
|
||||
@ -92,7 +94,7 @@ class Surge
|
||||
if ($server['tls']) {
|
||||
array_push($config, 'tls=true');
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['allowInsecure']) && !empty($tlsSettings['allowInsecure']))
|
||||
array_push($config, 'skip-cert-verify=' . ($tlsSettings['allowInsecure'] ? 'true' : 'false'));
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
@ -102,7 +104,7 @@ class Surge
|
||||
if ($server['network'] === 'ws') {
|
||||
array_push($config, 'ws=true');
|
||||
if ($server['networkSettings']) {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
|
||||
array_push($config, "ws-path={$wsSettings['path']}");
|
||||
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
|
||||
|
@ -63,19 +63,19 @@ class V2rayN
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
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'];
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
@ -63,19 +63,19 @@ class V2rayNG
|
||||
];
|
||||
if ($server['tls']) {
|
||||
if ($server['tlsSettings']) {
|
||||
$tlsSettings = json_decode($server['tlsSettings'], true);
|
||||
$tlsSettings = $server['tlsSettings'];
|
||||
if (isset($tlsSettings['serverName']) && !empty($tlsSettings['serverName']))
|
||||
$config['sni'] = $tlsSettings['serverName'];
|
||||
}
|
||||
}
|
||||
if ((string)$server['network'] === 'ws') {
|
||||
$wsSettings = json_decode($server['networkSettings'], true);
|
||||
$wsSettings = $server['networkSettings'];
|
||||
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'];
|
||||
$grpcSettings = $server['networkSettings'];
|
||||
if (isset($grpcSettings['serviceName'])) $config['path'] = $grpcSettings['serviceName'];
|
||||
}
|
||||
return "vmess://" . base64_encode(json_encode($config)) . "\r\n";
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ class PaymentController extends Controller
|
||||
}
|
||||
if ($order->status === 1) return true;
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->success($callbackNo)) {
|
||||
if (!$orderService->paid($callbackNo)) {
|
||||
return false;
|
||||
}
|
||||
$telegramService = new TelegramService();
|
||||
|
@ -102,10 +102,15 @@ class AuthController extends Controller
|
||||
if ((int)config('v2board.email_verify', 0)) {
|
||||
Cache::forget(CacheKey::get('EMAIL_VERIFY_CODE', $request->input('email')));
|
||||
}
|
||||
|
||||
$data = [
|
||||
'token' => $user->token,
|
||||
'auth_data' => base64_encode("{$user->email}:{$user->password}")
|
||||
];
|
||||
$request->session()->put('email', $user->email);
|
||||
$request->session()->put('id', $user->id);
|
||||
return response()->json([
|
||||
'data' => true
|
||||
'data' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
@ -120,6 +125,7 @@ class AuthController extends Controller
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$user->password_salt,
|
||||
$password,
|
||||
$user->password)
|
||||
) {
|
||||
@ -250,6 +256,7 @@ class AuthController extends Controller
|
||||
}
|
||||
$user->password = password_hash($request->input('password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
$user->password_salt = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, __('Reset failed'));
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@ -35,13 +35,13 @@ class DeepbworkController extends Controller
|
||||
public function user(Request $request)
|
||||
{
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = Server::find($nodeId);
|
||||
$server = ServerV2ray::find($nodeId);
|
||||
if (!$server) {
|
||||
abort(500, 'fail');
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$users = $serverService->getAvailableUsers($server->group_id);
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->v2ray_user = [
|
||||
@ -64,7 +64,7 @@ class DeepbworkController extends Controller
|
||||
public function submit(Request $request)
|
||||
{
|
||||
// Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
|
||||
$server = Server::find($request->input('node_id'));
|
||||
$server = ServerV2ray::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return response([
|
||||
'ret' => 0,
|
||||
@ -76,23 +76,11 @@ class DeepbworkController extends Controller
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess')) {
|
||||
continue;
|
||||
$userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'user fetch fail'
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'ret' => 1,
|
||||
|
@ -1,158 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Server;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use App\Services\UserService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/*
|
||||
* V2ray Poseidon
|
||||
* Github: https://github.com/ColetteContreras/trojan-poseidon
|
||||
*/
|
||||
class PoseidonController extends Controller
|
||||
{
|
||||
public $poseidonVersion;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->poseidonVersion = $request->input('poseidon_version');
|
||||
}
|
||||
|
||||
// 后端获取用户
|
||||
public function user(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
|
||||
$nodeId = $request->input('node_id');
|
||||
$server = Server::find($nodeId);
|
||||
if (!$server) {
|
||||
return $this->error("server could not be found", 404);
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->v2ray_user = [
|
||||
"uuid" => $user->uuid,
|
||||
"email" => sprintf("%s@v2board.user", $user->uuid),
|
||||
"alter_id" => $server->alter_id,
|
||||
"level" => 0,
|
||||
];
|
||||
unset($user['uuid']);
|
||||
unset($user['email']);
|
||||
array_push($result, $user);
|
||||
}
|
||||
|
||||
return $this->success($result);
|
||||
}
|
||||
|
||||
// 后端提交数据
|
||||
public function submit(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
$server = Server::find($request->input('node_id'));
|
||||
if (!$server) {
|
||||
return $this->error("server could not be found", 404);
|
||||
}
|
||||
$data = file_get_contents('php://input');
|
||||
$data = json_decode($data, true);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_V2RAY_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'vmess')) {
|
||||
return $this->error("user fetch fail", 500);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->success('');
|
||||
}
|
||||
|
||||
// 后端获取配置
|
||||
public function config(Request $request)
|
||||
{
|
||||
if ($r = $this->verifyToken($request)) { return $r; }
|
||||
|
||||
$nodeId = $request->input('node_id');
|
||||
$localPort = $request->input('local_port');
|
||||
if (empty($nodeId) || empty($localPort)) {
|
||||
return $this->error('invalid parameters', 400);
|
||||
}
|
||||
|
||||
$serverService = new ServerService();
|
||||
try {
|
||||
$json = $serverService->getV2RayConfig($nodeId, $localPort);
|
||||
$json->poseidon = [
|
||||
'license_key' => (string)config('v2board.server_license'),
|
||||
];
|
||||
if ($this->poseidonVersion >= 'v1.5.0') {
|
||||
// don't need it after v1.5.0
|
||||
unset($json->inboundDetour);
|
||||
unset($json->stats);
|
||||
unset($json->api);
|
||||
array_shift($json->routing->rules);
|
||||
}
|
||||
|
||||
foreach($json->policy->levels as &$level) {
|
||||
$level->handshake = 2;
|
||||
$level->uplinkOnly = 2;
|
||||
$level->downlinkOnly = 2;
|
||||
$level->connIdle = 60;
|
||||
}
|
||||
|
||||
return $this->success($json);
|
||||
} catch (\Exception $e) {
|
||||
return $this->error($e->getMessage(), 500);
|
||||
}
|
||||
}
|
||||
|
||||
protected function verifyToken(Request $request)
|
||||
{
|
||||
$token = $request->input('token');
|
||||
if (empty($token)) {
|
||||
return $this->error("token must be set");
|
||||
}
|
||||
if ($token !== config('v2board.server_token')) {
|
||||
return $this->error("invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
protected function error($msg, int $status = 400) {
|
||||
return response([
|
||||
'msg' => $msg,
|
||||
], $status);
|
||||
}
|
||||
|
||||
protected function success($data) {
|
||||
$req = request();
|
||||
// Only for "GET" method
|
||||
if (!$req->isMethod('GET') || !$data) {
|
||||
return response([
|
||||
'msg' => 'ok',
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
|
||||
$etag = sha1(json_encode($data));
|
||||
if ($etag == $req->header("IF-NONE-MATCH")) {
|
||||
return response(null, 304);
|
||||
}
|
||||
|
||||
return response([
|
||||
'msg' => 'ok',
|
||||
'data' => $data,
|
||||
])->header('ETAG', $etag);
|
||||
}
|
||||
}
|
@ -8,10 +8,6 @@ use App\Services\UserService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\User;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/*
|
||||
@ -41,7 +37,7 @@ class ShadowsocksTidalabController extends Controller
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$users = $serverService->getAvailableUsers($server->group_id);
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
array_push($result, [
|
||||
@ -72,23 +68,11 @@ class ShadowsocksTidalabController extends Controller
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_SHADOWSOCKS_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
if (!$userService->trafficFetch((float)$u, (float)$d, (int)$item['user_id'], $server, 'shadowsocks')) {
|
||||
continue;
|
||||
$userService->trafficFetch($u, $d, $item['user_id'], $server, 'shadowsocks');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'user fetch fail'
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'ret' => 1,
|
||||
|
@ -41,7 +41,7 @@ class TrojanTidalabController extends Controller
|
||||
}
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600);
|
||||
$serverService = new ServerService();
|
||||
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
|
||||
$users = $serverService->getAvailableUsers($server->group_id);
|
||||
$result = [];
|
||||
foreach ($users as $user) {
|
||||
$user->trojan_user = [
|
||||
@ -73,23 +73,11 @@ class TrojanTidalabController extends Controller
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
|
||||
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_PUSH_AT', $server->id), time(), 3600);
|
||||
$userService = new UserService();
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
foreach ($data as $item) {
|
||||
$u = $item['u'] * $server->rate;
|
||||
$d = $item['d'] * $server->rate;
|
||||
if (!$userService->trafficFetch($u, $d, $item['user_id'], $server, 'trojan')) {
|
||||
continue;
|
||||
$userService->trafficFetch($u, $d, $item['user_id'], $server, 'trojan');
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
return response([
|
||||
'ret' => 0,
|
||||
'msg' => 'user fetch fail'
|
||||
]);
|
||||
}
|
||||
DB::commit();
|
||||
|
||||
return response([
|
||||
'ret' => 1,
|
||||
|
@ -27,9 +27,8 @@ class CommController extends Controller
|
||||
->where('payment', 'StripeCredit')
|
||||
->first();
|
||||
if (!$payment) abort(500, 'payment is not found');
|
||||
$config = json_decode($payment->config, true);
|
||||
return response([
|
||||
'data' => $config['stripe_pk_live']
|
||||
'data' => $payment->config['stripe_pk_live']
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace App\Http\Controllers\User;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Services\CouponService;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Coupon;
|
||||
|
||||
@ -13,27 +14,12 @@ class CouponController extends Controller
|
||||
if (empty($request->input('code'))) {
|
||||
abort(500, __('Coupon cannot be empty'));
|
||||
}
|
||||
$coupon = Coupon::where('code', $request->input('code'))->first();
|
||||
if (!$coupon) {
|
||||
abort(500, __('Invalid coupon'));
|
||||
}
|
||||
if ($coupon->limit_use <= 0 && $coupon->limit_use !== NULL) {
|
||||
abort(500, __('This coupon is no longer available'));
|
||||
}
|
||||
if (time() < $coupon->started_at) {
|
||||
abort(500, __('This coupon has not yet started'));
|
||||
}
|
||||
if (time() > $coupon->ended_at) {
|
||||
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, __('The coupon code cannot be used for this subscription'));
|
||||
}
|
||||
}
|
||||
$couponService = new CouponService($request->input('code'));
|
||||
$couponService->setPlanId($request->input('plan_id'));
|
||||
$couponService->setUserId($request->session()->get('id'));
|
||||
$couponService->check();
|
||||
return response([
|
||||
'data' => $coupon
|
||||
'data' => $couponService->getCoupon()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -35,8 +35,6 @@ class KnowledgeController extends Controller
|
||||
}
|
||||
$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']);
|
||||
$knowledge['body'] = str_replace('{{subscribeUrl}}', $subscribeUrl, $knowledge['body']);
|
||||
$knowledge['body'] = str_replace('{{urlEncodeSubscribeUrl}}', urlencode($subscribeUrl), $knowledge['body']);
|
||||
$knowledge['body'] = str_replace(
|
||||
|
@ -108,7 +108,7 @@ class OrderController extends Controller
|
||||
$order->user_id = $request->session()->get('id');
|
||||
$order->plan_id = $plan->id;
|
||||
$order->cycle = $request->input('cycle');
|
||||
$order->trade_no = Helper::guid();
|
||||
$order->trade_no = Helper::generateOrderNo();
|
||||
$order->total_amount = $plan[$request->input('cycle')];
|
||||
|
||||
if ($request->input('coupon_code')) {
|
||||
@ -169,9 +169,8 @@ class OrderController extends Controller
|
||||
}
|
||||
// free process
|
||||
if ($order->total_amount <= 0) {
|
||||
$order->total_amount = 0;
|
||||
$order->status = 1;
|
||||
$order->save();
|
||||
$orderService = new OrderService($order);
|
||||
if (!$orderService->paid($order->trade_no)) abort(500, '');
|
||||
return response([
|
||||
'type' => -1,
|
||||
'data' => true
|
||||
|
@ -8,7 +8,7 @@ use App\Services\UserService;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerLog;
|
||||
use App\Models\User;
|
||||
|
||||
@ -36,16 +36,16 @@ class ServerController extends Controller
|
||||
$current = $request->input('current') ? $request->input('current') : 1;
|
||||
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
|
||||
$serverLogModel = ServerLog::where('user_id', $request->session()->get('id'))
|
||||
->orderBy('created_at', 'DESC');
|
||||
->orderBy('log_at', 'DESC');
|
||||
switch ($type) {
|
||||
case 0:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')));
|
||||
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')));
|
||||
break;
|
||||
case 1:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-d')) - 604800);
|
||||
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')) - 604800);
|
||||
break;
|
||||
case 2:
|
||||
$serverLogModel->where('created_at', '>=', strtotime(date('Y-m-1')));
|
||||
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-1')));
|
||||
}
|
||||
$total = $serverLogModel->count();
|
||||
$res = $serverLogModel->forPage($current, $pageSize)
|
||||
|
@ -168,7 +168,7 @@ class TicketController extends Controller
|
||||
$user = User::find($request->session()->get('id'));
|
||||
$limit = config('v2board.commission_withdraw_limit', 100);
|
||||
if ($limit > ($user->commission_balance / 100)) {
|
||||
abort(500, __('The current required minimum withdrawal commission is', ['limit' => $limit]));
|
||||
abort(500, __('The current required minimum withdrawal commission is :limit', ['limit' => $limit]));
|
||||
}
|
||||
DB::beginTransaction();
|
||||
$subject = __('[Commission Withdrawal Request] This ticket is opened by the system');
|
||||
|
@ -6,14 +6,16 @@ use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\User\UserTransfer;
|
||||
use App\Http\Requests\User\UserUpdate;
|
||||
use App\Http\Requests\User\UserChangePassword;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Models\Plan;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\Ticket;
|
||||
use App\Utils\Helper;
|
||||
use App\Models\Order;
|
||||
use App\Models\ServerLog;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
@ -33,6 +35,7 @@ class UserController extends Controller
|
||||
}
|
||||
if (!Helper::multiPasswordVerify(
|
||||
$user->password_algo,
|
||||
$user->password_salt,
|
||||
$request->input('old_password'),
|
||||
$user->password)
|
||||
) {
|
||||
@ -40,6 +43,7 @@ class UserController extends Controller
|
||||
}
|
||||
$user->password = password_hash($request->input('new_password'), PASSWORD_DEFAULT);
|
||||
$user->password_algo = NULL;
|
||||
$user->password_salt = NULL;
|
||||
if (!$user->save()) {
|
||||
abort(500, __('Save failed'));
|
||||
}
|
||||
@ -118,12 +122,7 @@ class UserController extends Controller
|
||||
abort(500, __('Subscription plan does not exist'));
|
||||
}
|
||||
}
|
||||
$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['subscribe_url'] = Helper::getSubscribeHost() . "/api/v1/client/subscribe?token={$user['token']}";
|
||||
$user['reset_day'] = $this->getResetDay($user);
|
||||
return response([
|
||||
'data' => $user
|
||||
@ -209,4 +208,26 @@ class UserController extends Controller
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getQuickLoginUrl(Request $request)
|
||||
{
|
||||
$user = User::find($request->session()->get('id'));
|
||||
if (!$user) {
|
||||
abort(500, __('The user does not exist'));
|
||||
}
|
||||
|
||||
$code = Helper::guid();
|
||||
$key = CacheKey::get('TEMP_TOKEN', $code);
|
||||
Cache::put($key, $user->id, 60);
|
||||
$redirect = '/#/login?verify=' . $code . '&redirect=' . ($request->input('redirect') ? $request->input('redirect') : 'dashboard');
|
||||
if (config('v2board.app_url')) {
|
||||
$url = config('v2board.app_url') . $redirect;
|
||||
} else {
|
||||
$url = url($redirect);
|
||||
}
|
||||
return response([
|
||||
'data' => $url
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ class Kernel extends HttpKernel
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
\App\Http\Middleware\CORS::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
|
@ -15,8 +15,9 @@ class User
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if ($request->input('auth_data')) {
|
||||
$authData = explode(':', base64_decode($request->input('auth_data')));
|
||||
$authorization = $request->input('auth_data') ?? $request->header('authorization');
|
||||
if ($authorization) {
|
||||
$authData = explode(':', base64_decode($authorization));
|
||||
if (!isset($authData[1]) || !isset($authData[0])) abort(403, '鉴权失败,请重新登入');
|
||||
$user = \App\Models\User::where('password', $authData[1])
|
||||
->where('email', $authData[0])
|
||||
|
@ -25,6 +25,10 @@ class ConfigSave extends FormRequest
|
||||
'commission_withdraw_limit' => 'nullable|numeric',
|
||||
'commission_withdraw_method' => 'nullable|array',
|
||||
'withdraw_close_enable' => 'in:0,1',
|
||||
'commission_distribution_enable' => 'in:0,1',
|
||||
'commission_distribution_l1' => 'nullable|numeric',
|
||||
'commission_distribution_l2' => 'nullable|numeric',
|
||||
'commission_distribution_l3' => 'nullable|numeric',
|
||||
// site
|
||||
'stop_register' => 'in:0,1',
|
||||
'email_verify' => 'in:0,1',
|
||||
@ -44,7 +48,7 @@ class ConfigSave extends FormRequest
|
||||
'tos_url' => 'nullable|url',
|
||||
// subscribe
|
||||
'plan_change_enable' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1',
|
||||
'reset_traffic_method' => 'in:0,1,2',
|
||||
'surplus_enable' => 'in:0,1',
|
||||
'new_order_event_id' => 'in:0,1',
|
||||
'renew_order_event_id' => 'in:0,1',
|
||||
@ -88,14 +92,11 @@ class ConfigSave extends FormRequest
|
||||
'frontend_theme' => '',
|
||||
'frontend_theme_sidebar' => 'in:dark,light',
|
||||
'frontend_theme_header' => 'in:dark,light',
|
||||
'frontend_theme_color' => 'in:default,darkblue,black',
|
||||
'frontend_theme_color' => 'in:default,darkblue,black,green',
|
||||
'frontend_background_url' => 'nullable|url',
|
||||
'frontend_admin_path' => '',
|
||||
'frontend_customer_service_method' => '',
|
||||
'frontend_customer_service_id' => '',
|
||||
// tutorial
|
||||
'apple_id' => 'nullable|email',
|
||||
'apple_id_password' => '',
|
||||
// email
|
||||
'email_template' => '',
|
||||
'email_host' => '',
|
||||
|
@ -21,6 +21,7 @@ class CouponGenerate extends FormRequest
|
||||
'started_at' => 'required|integer',
|
||||
'ended_at' => 'required|integer',
|
||||
'limit_use' => 'nullable|integer',
|
||||
'limit_use_with_user' => 'nullable|integer',
|
||||
'limit_plan_ids' => 'nullable|array',
|
||||
'code' => ''
|
||||
];
|
||||
@ -40,7 +41,8 @@ class CouponGenerate extends FormRequest
|
||||
'started_at.integer' => '开始时间格式有误',
|
||||
'ended_at.required' => '结束时间不能为空',
|
||||
'ended_at.integer' => '结束时间格式有误',
|
||||
'limit_use.integer' => '使用次数格式有误',
|
||||
'limit_use.integer' => '最大使用次数格式有误',
|
||||
'limit_use_with_user.integer' => '限制用户使用次数格式有误',
|
||||
'limit_plan_ids.array' => '指定订阅格式有误'
|
||||
];
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Admin;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class CouponSave extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'name' => 'required',
|
||||
'type' => 'required|in:1,2',
|
||||
'value' => 'required|integer',
|
||||
'started_at' => 'required|integer',
|
||||
'ended_at' => 'required|integer',
|
||||
'limit_use' => 'nullable|integer',
|
||||
'limit_plan_ids' => 'nullable|array',
|
||||
'code' => ''
|
||||
];
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'name.required' => '名称不能为空',
|
||||
'type.required' => '类型不能为空',
|
||||
'type.in' => '类型格式有误',
|
||||
'value.required' => '金额或比例不能为空',
|
||||
'value.integer' => '金额或比例格式有误',
|
||||
'started_at.required' => '开始时间不能为空',
|
||||
'started_at.integer' => '开始时间格式有误',
|
||||
'ended_at.required' => '结束时间不能为空',
|
||||
'ended_at.integer' => '结束时间格式有误',
|
||||
'limit_use.integer' => '使用次数格式有误',
|
||||
'limit_plan_ids.array' => '指定订阅格式有误'
|
||||
];
|
||||
}
|
||||
}
|
@ -25,7 +25,8 @@ class PlanSave extends FormRequest
|
||||
'two_year_price' => 'nullable|integer',
|
||||
'three_year_price' => 'nullable|integer',
|
||||
'onetime_price' => 'nullable|integer',
|
||||
'reset_price' => 'nullable|integer'
|
||||
'reset_price' => 'nullable|integer',
|
||||
'reset_traffic_method' => 'nullable|integer|in:0,1,2'
|
||||
];
|
||||
}
|
||||
|
||||
@ -44,7 +45,9 @@ class PlanSave extends FormRequest
|
||||
'two_year_price.integer' => '两年付金额格式有误',
|
||||
'three_year_price.integer' => '三年付金额格式有误',
|
||||
'onetime_price.integer' => '一次性金额有误',
|
||||
'reset_price.integer' => '流量重置包金额有误'
|
||||
'reset_price.integer' => '流量重置包金额有误',
|
||||
'reset_traffic_method.integer' => '流量重置方式格式有误',
|
||||
'reset_traffic_method.in' => '流量重置方式格式有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -26,10 +26,10 @@ class ServerV2raySave extends FormRequest
|
||||
'rate' => 'required|numeric',
|
||||
'alter_id' => 'required|integer',
|
||||
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
|
||||
'networkSettings' => '',
|
||||
'ruleSettings' => '',
|
||||
'tlsSettings' => '',
|
||||
'dnsSettings' => ''
|
||||
'networkSettings' => 'nullable|array',
|
||||
'ruleSettings' => 'nullable|array',
|
||||
'tlsSettings' => 'nullable|array',
|
||||
'dnsSettings' => 'nullable|array'
|
||||
];
|
||||
}
|
||||
|
||||
@ -48,7 +48,11 @@ class ServerV2raySave extends FormRequest
|
||||
'rate.required' => '倍率不能为空',
|
||||
'rate.numeric' => '倍率格式不正确',
|
||||
'network.required' => '传输协议不能为空',
|
||||
'network.in' => '传输协议格式不正确'
|
||||
'network.in' => '传输协议格式不正确',
|
||||
'networkSettings.array' => '传输协议配置有误',
|
||||
'ruleSettings.array' => '规则配置有误',
|
||||
'tlsSettings.array' => 'tls配置有误',
|
||||
'dnsSettings.array' => 'dns配置有误'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -63,9 +63,10 @@ class AdminRoute
|
||||
});
|
||||
// Order
|
||||
$router->get ('/order/fetch', 'Admin\\OrderController@fetch');
|
||||
$router->post('/order/repair', 'Admin\\OrderController@repair');
|
||||
$router->post('/order/update', 'Admin\\OrderController@update');
|
||||
$router->post('/order/assign', 'Admin\\OrderController@assign');
|
||||
$router->post('/order/paid', 'Admin\\OrderController@paid');
|
||||
$router->post('/order/cancel', 'Admin\\OrderController@cancel');
|
||||
// User
|
||||
$router->get ('/user/fetch', 'Admin\\UserController@fetch');
|
||||
$router->post('/user/update', 'Admin\\UserController@update');
|
||||
|
@ -20,6 +20,7 @@ class UserRoute
|
||||
$router->get ('/getSubscribe', 'User\\UserController@getSubscribe');
|
||||
$router->get ('/getStat', 'User\\UserController@getStat');
|
||||
$router->post('/transfer', 'User\\UserController@transfer');
|
||||
$router->post('/getQuickLoginUrl', 'User\\UserController@getQuickLoginUrl');
|
||||
// Order
|
||||
$router->post('/order/save', 'User\\OrderController@save');
|
||||
$router->post('/order/checkout', 'User\\OrderController@checkout');
|
||||
|
52
app/Jobs/OrderHandleJob.php
Normal file
52
app/Jobs/OrderHandleJob.php
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Order;
|
||||
use App\Services\OrderService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class OrderHandleJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $order;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($tradeNo)
|
||||
{
|
||||
$this->onQueue('order_handle');
|
||||
$this->order = Order::where('trade_no', $tradeNo)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->order) return;
|
||||
$orderService = new OrderService($this->order);
|
||||
switch ($this->order->status) {
|
||||
// cancel
|
||||
case 0:
|
||||
if ($this->order->created_at <= (time() - 1800)) {
|
||||
$orderService->cancel();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
$orderService->open();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
58
app/Jobs/ServerLogJob.php
Normal file
58
app/Jobs/ServerLogJob.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Services\ServerService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class ServerLogJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $u;
|
||||
protected $d;
|
||||
protected $userId;
|
||||
protected $server;
|
||||
protected $protocol;
|
||||
|
||||
public $tries = 3;
|
||||
public $timeout = 3;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($u, $d, $userId, $server, $protocol)
|
||||
{
|
||||
$this->onQueue('server_log');
|
||||
$this->u = $u;
|
||||
$this->d = $d;
|
||||
$this->userId = $userId;
|
||||
$this->server = $server;
|
||||
$this->protocol = $protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$serverService = new ServerService();
|
||||
if (!$serverService->log(
|
||||
$this->userId,
|
||||
$this->server->id,
|
||||
$this->u,
|
||||
$this->d,
|
||||
$this->server->rate,
|
||||
$this->protocol
|
||||
)) {
|
||||
throw new \Exception('日志记录失败');
|
||||
}
|
||||
}
|
||||
}
|
57
app/Jobs/TrafficFetchJob.php
Normal file
57
app/Jobs/TrafficFetchJob.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Services\MailService;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TrafficFetchJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
protected $u;
|
||||
protected $d;
|
||||
protected $userId;
|
||||
protected $server;
|
||||
protected $protocol;
|
||||
|
||||
public $tries = 3;
|
||||
public $timeout = 3;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($u, $d, $userId, $server, $protocol)
|
||||
{
|
||||
$this->onQueue('traffic_fetch');
|
||||
$this->u = $u;
|
||||
$this->d = $d;
|
||||
$this->userId = $userId;
|
||||
$this->server = $server;
|
||||
$this->protocol = $protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$user = User::lockForUpdate()->find($this->userId);
|
||||
if (!$user) return;
|
||||
|
||||
$user->t = time();
|
||||
$user->u = $user->u + $this->u;
|
||||
$user->d = $user->d + $this->d;
|
||||
if (!$user->save()) throw new \Exception('流量更新失败');
|
||||
$mailService = new MailService();
|
||||
$mailService->remindTraffic($user);
|
||||
}
|
||||
}
|
16
app/Models/CommissionLog.php
Normal file
16
app/Models/CommissionLog.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class CommissionLog extends Model
|
||||
{
|
||||
protected $table = 'v2_commission_log';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
@ -9,4 +9,9 @@ class Coupon extends Model
|
||||
protected $table = 'v2_coupon';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'limit_plan_ids' => 'array'
|
||||
];
|
||||
}
|
||||
|
@ -8,4 +8,8 @@ class InviteCode extends Model
|
||||
{
|
||||
protected $table = 'v2_invite_code';
|
||||
protected $dateFormat = 'U';
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class Knowledge extends Model
|
||||
protected $table = 'v2_knowledge';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class MailLog extends Model
|
||||
protected $table = 'v2_mail_log';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class Notice extends Model
|
||||
protected $table = 'v2_notice';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,9 @@ class Order extends Model
|
||||
protected $table = 'v2_order';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'surplus_order_ids' => 'array'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,9 @@ class Payment extends Model
|
||||
protected $table = 'v2_payment';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'config' => 'array'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class Plan extends Model
|
||||
protected $table = 'v2_plan';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -1,12 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Server extends Model
|
||||
{
|
||||
protected $table = 'v2_server';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
}
|
@ -8,4 +8,8 @@ class ServerGroup extends Model
|
||||
{
|
||||
protected $table = 'v2_server_group';
|
||||
protected $dateFormat = 'U';
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class ServerLog extends Model
|
||||
{
|
||||
protected $table = 'v2_server_log';
|
||||
protected $dateFormat = 'U';
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,10 @@ class ServerShadowsocks extends Model
|
||||
protected $table = 'v2_server_shadowsocks';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'group_id' => 'array',
|
||||
'tags' => 'array'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class ServerStat extends Model
|
||||
protected $table = 'v2_server_stat';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,10 @@ class ServerTrojan extends Model
|
||||
protected $table = 'v2_server_trojan';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'group_id' => 'array',
|
||||
'tags' => 'array'
|
||||
];
|
||||
}
|
||||
|
22
app/Models/ServerV2ray.php
Executable file
22
app/Models/ServerV2ray.php
Executable file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ServerV2ray extends Model
|
||||
{
|
||||
protected $table = 'v2_server_v2ray';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp',
|
||||
'group_id' => 'array',
|
||||
'tlsSettings' => 'array',
|
||||
'networkSettings' => 'array',
|
||||
'dnsSettings' => 'array',
|
||||
'ruleSettings' => 'array',
|
||||
'tags' => 'array'
|
||||
];
|
||||
}
|
@ -9,4 +9,8 @@ class StatOrder extends Model
|
||||
protected $table = 'v2_stat_order';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class StatServer extends Model
|
||||
protected $table = 'v2_stat_server';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class Ticket extends Model
|
||||
protected $table = 'v2_ticket';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class TicketMessage extends Model
|
||||
protected $table = 'v2_ticket_message';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -9,4 +9,8 @@ class User extends Model
|
||||
protected $table = 'v2_user';
|
||||
protected $dateFormat = 'U';
|
||||
protected $guarded = ['id'];
|
||||
protected $casts = [
|
||||
'created_at' => 'timestamp',
|
||||
'updated_at' => 'timestamp'
|
||||
];
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ namespace App\Payments;
|
||||
use \Curl\Curl;
|
||||
|
||||
class MGate {
|
||||
private $config;
|
||||
|
||||
public function __construct($config)
|
||||
{
|
||||
$this->config = $config;
|
||||
@ -47,6 +49,7 @@ class MGate {
|
||||
$str = http_build_query($params) . $this->config['mgate_app_secret'];
|
||||
$params['sign'] = md5($str);
|
||||
$curl = new Curl();
|
||||
$curl->setUserAgent('MGate');
|
||||
$curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
|
||||
$result = $curl->response;
|
||||
if (!$result) {
|
||||
|
43
app/Providers/HorizonServiceProvider.php
Normal file
43
app/Providers/HorizonServiceProvider.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Laravel\Horizon\Horizon;
|
||||
use Laravel\Horizon\HorizonApplicationServiceProvider;
|
||||
|
||||
class HorizonServiceProvider extends HorizonApplicationServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// Horizon::routeSmsNotificationsTo('15556667777');
|
||||
// Horizon::routeMailNotificationsTo('example@example.com');
|
||||
// Horizon::routeSlackNotificationsTo('slack-webhook-url', '#channel');
|
||||
|
||||
// Horizon::night();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the Horizon gate.
|
||||
*
|
||||
* This gate determines who can access Horizon in non-local environments.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function gate()
|
||||
{
|
||||
Gate::define('viewHorizon', function ($user) {
|
||||
return in_array($user->email, [
|
||||
//
|
||||
]);
|
||||
});
|
||||
}
|
||||
}
|
@ -8,27 +8,20 @@ use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CouponService
|
||||
{
|
||||
public $order;
|
||||
public $coupon;
|
||||
public $planId;
|
||||
public $userId;
|
||||
|
||||
public function __construct($code)
|
||||
{
|
||||
$this->coupon = Coupon::where('code', $code)->first();
|
||||
if (!$this->coupon) {
|
||||
abort(500, '优惠券无效');
|
||||
}
|
||||
if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
|
||||
abort(500, '优惠券已无可用次数');
|
||||
}
|
||||
if (time() < $this->coupon->started_at) {
|
||||
abort(500, '优惠券还未到可用时间');
|
||||
}
|
||||
if (time() > $this->coupon->ended_at) {
|
||||
abort(500, '优惠券已过期');
|
||||
}
|
||||
}
|
||||
|
||||
public function use(Order $order)
|
||||
public function use(Order $order):bool
|
||||
{
|
||||
$this->setPlanId($order->plan_id);
|
||||
$this->setUserId($order->user_id);
|
||||
$this->check();
|
||||
switch ($this->coupon->type) {
|
||||
case 1:
|
||||
$order->discount_amount = $this->coupon->value;
|
||||
@ -43,12 +36,6 @@ class CouponService
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ($this->coupon->limit_plan_ids) {
|
||||
$limitPlanIds = json_decode($this->coupon->limit_plan_ids);
|
||||
if (!in_array($order->plan_id, $limitPlanIds)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -56,4 +43,57 @@ class CouponService
|
||||
{
|
||||
return $this->coupon->id;
|
||||
}
|
||||
|
||||
public function getCoupon()
|
||||
{
|
||||
return $this->coupon;
|
||||
}
|
||||
|
||||
public function setPlanId($planId)
|
||||
{
|
||||
$this->planId = $planId;
|
||||
}
|
||||
|
||||
public function setUserId($userId)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
}
|
||||
|
||||
public function checkLimitUseWithUser():bool
|
||||
{
|
||||
$usedCount = Order::where('coupon_id', $this->coupon->id)
|
||||
->where('user_id', $this->userId)
|
||||
->whereNotIn('status', [0, 2])
|
||||
->count();
|
||||
if ($usedCount >= $this->coupon->limit_use_with_user) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public function check()
|
||||
{
|
||||
if (!$this->coupon) {
|
||||
abort(500, __('Invalid coupon'));
|
||||
}
|
||||
if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {
|
||||
abort(500, __('This coupon is no longer available'));
|
||||
}
|
||||
if (time() < $this->coupon->started_at) {
|
||||
abort(500, __('This coupon has not yet started'));
|
||||
}
|
||||
if (time() > $this->coupon->ended_at) {
|
||||
abort(500, __('This coupon has expired'));
|
||||
}
|
||||
if ($this->coupon->limit_plan_ids && $this->planId) {
|
||||
if (!in_array($this->planId, $this->coupon->limit_plan_ids)) {
|
||||
abort(500, __('The coupon code cannot be used for this subscription'));
|
||||
}
|
||||
}
|
||||
if ($this->coupon->limit_use_with_user !== NULL && $this->userId) {
|
||||
if (!$this->checkLimitUseWithUser()) {
|
||||
abort(500, __('The coupon can only be used :limit_use_with_user per person', [
|
||||
'limit_use_with_user' => $this->coupon->limit_use_with_user
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,13 +12,15 @@ class MailService
|
||||
public function remindTraffic (User $user)
|
||||
{
|
||||
if (!$user->remind_traffic) return;
|
||||
if (!$this->remindTrafficIsWarnValue(($user->u + $user->d), $user->transfer_enable)) return;
|
||||
if (!$this->remindTrafficIsWarnValue($user->u, $user->d, $user->transfer_enable)) return;
|
||||
$flag = CacheKey::get('LAST_SEND_EMAIL_REMIND_TRAFFIC', $user->id);
|
||||
if (Cache::get($flag)) return;
|
||||
if (!Cache::put($flag, 1, 24 * 3600)) return;
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => '在' . config('v2board.app_name', 'V2board') . '的流量使用已达到80%',
|
||||
'subject' => __('The traffic usage in :app_name has reached 80%', [
|
||||
'app_name' => config('v2board.app_name', 'V2board')
|
||||
]),
|
||||
'template_name' => 'remindTraffic',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
@ -27,8 +29,25 @@ class MailService
|
||||
]);
|
||||
}
|
||||
|
||||
private function remindTrafficIsWarnValue($ud, $transfer_enable)
|
||||
public function remindExpire(User $user)
|
||||
{
|
||||
if (!($user->expired_at !== NULL && ($user->expired_at - 86400) < time() && $user->expired_at > time())) return;
|
||||
SendEmailJob::dispatch([
|
||||
'email' => $user->email,
|
||||
'subject' => __('The service in :app_name is about to expire', [
|
||||
'app_name' => config('v2board.app_name', 'V2board')
|
||||
]),
|
||||
'template_name' => 'remindExpire',
|
||||
'template_value' => [
|
||||
'name' => config('v2board.app_name', 'V2Board'),
|
||||
'url' => config('v2board.app_url')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
private function remindTrafficIsWarnValue($u, $d, $transfer_enable)
|
||||
{
|
||||
$ud = $u + $d;
|
||||
if (!$ud) return false;
|
||||
if (!$transfer_enable) return false;
|
||||
$percentage = ($ud / $transfer_enable) * 100;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\OrderHandleJob;
|
||||
use App\Models\Order;
|
||||
use App\Models\Plan;
|
||||
use App\Models\User;
|
||||
@ -37,7 +38,7 @@ class OrderService
|
||||
DB::beginTransaction();
|
||||
if ($order->surplus_order_ids) {
|
||||
try {
|
||||
Order::whereIn('id', json_decode($order->surplus_order_ids))->update([
|
||||
Order::whereIn('id', $order->surplus_order_ids)->update([
|
||||
'status' => 4
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
@ -81,25 +82,6 @@ class OrderService
|
||||
DB::commit();
|
||||
}
|
||||
|
||||
public function cancel():bool
|
||||
{
|
||||
$order = $this->order;
|
||||
DB::beginTransaction();
|
||||
$order->status = 2;
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
if ($order->balance_amount) {
|
||||
$userService = new UserService();
|
||||
if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setOrderType(User $user)
|
||||
{
|
||||
@ -190,13 +172,13 @@ class OrderService
|
||||
$result = $trafficUnitPrice * $notUsedTraffic;
|
||||
$orderModel = Order::where('user_id', $user->id)->where('cycle', '!=', 'reset_price')->where('status', 3);
|
||||
$order->surplus_amount = $result > 0 ? $result : 0;
|
||||
$order->surplus_order_ids = json_encode(array_column($orderModel->get()->toArray(), 'id'));
|
||||
$order->surplus_order_ids = array_column($orderModel->get()->toArray(), 'id');
|
||||
}
|
||||
|
||||
private function orderIsUsed(Order $order):bool
|
||||
{
|
||||
$month = self::STR_TO_TIME[$order->cycle];
|
||||
$orderExpireDay = strtotime('+' . $month . ' month', $order->created_at->timestamp);
|
||||
$orderExpireDay = strtotime('+' . $month . ' month', $order->created_at);
|
||||
if ($orderExpireDay < time()) return true;
|
||||
return false;
|
||||
}
|
||||
@ -229,20 +211,40 @@ class OrderService
|
||||
return;
|
||||
}
|
||||
$order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
|
||||
$order->surplus_order_ids = json_encode(array_column($orders->toArray(), 'id'));
|
||||
$order->surplus_order_ids = array_column($orders->toArray(), 'id');
|
||||
}
|
||||
|
||||
public function success(string $callbackNo)
|
||||
public function paid(string $callbackNo)
|
||||
{
|
||||
$order = $this->order;
|
||||
if ($order->status !== 0) {
|
||||
if ($order->status !== 0) return true;
|
||||
$order->status = 1;
|
||||
$order->paid_at = time();
|
||||
$order->callback_no = $callbackNo;
|
||||
if (!$order->save()) return false;
|
||||
OrderHandleJob::dispatch($order->trade_no);
|
||||
return true;
|
||||
}
|
||||
$order->status = 1;
|
||||
$order->callback_no = $callbackNo;
|
||||
return $order->save();
|
||||
}
|
||||
|
||||
public function cancel():bool
|
||||
{
|
||||
$order = $this->order;
|
||||
DB::beginTransaction();
|
||||
$order->status = 2;
|
||||
if (!$order->save()) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
if ($order->balance_amount) {
|
||||
$userService = new UserService();
|
||||
if (!$userService->addBalance($order->user_id, $order->balance_amount)) {
|
||||
DB::rollBack();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
DB::commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
private function buyByResetTraffic()
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ class PaymentService
|
||||
if ($uuid) $payment = Payment::where('uuid', $uuid)->first()->toArray();
|
||||
$this->config = [];
|
||||
if (isset($payment)) {
|
||||
$this->config = json_decode($payment['config'], true);
|
||||
$this->config = $payment['config'];
|
||||
$this->config['enable'] = $payment['enable'];
|
||||
$this->config['id'] = $payment['id'];
|
||||
$this->config['uuid'] = $payment['uuid'];
|
||||
|
@ -5,7 +5,7 @@ namespace App\Services;
|
||||
use App\Models\ServerLog;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\User;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Utils\CacheKey;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
@ -18,14 +18,14 @@ class ServerService
|
||||
public function getV2ray(User $user, $all = false):array
|
||||
{
|
||||
$servers = [];
|
||||
$model = Server::orderBy('sort', 'ASC');
|
||||
$model = ServerV2ray::orderBy('sort', 'ASC');
|
||||
if (!$all) {
|
||||
$model->where('show', 1);
|
||||
}
|
||||
$v2ray = $model->get();
|
||||
for ($i = 0; $i < count($v2ray); $i++) {
|
||||
$v2ray[$i]['type'] = 'v2ray';
|
||||
$groupId = json_decode($v2ray[$i]['group_id']);
|
||||
$groupId = $v2ray[$i]['group_id'];
|
||||
if (in_array($user->group_id, $groupId)) {
|
||||
if ($v2ray[$i]['parent_id']) {
|
||||
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
|
||||
@ -50,7 +50,7 @@ class ServerService
|
||||
$trojan = $model->get();
|
||||
for ($i = 0; $i < count($trojan); $i++) {
|
||||
$trojan[$i]['type'] = 'trojan';
|
||||
$groupId = json_decode($trojan[$i]['group_id']);
|
||||
$groupId = $trojan[$i]['group_id'];
|
||||
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']));
|
||||
@ -73,7 +73,7 @@ class ServerService
|
||||
$shadowsocks = $model->get();
|
||||
for ($i = 0; $i < count($shadowsocks); $i++) {
|
||||
$shadowsocks[$i]['type'] = 'shadowsocks';
|
||||
$groupId = json_decode($shadowsocks[$i]['group_id']);
|
||||
$groupId = $shadowsocks[$i]['group_id'];
|
||||
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']));
|
||||
@ -123,7 +123,7 @@ class ServerService
|
||||
|
||||
public function getV2RayConfig(int $nodeId, int $localPort)
|
||||
{
|
||||
$server = Server::find($nodeId);
|
||||
$server = ServerV2ray::find($nodeId);
|
||||
if (!$server) {
|
||||
abort(500, '节点不存在');
|
||||
}
|
||||
@ -156,10 +156,10 @@ class ServerService
|
||||
return $json;
|
||||
}
|
||||
|
||||
private function setDns(Server $server, object $json)
|
||||
private function setDns(ServerV2ray $server, object $json)
|
||||
{
|
||||
if ($server->dnsSettings) {
|
||||
$dns = json_decode($server->dnsSettings);
|
||||
$dns = $server->dnsSettings;
|
||||
if (isset($dns->servers)) {
|
||||
array_push($dns->servers, '1.1.1.1');
|
||||
array_push($dns->servers, 'localhost');
|
||||
@ -169,41 +169,41 @@ class ServerService
|
||||
}
|
||||
}
|
||||
|
||||
private function setNetwork(Server $server, object $json)
|
||||
private function setNetwork(ServerV2ray $server, object $json)
|
||||
{
|
||||
if ($server->networkSettings) {
|
||||
switch ($server->network) {
|
||||
case 'tcp':
|
||||
$json->inbound->streamSettings->tcpSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->tcpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'kcp':
|
||||
$json->inbound->streamSettings->kcpSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->kcpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'ws':
|
||||
$json->inbound->streamSettings->wsSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->wsSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'http':
|
||||
$json->inbound->streamSettings->httpSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->httpSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'domainsocket':
|
||||
$json->inbound->streamSettings->dsSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->dsSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'quic':
|
||||
$json->inbound->streamSettings->quicSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->quicSettings = $server->networkSettings;
|
||||
break;
|
||||
case 'grpc':
|
||||
$json->inbound->streamSettings->grpcSettings = json_decode($server->networkSettings);
|
||||
$json->inbound->streamSettings->grpcSettings = $server->networkSettings;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setRule(Server $server, object $json)
|
||||
private function setRule(ServerV2ray $server, object $json)
|
||||
{
|
||||
$domainRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_domain')));
|
||||
$protocolRules = array_filter(explode(PHP_EOL, config('v2board.server_v2ray_protocol')));
|
||||
if ($server->ruleSettings) {
|
||||
$ruleSettings = json_decode($server->ruleSettings);
|
||||
$ruleSettings = $server->ruleSettings;
|
||||
// domain
|
||||
if (isset($ruleSettings->domain)) {
|
||||
$ruleSettings->domain = array_filter($ruleSettings->domain);
|
||||
@ -238,10 +238,10 @@ class ServerService
|
||||
}
|
||||
}
|
||||
|
||||
private function setTls(Server $server, object $json)
|
||||
private function setTls(ServerV2ray $server, object $json)
|
||||
{
|
||||
if ((int)$server->tls) {
|
||||
$tlsSettings = json_decode($server->tlsSettings);
|
||||
$tlsSettings = $server->tlsSettings;
|
||||
$json->inbound->streamSettings->security = 'tls';
|
||||
$tls = (object)[
|
||||
'certificateFile' => '/root/.cert/server.crt',
|
||||
@ -260,20 +260,23 @@ class ServerService
|
||||
|
||||
public function log(int $userId, int $serverId, int $u, int $d, float $rate, string $method)
|
||||
{
|
||||
if (($u + $d) <= 10240) return;
|
||||
$timestamp = strtotime(date('Y-m-d H:0'));
|
||||
if (($u + $d) < 10240) return true;
|
||||
$timestamp = strtotime(date('Y-m-d'));
|
||||
$serverLog = ServerLog::where('log_at', '>=', $timestamp)
|
||||
->where('log_at', '<', $timestamp + 3600)
|
||||
->where('server_id', $serverId)
|
||||
->where('user_id', $userId)
|
||||
->where('rate', $rate)
|
||||
->where('method', $method)
|
||||
->lockForUpdate()
|
||||
->first();
|
||||
if ($serverLog) {
|
||||
$serverLog->u = $serverLog->u + $u;
|
||||
$serverLog->d = $serverLog->d + $d;
|
||||
$serverLog->save();
|
||||
try {
|
||||
$serverLog->increment('u', $u);
|
||||
$serverLog->increment('d', $d);
|
||||
return true;
|
||||
} catch (\Exception $e) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$serverLog = new ServerLog();
|
||||
$serverLog->user_id = $userId;
|
||||
@ -283,7 +286,7 @@ class ServerService
|
||||
$serverLog->rate = $rate;
|
||||
$serverLog->log_at = $timestamp;
|
||||
$serverLog->method = $method;
|
||||
$serverLog->save();
|
||||
return $serverLog->save();
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,32 +295,15 @@ class ServerService
|
||||
$server = ServerShadowsocks::orderBy('sort', 'ASC')->get();
|
||||
for ($i = 0; $i < count($server); $i++) {
|
||||
$server[$i]['type'] = 'shadowsocks';
|
||||
if (!empty($server[$i]['tags'])) {
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
|
||||
public function getV2rayServers()
|
||||
{
|
||||
$server = Server::orderBy('sort', 'ASC')->get();
|
||||
$server = ServerV2ray::orderBy('sort', 'ASC')->get();
|
||||
for ($i = 0; $i < count($server); $i++) {
|
||||
$server[$i]['type'] = 'v2ray';
|
||||
if (!empty($server[$i]['tags'])) {
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
if (!empty($server[$i]['dnsSettings'])) {
|
||||
$server[$i]['dnsSettings'] = json_decode($server[$i]['dnsSettings']);
|
||||
}
|
||||
if (!empty($server[$i]['tlsSettings'])) {
|
||||
$server[$i]['tlsSettings'] = json_decode($server[$i]['tlsSettings']);
|
||||
}
|
||||
if (!empty($server[$i]['ruleSettings'])) {
|
||||
$server[$i]['ruleSettings'] = json_decode($server[$i]['ruleSettings']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
@ -327,10 +313,6 @@ class ServerService
|
||||
$server = ServerTrojan::orderBy('sort', 'ASC')->get();
|
||||
for ($i = 0; $i < count($server); $i++) {
|
||||
$server[$i]['type'] = 'trojan';
|
||||
if (!empty($server[$i]['tags'])) {
|
||||
$server[$i]['tags'] = json_decode($server[$i]['tags']);
|
||||
}
|
||||
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
|
||||
}
|
||||
return $server->toArray();
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ class TelegramService {
|
||||
$curl->get($this->api . $method . '?' . http_build_query($params));
|
||||
$response = $curl->response;
|
||||
$curl->close();
|
||||
if (!isset($response->ok)) abort(500, '请求失败');
|
||||
if (!$response->ok) {
|
||||
abort(500, '来自TG的错误:' . $response->description);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ use Illuminate\Support\Facades\DB;
|
||||
class TicketService {
|
||||
public function replyByAdmin($ticketId, $message, $userId):void
|
||||
{
|
||||
if ($message)
|
||||
$ticket = Ticket::where('id', $ticketId)
|
||||
->first();
|
||||
if (!$ticket) {
|
||||
|
@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Jobs\ServerLogJob;
|
||||
use App\Jobs\TrafficFetchJob;
|
||||
use App\Models\InviteCode;
|
||||
use App\Models\Order;
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\Ticket;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -80,33 +82,9 @@ class UserService
|
||||
return true;
|
||||
}
|
||||
|
||||
public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol):bool
|
||||
public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol)
|
||||
{
|
||||
$user = User::lockForUpdate()
|
||||
->find($userId);
|
||||
if (!$user) {
|
||||
return true;
|
||||
}
|
||||
$user->t = time();
|
||||
$user->u = $user->u + $u;
|
||||
$user->d = $user->d + $d;
|
||||
if (!$user->save()) {
|
||||
return false;
|
||||
}
|
||||
$mailService = new MailService();
|
||||
$serverService = new ServerService();
|
||||
try {
|
||||
$mailService->remindTraffic($user);
|
||||
$serverService->log(
|
||||
$userId,
|
||||
$server->id,
|
||||
$u,
|
||||
$d,
|
||||
$server->rate,
|
||||
$protocol
|
||||
);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
return true;
|
||||
TrafficFetchJob::dispatch($u, $d, $userId, $server, $protocol);
|
||||
ServerLogJob::dispatch($u, $d, $userId, $server, $protocol);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ class CacheKey
|
||||
'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
|
||||
'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
|
||||
'TEMP_TOKEN' => '临时令牌',
|
||||
'LAST_SEND_EMAIL_REMIND_TRAFFIC'
|
||||
'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒'
|
||||
];
|
||||
|
||||
public static function get(string $key, $uniqueValue)
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\ServerV2ray;
|
||||
use App\Models\ServerShadowsocks;
|
||||
use App\Models\ServerTrojan;
|
||||
use App\Models\User;
|
||||
@ -23,6 +23,12 @@ class Helper
|
||||
return md5(vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)) . '-' . time());
|
||||
}
|
||||
|
||||
public static function generateOrderNo(): string
|
||||
{
|
||||
$randomChar = rand(10000, 99999);
|
||||
return date('YmdHms') . $randomChar;
|
||||
}
|
||||
|
||||
public static function exchange($from, $to)
|
||||
{
|
||||
$result = file_get_contents('https://api.exchangerate.host/latest?symbols=' . $to . '&base=' . $from);
|
||||
@ -58,11 +64,12 @@ class Helper
|
||||
return $str;
|
||||
}
|
||||
|
||||
public static function multiPasswordVerify($algo, $password, $hash)
|
||||
public static function multiPasswordVerify($algo, $salt, $password, $hash)
|
||||
{
|
||||
switch($algo) {
|
||||
case 'md5': return md5($password) === $hash;
|
||||
case 'sha256': return hash('sha256', $password) === $hash;
|
||||
case 'md5salt': return md5($password . $salt) === $hash;
|
||||
default: return password_verify($password, $hash);
|
||||
}
|
||||
}
|
||||
@ -95,4 +102,14 @@ class Helper
|
||||
return round($byte, 2) . ' B';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getSubscribeHost()
|
||||
{
|
||||
$subscribeUrl = config('v2board.app_url');
|
||||
$subscribeUrls = explode(',', config('v2board.subscribe_url'));
|
||||
if ($subscribeUrls && $subscribeUrls[0]) {
|
||||
$subscribeUrl = $subscribeUrls[rand(0, count($subscribeUrls) - 1)];
|
||||
}
|
||||
return $subscribeUrl;
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,37 @@
|
||||
{
|
||||
"name": "v2board/v2board",
|
||||
"type": "project",
|
||||
"description": "v2board is a v2ray manage.",
|
||||
"description": "v2board is a proxy protocol manage.",
|
||||
"keywords": [
|
||||
"v2board",
|
||||
"v2ray",
|
||||
"shadowsocks",
|
||||
"trojan",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"require": {
|
||||
"php": "^7.2",
|
||||
"fideloper/proxy": "^4.0",
|
||||
"php": "^7.2.5|^8.0",
|
||||
"fideloper/proxy": "^4.4",
|
||||
"fruitcake/laravel-cors": "^2.0",
|
||||
"google/recaptcha": "^1.2",
|
||||
"laravel/framework": "^6.0",
|
||||
"laravel/tinker": "^1.0",
|
||||
"lokielse/omnipay-alipay": "3.0.6",
|
||||
"guzzlehttp/guzzle": "^6.3.1|^7.0.1",
|
||||
"laravel/framework": "^7.29",
|
||||
"laravel/horizon": "^4.3.5",
|
||||
"laravel/tinker": "^2.5",
|
||||
"linfo/linfo": "^4.0",
|
||||
"lokielse/omnipay-alipay": "3.1.2",
|
||||
"lokielse/omnipay-wechatpay": "^3.0",
|
||||
"php-curl-class/php-curl-class": "^8.6",
|
||||
"stripe/stripe-php": "^7.36.1",
|
||||
"symfony/yaml": "^4.3"
|
||||
},
|
||||
"require-dev": {
|
||||
"facade/ignition": "^1.4",
|
||||
"fzaninotto/faker": "^1.4",
|
||||
"mockery/mockery": "^1.0",
|
||||
"nunomaduro/collision": "^3.0",
|
||||
"phpunit/phpunit": "^8.0"
|
||||
"facade/ignition": "^2.0",
|
||||
"fakerphp/faker": "^1.9.1",
|
||||
"mockery/mockery": "^1.3.1",
|
||||
"nunomaduro/collision": "^4.3",
|
||||
"phpunit/phpunit": "^8.5.8|^9.3.3"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
|
@ -173,6 +173,7 @@ return [
|
||||
App\Providers\AuthServiceProvider::class,
|
||||
// App\Providers\BroadcastServiceProvider::class,
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\HorizonServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
|
||||
],
|
||||
@ -236,5 +237,5 @@ return [
|
||||
| The only modification by laravel config
|
||||
|
|
||||
*/
|
||||
'version' => '1.5.2.1627559775390'
|
||||
'version' => '1.5.3.1628409393360'
|
||||
];
|
||||
|
34
config/cors.php
Normal file
34
config/cors.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your settings for cross-origin resource sharing
|
||||
| or "CORS". This determines what cross-origin operations may execute
|
||||
| in web browsers. You are free to adjust these settings as needed.
|
||||
|
|
||||
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||
|
|
||||
*/
|
||||
|
||||
'paths' => ['api/*'],
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => ['*'],
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => false,
|
||||
|
||||
];
|
191
config/horizon.php
Normal file
191
config/horizon.php
Normal file
@ -0,0 +1,191 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Support\Str;
|
||||
use Linfo\Linfo;
|
||||
|
||||
$lInfo = new Linfo();
|
||||
$parser = $lInfo->getParser();
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Domain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the subdomain where Horizon will be accessible from. If this
|
||||
| setting is null, Horizon will reside under the same domain as the
|
||||
| application. Otherwise, this value will serve as the subdomain.
|
||||
|
|
||||
*/
|
||||
|
||||
'domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the URI path where Horizon will be accessible from. Feel free
|
||||
| to change this path to anything you like. Note that the URI will not
|
||||
| affect the paths of its internal API that aren't exposed to users.
|
||||
|
|
||||
*/
|
||||
|
||||
'path' => 'monitor',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Redis Connection
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This is the name of the Redis connection where Horizon will store the
|
||||
| meta information required for it to function. It includes the list
|
||||
| of supervisors, failed jobs, job metrics, and other information.
|
||||
|
|
||||
*/
|
||||
|
||||
'use' => 'default',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Redis Prefix
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This prefix will be used when storing all Horizon data in Redis. You
|
||||
| may modify the prefix when you are running multiple installations
|
||||
| of Horizon on the same server so that they don't have problems.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => env(
|
||||
'HORIZON_PREFIX',
|
||||
Str::slug(env('APP_NAME', 'laravel'), '_').'_horizon:'
|
||||
),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Horizon Route Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| These middleware will get attached onto each Horizon route, giving you
|
||||
| the chance to add your own middleware to this list or change any of
|
||||
| the existing middleware. Or, you can simply stick with this list.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => ['web', 'admin'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Wait Time Thresholds
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option allows you to configure when the LongWaitDetected event
|
||||
| will be fired. Every connection / queue combination may have its
|
||||
| own, unique threshold (in seconds) before this event is fired.
|
||||
|
|
||||
*/
|
||||
|
||||
'waits' => [
|
||||
'redis:default' => 60,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Job Trimming Times
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can configure for how long (in minutes) you desire Horizon to
|
||||
| persist the recent and failed jobs. Typically, recent jobs are kept
|
||||
| for one hour while all failed jobs are stored for an entire week.
|
||||
|
|
||||
*/
|
||||
|
||||
'trim' => [
|
||||
'recent' => 60,
|
||||
'pending' => 60,
|
||||
'completed' => 60,
|
||||
'recent_failed' => 10080,
|
||||
'failed' => 10080,
|
||||
'monitored' => 10080,
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metrics
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you can configure how many snapshots should be kept to display in
|
||||
| the metrics graph. This will get used in combination with Horizon's
|
||||
| `horizon:snapshot` schedule to define how long to retain metrics.
|
||||
|
|
||||
*/
|
||||
|
||||
'metrics' => [
|
||||
'trim_snapshots' => [
|
||||
'job' => 24,
|
||||
'queue' => 24,
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fast Termination
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When this option is enabled, Horizon's "terminate" command will not
|
||||
| wait on all of the workers to terminate unless the --wait option
|
||||
| is provided. Fast termination can shorten deployment delay by
|
||||
| allowing a new instance of Horizon to start while the last
|
||||
| instance will continue to terminate each of its workers.
|
||||
|
|
||||
*/
|
||||
|
||||
'fast_termination' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Memory Limit (MB)
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value describes the maximum amount of memory the Horizon worker
|
||||
| may consume before it is terminated and restarted. You should set
|
||||
| this value according to the resources available to your server.
|
||||
|
|
||||
*/
|
||||
|
||||
'memory_limit' => 32,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Queue Worker Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may define the queue worker settings used by your application
|
||||
| in all environments. These supervisors and settings handle all your
|
||||
| queued jobs and will be provisioned by Horizon during deployment.
|
||||
|
|
||||
*/
|
||||
|
||||
'environments' => [
|
||||
'local' => [
|
||||
'V2board' => [
|
||||
'connection' => 'redis',
|
||||
'queue' => [
|
||||
'traffic_fetch',
|
||||
'server_log',
|
||||
'send_email',
|
||||
'send_telegram',
|
||||
'stat_server',
|
||||
'order_handle'
|
||||
],
|
||||
'balance' => 'auto',
|
||||
'minProcesses' => 1,
|
||||
'maxProcesses' => (int)ceil($parser->getRam()['total'] / 1024 / 1024 / 1024 * 6),
|
||||
'tries' => 1,
|
||||
'nice' => 0,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
@ -19,6 +19,20 @@ CREATE TABLE `failed_jobs` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_commission_log`;
|
||||
CREATE TABLE `v2_commission_log` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`invite_user_id` int(11) NOT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`trade_no` char(36) NOT NULL,
|
||||
`order_amount` int(11) NOT NULL,
|
||||
`get_amount` int(11) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_coupon`;
|
||||
CREATE TABLE `v2_coupon` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
@ -27,6 +41,7 @@ CREATE TABLE `v2_coupon` (
|
||||
`type` tinyint(1) NOT NULL,
|
||||
`value` int(11) NOT NULL,
|
||||
`limit_use` int(11) DEFAULT NULL,
|
||||
`limit_use_with_user` int(11) DEFAULT NULL,
|
||||
`limit_plan_ids` varchar(255) DEFAULT NULL,
|
||||
`started_at` int(11) NOT NULL,
|
||||
`ended_at` int(11) NOT NULL,
|
||||
@ -110,6 +125,7 @@ CREATE TABLE `v2_order` (
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
|
||||
`commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
|
||||
`commission_balance` int(11) NOT NULL DEFAULT '0',
|
||||
`paid_at` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -149,34 +165,7 @@ CREATE TABLE `v2_plan` (
|
||||
`three_year_price` int(11) DEFAULT NULL,
|
||||
`onetime_price` int(11) DEFAULT NULL,
|
||||
`reset_price` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server`;
|
||||
CREATE TABLE `v2_server` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`parent_id` int(11) DEFAULT NULL,
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` text NOT NULL,
|
||||
`alter_id` int(11) NOT NULL DEFAULT '1',
|
||||
`settings` text,
|
||||
`rules` text,
|
||||
`networkSettings` text,
|
||||
`tlsSettings` text,
|
||||
`ruleSettings` text,
|
||||
`dnsSettings` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`reset_traffic_method` tinyint(1) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -206,7 +195,9 @@ CREATE TABLE `v2_server_log` (
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `log_at` (`log_at`)
|
||||
KEY `log_at` (`log_at`),
|
||||
KEY `user_id` (`user_id`),
|
||||
KEY `server_id` (`server_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
@ -251,6 +242,34 @@ CREATE TABLE `v2_server_trojan` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_server_v2ray`;
|
||||
CREATE TABLE `v2_server_v2ray` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`group_id` varchar(255) NOT NULL,
|
||||
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
|
||||
`parent_id` int(11) DEFAULT NULL,
|
||||
`host` varchar(255) NOT NULL,
|
||||
`port` int(11) NOT NULL,
|
||||
`server_port` int(11) NOT NULL,
|
||||
`tls` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`tags` varchar(255) DEFAULT NULL,
|
||||
`rate` varchar(11) NOT NULL,
|
||||
`network` text NOT NULL,
|
||||
`alter_id` int(11) NOT NULL DEFAULT '1',
|
||||
`settings` text,
|
||||
`rules` text,
|
||||
`networkSettings` text,
|
||||
`tlsSettings` text,
|
||||
`ruleSettings` text,
|
||||
`dnsSettings` text,
|
||||
`show` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`sort` int(11) DEFAULT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
DROP TABLE IF EXISTS `v2_stat_order`;
|
||||
CREATE TABLE `v2_stat_order` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
@ -304,7 +323,7 @@ CREATE TABLE `v2_ticket_message` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`ticket_id` int(11) NOT NULL,
|
||||
`message` varchar(255) NOT NULL,
|
||||
`message` text CHARACTER SET utf8mb4 NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
@ -319,6 +338,7 @@ CREATE TABLE `v2_user` (
|
||||
`email` varchar(64) NOT NULL,
|
||||
`password` varchar(64) NOT NULL,
|
||||
`password_algo` char(10) DEFAULT NULL,
|
||||
`password_salt` 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',
|
||||
@ -336,8 +356,8 @@ CREATE TABLE `v2_user` (
|
||||
`uuid` varchar(36) NOT NULL,
|
||||
`group_id` int(11) DEFAULT NULL,
|
||||
`plan_id` int(11) DEFAULT NULL,
|
||||
`remind_expire` tinyint(4) DEFAULT '1',
|
||||
`remind_traffic` tinyint(4) DEFAULT '1',
|
||||
`remind_expire` tinyint(4) DEFAULT '0',
|
||||
`remind_traffic` tinyint(4) DEFAULT '0',
|
||||
`token` char(32) NOT NULL,
|
||||
`remarks` text,
|
||||
`expired_at` bigint(20) DEFAULT '0',
|
||||
@ -348,4 +368,4 @@ CREATE TABLE `v2_user` (
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
|
||||
-- 2021-07-13 13:50:52
|
||||
-- 2021-09-21 10:07:22
|
||||
|
@ -425,3 +425,40 @@ 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`;
|
||||
|
||||
ALTER TABLE `v2_order`
|
||||
ADD `paid_at` int(11) NULL AFTER `commission_balance`;
|
||||
|
||||
ALTER TABLE `v2_server_log`
|
||||
ADD INDEX `user_id` (`user_id`),
|
||||
ADD INDEX `server_id` (`server_id`);
|
||||
|
||||
ALTER TABLE `v2_ticket_message`
|
||||
CHANGE `message` `message` text COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `ticket_id`;
|
||||
|
||||
ALTER TABLE `v2_coupon`
|
||||
ADD `limit_use_with_user` int(11) NULL AFTER `limit_use`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
ADD `password_salt` char(10) COLLATE 'utf8_general_ci' NULL AFTER `password_algo`;
|
||||
|
||||
CREATE TABLE `v2_commission_log` (
|
||||
`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
`invite_user_id` int(11) NOT NULL,
|
||||
`user_id` int(11) NOT NULL,
|
||||
`trade_no` char(36) NOT NULL,
|
||||
`order_amount` int(11) NOT NULL,
|
||||
`get_amount` int(11) NOT NULL,
|
||||
`created_at` int(11) NOT NULL,
|
||||
`updated_at` int(11) NOT NULL
|
||||
) COLLATE 'utf8mb4_general_ci';
|
||||
|
||||
ALTER TABLE `v2_plan`
|
||||
ADD `reset_traffic_method` tinyint(1) NULL AFTER `reset_price`;
|
||||
|
||||
ALTER TABLE `v2_server`
|
||||
RENAME TO `v2_server_v2ray`;
|
||||
|
||||
ALTER TABLE `v2_user`
|
||||
CHANGE `remind_expire` `remind_expire` tinyint(4) NULL DEFAULT '0' AFTER `plan_id`,
|
||||
CHANGE `remind_traffic` `remind_traffic` tinyint(4) NULL DEFAULT '0' AFTER `remind_expire`;
|
||||
|
1
init.sh
1
init.sh
@ -1,3 +1,4 @@
|
||||
rm -rf composer.phar
|
||||
wget https://getcomposer.org/download/2.0.13/composer.phar
|
||||
php composer.phar install -vvv
|
||||
php artisan v2board:install
|
||||
|
4
pm2.yaml
4
pm2.yaml
@ -1,5 +1,5 @@
|
||||
apps:
|
||||
- name : 'V2Board'
|
||||
script : 'php artisan queue:work --queue=send_email,send_telegram,stat_server'
|
||||
instances: 4
|
||||
script : 'php artisan horizon'
|
||||
instances: 1
|
||||
out_file : './storage/logs/queue/queue.log'
|
||||
|
4
public/assets/admin/theme/green.css
vendored
Normal file
4
public/assets/admin/theme/green.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2
public/assets/admin/umi.css
vendored
2
public/assets/admin/umi.css
vendored
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user