Merge pull request #270 from v2board/dev

1.3.1
This commit is contained in:
tokumeikoi 2020-07-05 14:19:50 +08:00 committed by GitHub
commit b5376c9c1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1743 additions and 533 deletions

View File

@ -117,7 +117,8 @@ class CheckOrder extends Command
$user->expired_at = time();
}
$user->transfer_enable = $plan->transfer_enable * 1073741824;
if ((int)config('v2board.renew_reset_traffic_enable', 1)) {
// 当续费清空流量或用户先前是一次性订阅
if ((int)config('v2board.renew_reset_traffic_enable', 1) || $user->expired_at === NULL) {
$user->u = 0;
$user->d = 0;
}

View File

@ -2,13 +2,12 @@
namespace App\Console\Commands;
use App\Utils\CacheKey;
use Illuminate\Console\Command;
use App\Models\User;
use App\Models\Order;
use App\Models\Server;
use App\Models\ServerLog;
use App\Utils\Helper;
use App\Models\ServerStat;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class V2boardCache extends Command
{
@ -44,4 +43,26 @@ class V2boardCache extends Command
public function handle()
{
}
private function cacheServerStat()
{
$serverLogs = ServerLog::select(
'server_id',
DB::raw("sum(u) as u"),
DB::raw("sum(d) as d"),
DB::raw("count(*) as online")
)
->where('updated_at', '>=', time() - 3600)
->groupBy('server_id')
->get();
foreach ($serverLogs as $serverLog) {
$data = [
'server_id' => $serverLog->server_id,
'u' => $serverLog->u,
'd' => $serverLog->d,
'online' => $serverLog->online
];
// ServerStat::create($data);
}
}
}

View File

@ -114,7 +114,7 @@ class V2boardInstall extends Command
abort(500, '管理员密码长度最小为8位字符');
}
$user->password = password_hash($password, PASSWORD_DEFAULT);
$user->v2ray_uuid = Helper::guid(true);
$user->uuid = Helper::guid(true);
$user->token = Helper::guid();
$user->is_admin = 1;
return $user->save();

View File

@ -76,6 +76,7 @@ class ConfigController extends Controller
// stripe
'stripe_alipay_enable' => (int)config('v2board.stripe_alipay_enable', 0),
'stripe_wepay_enable' => (int)config('v2board.stripe_wepay_enable', 0),
'stripe_card_enable' => (int)config('v2board.stripe_card_enable', 0),
'stripe_sk_live' => config('v2board.stripe_sk_live'),
'stripe_pk_live' => config('v2board.stripe_pk_live'),
'stripe_webhook_key' => config('v2board.stripe_webhook_key'),

View File

@ -12,24 +12,25 @@ class CouponController extends Controller
{
public function fetch(Request $request)
{
$coupons = Coupon::all();
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' => Coupon::all()
'data' => $coupons
]);
}
public function save(CouponSave $request)
{
$params = $request->only([
'name',
'type',
'value',
'started_at',
'ended_at',
'limit_use'
]);
$params = $request->only(array_keys(CouponSave::RULES));
if (isset($params['limit_plan_ids'])) {
$params['limit_plan_ids'] = json_encode($params['limit_plan_ids']);
}
if (!$request->input('id')) {
$params['code'] = Helper::randomChar(8);
if (!$params['code']) {
$params['code'] = Helper::randomChar(8);
}
if (!Coupon::create($params)) {
abort(500, '创建失败');
}

View File

@ -16,8 +16,27 @@ class PlanController extends Controller
{
public function fetch(Request $request)
{
$counts = User::select(
DB::raw("plan_id"),
DB::raw("count(*) as count")
)
->where('plan_id', '!=', NULL)
->where(function ($query) {
$query->where('expired_at', '>=', time())
->orWhere('expired_at', NULL);
})
->groupBy("plan_id")
->get();
$plans = Plan::orderBy('sort', 'ASC')->get();
foreach ($plans as $k => $v) {
$plans[$k]->count = 0;
foreach ($counts as $kk => $vv) {
if ($plans[$k]->id === $counts[$kk]->plan_id) $plans[$k]->count = $counts[$kk]->count;
}
}
return response([
'data' => Plan::orderBy('sort', 'ASC')->get()
'data' => $plans
]);
}

View File

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers\Admin\Server;
use App\Models\Plan;
use App\Models\Server;
use App\Models\ServerGroup;
use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class GroupController extends Controller
{
public function fetch(Request $request)
{
if ($request->input('group_id')) {
return response([
'data' => [ServerGroup::find($request->input('group_id'))]
]);
}
return response([
'data' => ServerGroup::get()
]);
}
public function save(Request $request)
{
if (empty($request->input('name'))) {
abort(500, '组名不能为空');
}
if ($request->input('id')) {
$serverGroup = ServerGroup::find($request->input('id'));
} else {
$serverGroup = new ServerGroup();
}
$serverGroup->name = $request->input('name');
return response([
'data' => $serverGroup->save()
]);
}
public function drop(Request $request)
{
if ($request->input('id')) {
$serverGroup = ServerGroup::find($request->input('id'));
if (!$serverGroup) {
abort(500, '组不存在');
}
}
$servers = Server::all();
foreach ($servers as $server) {
$groupId = json_decode($server->group_id);
if (in_array($request->input('id'), $groupId)) {
abort(500, '该组已被节点所使用,无法删除');
}
}
if (Plan::where('group_id', $request->input('id'))->first()) {
abort(500, '该组已被订阅所使用,无法删除');
}
if (User::where('group_id', $request->input('id'))->first()) {
abort(500, '该组已被用户所使用,无法删除');
}
return response([
'data' => $serverGroup->delete()
]);
}
}

View File

@ -0,0 +1,144 @@
<?php
namespace App\Http\Controllers\Admin\Server;
use App\Http\Requests\Admin\ServerTrojanSave;
use App\Http\Requests\Admin\ServerTrojanSort;
use App\Http\Requests\Admin\ServerTrojanUpdate;
use App\Services\ServerService;
use App\Utils\CacheKey;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ServerTrojan;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class TrojanController extends Controller
{
public function fetch(Request $request)
{
$server = ServerTrojan::orderBy('sort', 'ASC')->get();
for ($i = 0; $i < count($server); $i++) {
if (!empty($server[$i]['tags'])) {
$server[$i]['tags'] = json_decode($server[$i]['tags']);
}
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server[$i]['id']));
if ($server[$i]['parent_id']) {
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['parent_id']));
} else {
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server[$i]['id']));
}
}
return response([
'data' => $server
]);
}
public function save(ServerTrojanSave $request)
{
$params = $request->only(array_keys(ServerTrojanSave::RULES));
$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) {
abort(500, '服务器不存在');
}
try {
$server->update($params);
} catch (\Exception $e) {
abort(500, '保存失败');
}
return response([
'data' => true
]);
}
if (!ServerTrojan::create($params)) {
abort(500, '创建失败');
}
return response([
'data' => true
]);
}
public function drop(Request $request)
{
if ($request->input('id')) {
$server = ServerTrojan::find($request->input('id'));
if (!$server) {
abort(500, '节点ID不存在');
}
}
return response([
'data' => $server->delete()
]);
}
public function update(ServerTrojanUpdate $request)
{
$params = $request->only([
'show',
]);
$server = ServerTrojan::find($request->input('id'));
if (!$server) {
abort(500, '该服务器不存在');
}
try {
$server->update($params);
} catch (\Exception $e) {
abort(500, '保存失败');
}
return response([
'data' => true
]);
}
public function copy(Request $request)
{
$server = ServerTrojan::find($request->input('id'));
$server->show = 0;
if (!$server) {
abort(500, '服务器不存在');
}
if (!ServerTrojan::create($server->toArray())) {
abort(500, '复制失败');
}
return response([
'data' => true
]);
}
public function sort(ServerTrojanSort $request)
{
DB::beginTransaction();
foreach ($request->input('server_ids') as $k => $v) {
if (!ServerTrojan::find($v)->update(['sort' => $k + 1])) {
DB::rollBack();
abort(500, '保存失败');
}
}
DB::commit();
return response([
'data' => true
]);
}
public function viewConfig(Request $request)
{
$serverService = new ServerService();
$config = $serverService->getTrojanConfig($request->input('node_id'), 23333);
return response([
'data' => $config
]);
}
}

View File

@ -1,21 +1,19 @@
<?php
namespace App\Http\Controllers\Admin;
namespace App\Http\Controllers\Admin\Server;
use App\Http\Requests\Admin\ServerSave;
use App\Http\Requests\Admin\ServerSort;
use App\Http\Requests\Admin\ServerUpdate;
use App\Http\Requests\Admin\ServerV2raySave;
use App\Http\Requests\Admin\ServerV2raySort;
use App\Http\Requests\Admin\ServerV2rayUpdate;
use App\Services\ServerService;
use App\Utils\CacheKey;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ServerGroup;
use App\Models\Server;
use App\Models\Plan;
use App\Models\User;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
class ServerController extends Controller
class V2rayController extends Controller
{
public function fetch(Request $request)
{
@ -25,10 +23,11 @@ class ServerController extends Controller
$server[$i]['tags'] = json_decode($server[$i]['tags']);
}
$server[$i]['group_id'] = json_decode($server[$i]['group_id']);
$server[$i]['online'] = Cache::get(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server[$i]['parent_id'] ? $server[$i]['parent_id'] : $server[$i]['id']));
if ($server[$i]['parent_id']) {
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['parent_id']);
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['parent_id']));
} else {
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['id']);
$server[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $server[$i]['id']));
}
}
return response([
@ -36,9 +35,9 @@ class ServerController extends Controller
]);
}
public function save(ServerSave $request)
public function save(ServerV2raySave $request)
{
$params = $request->only(array_keys(ServerSave::RULES));
$params = $request->only(array_keys(ServerV2raySave::RULES));
$params['group_id'] = json_encode($params['group_id']);
if (isset($params['tags'])) {
$params['tags'] = json_encode($params['tags']);
@ -92,64 +91,6 @@ class ServerController extends Controller
]);
}
public function groupFetch(Request $request)
{
if ($request->input('group_id')) {
return response([
'data' => [ServerGroup::find($request->input('group_id'))]
]);
}
return response([
'data' => ServerGroup::get()
]);
}
public function groupSave(Request $request)
{
if (empty($request->input('name'))) {
abort(500, '组名不能为空');
}
if ($request->input('id')) {
$serverGroup = ServerGroup::find($request->input('id'));
} else {
$serverGroup = new ServerGroup();
}
$serverGroup->name = $request->input('name');
return response([
'data' => $serverGroup->save()
]);
}
public function groupDrop(Request $request)
{
if ($request->input('id')) {
$serverGroup = ServerGroup::find($request->input('id'));
if (!$serverGroup) {
abort(500, '组不存在');
}
}
$servers = Server::all();
foreach ($servers as $server) {
$groupId = json_decode($server->group_id);
if (in_array($request->input('id'), $groupId)) {
abort(500, '该组已被节点所使用,无法删除');
}
}
if (Plan::where('group_id', $request->input('id'))->first()) {
abort(500, '该组已被订阅所使用,无法删除');
}
if (User::where('group_id', $request->input('id'))->first()) {
abort(500, '该组已被用户所使用,无法删除');
}
return response([
'data' => $serverGroup->delete()
]);
}
public function drop(Request $request)
{
if ($request->input('id')) {
@ -163,7 +104,7 @@ class ServerController extends Controller
]);
}
public function update(ServerUpdate $request)
public function update(ServerV2rayUpdate $request)
{
$params = $request->only([
'show',
@ -188,6 +129,7 @@ class ServerController extends Controller
public function copy(Request $request)
{
$server = Server::find($request->input('id'));
$server->show = 0;
if (!$server) {
abort(500, '服务器不存在');
}
@ -203,13 +145,13 @@ class ServerController extends Controller
public function viewConfig(Request $request)
{
$serverService = new ServerService();
$config = $serverService->getConfig($request->input('node_id'), 23333);
$config = $serverService->getVmessConfig($request->input('node_id'), 23333);
return response([
'data' => $config
]);
}
public function sort(ServerSort $request)
public function sort(ServerV2raySort $request)
{
DB::beginTransaction();
foreach ($request->input('server_ids') as $k => $v) {

View File

@ -3,12 +3,12 @@
namespace App\Http\Controllers\Client;
use App\Http\Controllers\Controller;
use App\Services\ServerService;
use App\Services\UserService;
use App\Utils\Clash;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Plan;
use App\Models\Server;
use App\Models\Notice;
use App\Utils\Helper;
use Symfony\Component\Yaml\Yaml;
class AppController extends Controller
{
@ -16,38 +16,40 @@ class AppController extends Controller
CONST SOCKS_PORT = 10010;
CONST HTTP_PORT = 10011;
// TODO: 1.1.1 abolish
public function data(Request $request)
public function getConfig(Request $request)
{
$server = [];
$user = $request->user;
$nodes = [];
if ($user->plan_id) {
$user['plan'] = Plan::find($user->plan_id);
if (!$user['plan']) {
abort(500, '订阅计划不存在');
}
if ($user->expired_at > time()) {
$servers = Server::where('show', 1)
->orderBy('name')
->get();
foreach ($servers as $item) {
$groupId = json_decode($item['group_id']);
if (in_array($user->group_id, $groupId)) {
array_push($nodes, $item);
}
}
}
$userService = new UserService();
if ($userService->isAvailable($user)) {
$serverService = new ServerService();
$servers = $serverService->getAllServers($user);
}
$config = Yaml::parseFile(base_path() . '/resources/rules/app.clash.yaml');
$proxy = [];
$proxies = [];
foreach ($servers['vmess'] as $item) {
array_push($proxy, Clash::buildVmess($user->uuid, $item));
array_push($proxies, $item->name);
}
foreach ($servers['trojan'] as $item) {
array_push($proxy, Clash::buildTrojan($user->uuid, $item));
array_push($proxies, $item->name);
}
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
foreach ($config['proxy-groups'] as $k => $v) {
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
die(Yaml::dump($config));
}
public function getVersion()
{
return response([
'data' => [
'nodes' => $nodes,
'u' => $user->u,
'd' => $user->d,
'transfer_enable' => $user->transfer_enable,
'expired_at' => $user->expired_at,
'plan' => isset($user['plan']) ? $user['plan'] : false,
'notice' => Notice::orderBy('created_at', 'DESC')->first()
]
'data' => '4.0.0'
]);
}
@ -74,7 +76,7 @@ class AppController extends Controller
//other
$json->outbound->settings->vnext[0]->address = (string)$server->host;
$json->outbound->settings->vnext[0]->port = (int)$server->port;
$json->outbound->settings->vnext[0]->users[0]->id = (string)$user->v2ray_uuid;
$json->outbound->settings->vnext[0]->users[0]->id = (string)$user->uuid;
$json->outbound->settings->vnext[0]->users[0]->alterId = (int)$user->v2ray_alter_id;
$json->outbound->settings->vnext[0]->remark = (string)$server->name;
$json->outbound->streamSettings->network = $server->network;

View File

@ -3,6 +3,10 @@
namespace App\Http\Controllers\Client;
use App\Http\Controllers\Controller;
use App\Services\ServerService;
use App\Utils\Clash;
use App\Utils\QuantumultX;
use App\Utils\Surge;
use Illuminate\Http\Request;
use App\Models\Server;
use App\Utils\Helper;
@ -14,76 +18,41 @@ class ClientController extends Controller
public function subscribe(Request $request)
{
$user = $request->user;
$server = [];
// account not expired and is not banned.
$userService = new UserService();
if ($userService->isAvailable($user)) {
$servers = Server::where('show', 1)
->orderBy('sort', 'ASC')
->get();
foreach ($servers as $item) {
$groupId = json_decode($item['group_id']);
if (in_array($user->group_id, $groupId)) {
array_push($server, $item);
}
}
}
if (isset($_SERVER['HTTP_USER_AGENT'])) {
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult%20X') !== false) {
die($this->quantumultX($user, $server));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Quantumult') !== false) {
die($this->quantumult($user, $server));
}
if (strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'clash') !== false) {
die($this->clash($user, $server));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Surfboard') !== false) {
die($this->surfboard($user, $server));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'Surge') !== false) {
die($this->surge($user, $server));
}
}
die($this->origin($user, $server));
}
$serverService = new ServerService();
$servers = $serverService->getAllServers($user);
private function quantumultX($user, $server)
{
$uri = '';
foreach ($server as $item) {
$uri .= "vmess=" . $item->host . ":" . $item->port . ", method=none, password=" . $user->v2ray_uuid . ", fast-open=false, udp-relay=false, tag=" . $item->name;
if ($item->tls) {
$tlsSettings = json_decode($item->tlsSettings);
if ($item->network === 'tcp') $uri .= ', obfs=over-tls';
if (isset($tlsSettings->allowInsecure)) {
// Default: tls-verification=true
$uri .= ', tls-verification=' . ($tlsSettings->allowInsecure ? "false" : "true");
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$_SERVER['HTTP_USER_AGENT'] = strtolower($_SERVER['HTTP_USER_AGENT']);
if (strpos($_SERVER['HTTP_USER_AGENT'], 'quantumult%20x') !== false) {
die($this->quantumultX($user, $servers['vmess'], $servers['trojan']));
}
if (isset($tlsSettings->serverName)) {
$uri .= ', obfs-host=' . $tlsSettings->serverName;
if (strpos($_SERVER['HTTP_USER_AGENT'], 'quantumult') !== false) {
die($this->quantumult($user, $servers['vmess']));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'clash') !== false) {
die($this->clash($user, $servers['vmess'], $servers['trojan']));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'surfboard') !== false) {
die($this->surfboard($user, $servers['vmess']));
}
if (strpos($_SERVER['HTTP_USER_AGENT'], 'surge') !== false) {
die($this->surge($user, $servers['vmess'], $servers['trojan']));
}
}
if ($item->network === 'ws') {
$uri .= ', obfs=' . ($item->tls ? 'wss' : 'ws');
if ($item->networkSettings) {
$wsSettings = json_decode($item->networkSettings);
if (isset($wsSettings->path)) $uri .= ', obfs-uri=' . $wsSettings->path;
if (isset($wsSettings->headers->Host)) $uri .= ', obfs-host=' . $wsSettings->headers->Host;
}
}
$uri .= "\r\n";
die($this->origin($user, $servers['vmess'], $servers['trojan']));
}
return base64_encode($uri);
}
private function quantumult($user, $server)
// TODO: Ready to stop support
private function quantumult($user, $vmess = [])
{
$uri = '';
header('subscription-userinfo: upload=' . $user->u . '; download=' . $user->d . ';total=' . $user->transfer_enable);
foreach ($server as $item) {
foreach ($vmess as $item) {
$str = '';
$str .= $item->name . '= vmess, ' . $item->host . ', ' . $item->port . ', chacha20-ietf-poly1305, "' . $user->v2ray_uuid . '", over-tls=' . ($item->tls ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
$str .= $item->name . '= vmess, ' . $item->host . ', ' . $item->port . ', chacha20-ietf-poly1305, "' . $user->uuid . '", over-tls=' . ($item->tls ? "true" : "false") . ', certificate=0, group=' . config('v2board.app_name', 'V2Board');
if ($item->network === 'ws') {
$str .= ', obfs=ws';
if ($item->networkSettings) {
@ -97,38 +66,44 @@ class ClientController extends Controller
return base64_encode($uri);
}
private function origin($user, $server)
private function quantumultX($user, $vmess = [], $trojan = [])
{
$uri = '';
foreach ($server as $item) {
$uri .= Helper::buildVmessLink($item, $user);
foreach ($vmess as $item) {
$uri .= QuantumultX::buildVmess($user->uuid, $item);
}
foreach ($trojan as $item) {
$uri .= QuantumultX::buildTrojan($user->uuid, $item);
}
return base64_encode($uri);
}
private function surge($user, $server)
private function origin($user, $vmess = [], $trojan = [])
{
$uri = '';
foreach ($vmess as $item) {
$uri .= Helper::buildVmessLink($item, $user);
}
foreach ($trojan as $item) {
$uri .= Helper::buildTrojanLink($item, $user);
}
return base64_encode($uri);
}
private function surge($user, $vmess = [], $trojan = [])
{
$proxies = '';
$proxyGroup = '';
foreach ($server as $item) {
foreach ($vmess as $item) {
// [Proxy]
$proxies .= $item->name . ' = vmess, ' . $item->host . ', ' . $item->port . ', username=' . $user->v2ray_uuid . ', tfo=true';
if ($item->tls) {
$tlsSettings = json_decode($item->tlsSettings);
$proxies .= ', tls=' . ($item->tls ? "true" : "false");
if (isset($tlsSettings->allowInsecure)) {
$proxies .= ', skip-cert-verify=' . ($tlsSettings->allowInsecure ? "true" : "false");
}
}
if ($item->network == 'ws') {
$proxies .= ', ws=true';
if ($item->networkSettings) {
$wsSettings = json_decode($item->networkSettings);
if (isset($wsSettings->path)) $proxies .= ', ws-path=' . $wsSettings->path;
if (isset($wsSettings->headers->Host)) $proxies .= ', ws-headers=host:' . $wsSettings->headers->Host;
}
}
$proxies .= "\r\n";
$proxies .= Surge::buildVmess($user->uuid, $item);
// [Proxy Group]
$proxyGroup .= $item->name . ', ';
}
foreach ($trojan as $item) {
// [Proxy]
$proxies .= Surge::buildTrojan($user->uuid, $item);
// [Proxy Group]
$proxyGroup .= $item->name . ', ';
}
@ -142,30 +117,21 @@ class ClientController extends Controller
}
// Subscription link
$subsURL = 'http';
if (isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on') {
$subsURL .= 's';
}
$subsURL .= '://';
if ($_SERVER['SERVER_PORT'] != ('80' || '443')) {
$subsURL .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
} else {
$subsURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
}
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
$config = str_replace('$subs_link',$subsURL,$config);
$config = str_replace('$proxies',$proxies,$config);
$config = str_replace('$proxy_group',rtrim($proxyGroup, ', '),$config);
$config = str_replace('$subs_link', $subsURL, $config);
$config = str_replace('$proxies', $proxies, $config);
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
return $config;
}
private function surfboard($user, $server)
private function surfboard($user, $vmess = [])
{
$proxies = '';
$proxyGroup = '';
foreach ($server as $item) {
foreach ($vmess as $item) {
// [Proxy]
$proxies .= $item->name . ' = vmess, ' . $item->host . ', ' . $item->port . ', username=' . $user->v2ray_uuid;
$proxies .= $item->name . ' = vmess, ' . $item->host . ', ' . $item->port . ', username=' . $user->uuid;
if ($item->tls) {
$tlsSettings = json_decode($item->tlsSettings);
$proxies .= ', tls=' . ($item->tls ? "true" : "false");
@ -195,24 +161,15 @@ class ClientController extends Controller
}
// Subscription link
$subsURL = 'http';
if (isset( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) == 'on') {
$subsURL .= 's';
}
$subsURL .= '://';
if ($_SERVER['SERVER_PORT'] != ('80' || '443')) {
$subsURL .= $_SERVER['SERVER_NAME'] . ':' . $_SERVER['SERVER_PORT'] . $_SERVER['REQUEST_URI'];
} else {
$subsURL .= $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
}
$subsURL = config('v2board.subscribe_url', config('v2board.app_url', env('APP_URL'))) . '/api/v1/client/subscribe?token=' . $user['token'];
$config = str_replace('$subs_link',$subsURL,$config);
$config = str_replace('$proxies',$proxies,$config);
$config = str_replace('$proxy_group',rtrim($proxyGroup, ', '),$config);
$config = str_replace('$subs_link', $subsURL, $config);
$config = str_replace('$proxies', $proxies, $config);
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
return $config;
}
private function clash($user, $server)
private function clash($user, $vmess = [], $trojan = [])
{
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
@ -223,37 +180,20 @@ class ClientController extends Controller
}
$proxy = [];
$proxies = [];
foreach ($server as $item) {
$array = [];
$array['name'] = $item->name;
$array['type'] = 'vmess';
$array['server'] = $item->host;
$array['port'] = $item->port;
$array['uuid'] = $user->v2ray_uuid;
$array['alterId'] = $user->v2ray_alter_id;
$array['cipher'] = 'auto';
if ($item->tls) {
$tlsSettings = json_decode($item->tlsSettings);
$array['tls'] = true;
if (isset($tlsSettings->allowInsecure)) $array['skip-cert-verify'] = ($tlsSettings->allowInsecure ? true : false );
}
if ($item->network == 'ws') {
$array['network'] = $item->network;
if ($item->networkSettings) {
$wsSettings = json_decode($item->networkSettings);
if (isset($wsSettings->path)) $array['ws-path'] = $wsSettings->path;
if (isset($wsSettings->headers->Host)) $array['ws-headers'] = [
'Host' => $wsSettings->headers->Host
];
}
}
array_push($proxy, $array);
foreach ($vmess as $item) {
array_push($proxy, Clash::buildVmess($user->uuid, $item));
array_push($proxies, $item->name);
}
$config['Proxy'] = array_merge($config['Proxy'] ? $config['Proxy'] : [], $proxy);
foreach ($config['Proxy Group'] as $k => $v) {
$config['Proxy Group'][$k]['proxies'] = array_merge($config['Proxy Group'][$k]['proxies'], $proxies);
foreach ($trojan as $item) {
array_push($proxy, Clash::buildTrojan($user->uuid, $item));
array_push($proxies, $item->name);
}
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
foreach ($config['proxy-groups'] as $k => $v) {
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
$yaml = Yaml::dump($config);
$yaml = str_replace('$app_name', config('v2board.app_name', 'V2Board'), $yaml);

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Guest;
use App\Services\OrderService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\Order;
@ -66,22 +67,26 @@ class OrderController extends Controller
}
switch ($event->type) {
case 'source.chargeable':
$source = $event->data->object;
$charge = \Stripe\Charge::create([
'amount' => $source['amount'],
'currency' => $source['currency'],
'source' => $source['id'],
'description' => config('v2board.app_name', 'V2Board') . $source['metadata']['invoice_id'],
$object = $event->data->object;
\Stripe\Charge::create([
'amount' => $object->amount,
'currency' => $object->currency,
'source' => $object->id,
'metadata' => json_decode($object->metadata, true)
]);
if ($charge['status'] == 'succeeded') {
$trade_no = Cache::get($source['id']);
if (!$trade_no) {
abort(500, 'redis is not found trade no by stripe source id');
die('success');
break;
case 'charge.succeeded':
$object = $event->data->object;
if ($object->status === 'succeeded') {
$metaData = isset($object->metadata->out_trade_no) ? $object->metadata : $object->source->metadata;
$tradeNo = $metaData->out_trade_no;
if (!$tradeNo) {
abort(500, 'trade no is not found in metadata');
}
if (!$this->handle($trade_no, $source['id'])) {
if (!$this->handle($tradeNo, $object->balance_transaction)) {
abort(500, 'fail');
}
Cache::forget($source['id']);
die('success');
}
break;
@ -143,11 +148,7 @@ class OrderController extends Controller
if (!$order) {
abort(500, 'order is not found');
}
if ($order->status !== 0) {
return true;
}
$order->status = 1;
$order->callback_no = $callbackNo;
return $order->save();
$orderService = new OrderService($order);
return $orderService->success($callbackNo);
}
}

View File

@ -29,6 +29,8 @@ class TelegramController extends Controller
break;
case '/traffic': $this->traffic();
break;
case '/getlatesturl': $this->getLatestUrl();
break;
default: $this->help();
}
} catch (\Exception $e) {
@ -84,7 +86,8 @@ class TelegramController extends Controller
$telegramService = new TelegramService();
$commands = [
'/bind 订阅地址 - 绑定你的' . config('v2board.app_name', 'V2Board') . '账号',
'/traffic - 查询流量信息'
'/traffic - 查询流量信息',
'/getlatesturl - 获取最新的' . config('v2board.app_name', 'V2Board') . '网址'
];
$text = implode(PHP_EOL, $commands);
$telegramService->sendMessage($msg->chat_id, "你可以使用以下命令进行操作:\n\n$text", 'markdown');
@ -108,4 +111,17 @@ class TelegramController extends Controller
$text = "🚥流量查询\n———————————————\n计划流量:`{$transferEnable}`\n已用上行:`{$up}`\n已用下行:`{$down}`\n剩余流量:`{$remaining}`";
$telegramService->sendMessage($msg->chat_id, $text, 'markdown');
}
private function getLatestUrl()
{
$msg = $this->msg;
$user = User::where('telegram_id', $msg->chat_id)->first();
$telegramService = new TelegramService();
$text = sprintf(
"%s的最新网址是%s",
config('v2board.app_name', 'V2Board'),
config('v2board.app_url')
);
$telegramService->sendMessage($msg->chat_id, $text, 'markdown');
}
}

View File

@ -58,7 +58,7 @@ class AuthController extends Controller
$user = new User();
$user->email = $email;
$user->password = password_hash($password, PASSWORD_DEFAULT);
$user->v2ray_uuid = Helper::guid(true);
$user->uuid = Helper::guid(true);
$user->token = Helper::guid();
if ($request->input('invite_code')) {
$inviteCode = InviteCode::where('code', $request->input('invite_code'))

View File

@ -4,6 +4,7 @@ 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;
@ -13,10 +14,12 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
/*
* V2ray Aurora
* Github: https://github.com/tokumeikoi/aurora
*/
class DeepbworkController extends Controller
{
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
public function __construct(Request $request)
{
$token = $request->input('token');
@ -36,18 +39,18 @@ class DeepbworkController extends Controller
if (!$server) {
abort(500, 'fail');
}
Cache::put('server_last_check_at_' . $server->id, time());
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->v2ray_uuid,
"email" => sprintf("%s@v2board.user", $user->v2ray_uuid),
"uuid" => $user->uuid,
"email" => sprintf("%s@v2board.user", $user->uuid),
"alter_id" => $user->v2ray_alter_id,
"level" => $user->v2ray_level,
];
unset($user['v2ray_uuid']);
unset($user['uuid']);
unset($user['v2ray_alter_id']);
unset($user['v2ray_level']);
array_push($result, $user);
@ -71,6 +74,7 @@ class DeepbworkController extends Controller
}
$data = file_get_contents('php://input');
$data = json_decode($data, true);
Cache::put(CacheKey::get('SERVER_V2RAY_ONLINE_USER', $server->id), count($data), 3600);
$serverService = new ServerService();
$userService = new UserService();
foreach ($data as $item) {
@ -88,7 +92,8 @@ class DeepbworkController extends Controller
$request->input('node_id'),
$item['u'],
$item['d'],
$server->rate
$server->rate,
'vmess'
);
}
@ -108,7 +113,7 @@ class DeepbworkController extends Controller
}
$serverService = new ServerService();
try {
$json = $serverService->getConfig($nodeId, $localPort);
$json = $serverService->getVmessConfig($nodeId, $localPort);
} catch (\Exception $e) {
abort(500, $e->getMessage());
}

View File

@ -4,6 +4,7 @@ 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;
@ -13,10 +14,12 @@ 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
{
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled": true,"destOverride": ["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
public $poseidonVersion;
public function __construct(Request $request)
@ -34,18 +37,18 @@ class PoseidonController extends Controller
if (!$server) {
return $this->error("server could not be found", 404);
}
Cache::put('server_last_check_at_' . $server->id, time());
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->v2ray_uuid,
"email" => sprintf("%s@v2board.user", $user->v2ray_uuid),
"uuid" => $user->uuid,
"email" => sprintf("%s@v2board.user", $user->uuid),
"alter_id" => $user->v2ray_alter_id,
"level" => $user->v2ray_level,
];
unset($user['v2ray_uuid']);
unset($user['uuid']);
unset($user['v2ray_alter_id']);
unset($user['v2ray_level']);
array_push($result, $user);
@ -58,14 +61,13 @@ class PoseidonController extends Controller
public function submit(Request $request)
{
if ($r = $this->verifyToken($request)) { return $r; }
// Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
$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);
$serverService = new ServerService();
$userService = new UserService();
foreach ($data as $item) {
@ -80,7 +82,8 @@ class PoseidonController extends Controller
$request->input('node_id'),
$item['u'],
$item['d'],
$server->rate
$server->rate,
'vmess'
);
}
@ -100,7 +103,7 @@ class PoseidonController extends Controller
$serverService = new ServerService();
try {
$json = $serverService->getConfig($nodeId, $localPort);
$json = $serverService->getVmessConfig($nodeId, $localPort);
$json->poseidon = [
'license_key' => (string)config('v2board.server_license'),
];

View File

@ -0,0 +1,120 @@
<?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\ServerTrojan;
use App\Models\ServerLog;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
/*
* Tidal Lab Trojan
* Github: https://github.com/tokumeikoi/tidalab-trojan
*/
class TrojanTidalabController extends Controller
{
public function __construct(Request $request)
{
$token = $request->input('token');
if (empty($token)) {
abort(500, 'token is null');
}
if ($token !== config('v2board.server_token')) {
abort(500, 'token is error');
}
}
// 后端获取用户
public function user(Request $request)
{
$nodeId = $request->input('node_id');
$server = ServerTrojan::find($nodeId);
if (!$server) {
abort(500, 'fail');
}
Cache::put(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $server->id), time(), 3600);
$serverService = new ServerService();
$users = $serverService->getAvailableUsers(json_decode($server->group_id));
$result = [];
foreach ($users as $user) {
$user->trojan_user = [
"password" => $user->uuid,
];
unset($user['uuid']);
unset($user['v2ray_alter_id']);
unset($user['v2ray_level']);
array_push($result, $user);
}
return response([
'msg' => 'ok',
'data' => $result,
]);
}
// 后端提交数据
public function submit(Request $request)
{
// Log::info('serverSubmitData:' . $request->input('node_id') . ':' . file_get_contents('php://input'));
$server = ServerTrojan::find($request->input('node_id'));
if (!$server) {
return response([
'ret' => 0,
'msg' => 'server is not found'
]);
}
$data = file_get_contents('php://input');
$data = json_decode($data, true);
Cache::put(CacheKey::get('SERVER_TROJAN_ONLINE_USER', $server->id), count($data), 3600);
$serverService = new ServerService();
$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'])) {
return response([
'ret' => 0,
'msg' => 'user fetch fail'
]);
}
$serverService->log(
$item['user_id'],
$request->input('node_id'),
$item['u'],
$item['d'],
$server->rate,
'trojan'
);
}
return response([
'ret' => 1,
'msg' => 'ok'
]);
}
// 后端获取配置
public function config(Request $request)
{
$nodeId = $request->input('node_id');
$localPort = $request->input('local_port');
if (empty($nodeId) || empty($localPort)) {
abort(500, '参数错误');
}
$serverService = new ServerService();
try {
$json = $serverService->getTrojanConfig($nodeId, $localPort);
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
die(json_encode($json, JSON_UNESCAPED_UNICODE));
}
}

View File

@ -11,7 +11,8 @@ class CommController extends Controller
{
return response([
'data' => [
'isTelegram' => (int)config('v2board.telegram_bot_enable', 0)
'isTelegram' => (int)config('v2board.telegram_bot_enable', 0),
'stripePk' => config('v2board.stripe_pk_live')
]
]);
}

View File

@ -26,6 +26,13 @@ class CouponController extends Controller
if (time() > $coupon->ended_at) {
abort(500, '优惠券已过期');
}
if ($coupon->limit_plan_ids) {
$limitPlanIds = json_decode($coupon->limit_plan_ids);
info($limitPlanIds);
if (!in_array($request->input('plan_id'), $limitPlanIds)) {
abort(500, '这个计划无法使用该优惠码');
}
}
return response([
'data' => $coupon
]);

View File

@ -9,12 +9,10 @@ use App\Services\OrderService;
use App\Services\UserService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use App\Models\Order;
use App\Models\Plan;
use App\Models\User;
use App\Models\Coupon;
use App\Utils\Helper;
use Omnipay\Omnipay;
use Stripe\Stripe;
@ -173,7 +171,7 @@ class OrderController extends Controller
]);
}
switch ($method) {
// return type => 0: QRCode / 1: URL
// return type => 0: QRCode / 1: URL / 2: No action
case 0:
// alipayF2F
if (!(int)config('v2board.alipay_enable')) {
@ -218,6 +216,14 @@ class OrderController extends Controller
'type' => 1,
'data' => $this->payTaro($order)
]);
case 6:
if (!(int)config('v2board.stripe_card_enable')) {
abort(500, '支付方式不可用');
}
return response([
'type' => 2,
'data' => $this->stripeCard($order, $request->input('token'))
]);
default:
abort(500, '支付方式不存在');
}
@ -266,7 +272,7 @@ class OrderController extends Controller
if ((int)config('v2board.bitpayx_enable')) {
$bitpayX = new \StdClass();
$bitpayX->name = config('v2board.bitpayx_name', '聚合支付');
$bitpayX->name = config('v2board.bitpayx_name', '在线支付');
$bitpayX->method = 4;
$bitpayX->icon = 'wallet';
array_push($data, $bitpayX);
@ -274,12 +280,20 @@ class OrderController extends Controller
if ((int)config('v2board.paytaro_enable')) {
$obj = new \StdClass();
$obj->name = config('v2board.paytaro_name', '聚合支付');
$obj->name = config('v2board.paytaro_name', '在线支付');
$obj->method = 5;
$obj->icon = 'wallet';
array_push($data, $obj);
}
if ((int)config('v2board.stripe_card_enable')) {
$obj = new \StdClass();
$obj->name = '信用卡';
$obj->method = 6;
$obj->icon = 'card';
array_push($data, $obj);
}
return response([
'data' => $data
]);
@ -347,7 +361,7 @@ class OrderController extends Controller
'statement_descriptor' => $order->trade_no,
'metadata' => [
'user_id' => $order->user_id,
'invoice_id' => $order->trade_no,
'out_trade_no' => $order->trade_no,
'identifier' => ''
],
'redirect' => [
@ -357,10 +371,6 @@ class OrderController extends Controller
if (!$source['redirect']['url']) {
abort(500, '支付网关请求失败');
}
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
abort(500, '订单创建失败');
}
return $source['redirect']['url'];
}
@ -378,7 +388,7 @@ class OrderController extends Controller
'type' => 'wechat',
'metadata' => [
'user_id' => $order->user_id,
'invoice_id' => $order->trade_no,
'out_trade_no' => $order->trade_no,
'identifier' => ''
],
'redirect' => [
@ -388,12 +398,38 @@ class OrderController extends Controller
if (!$source['wechat']['qr_code_url']) {
abort(500, '支付网关请求失败');
}
if (!Cache::put($source['id'], $order->trade_no, 3600)) {
abort(500, '订单创建失败');
}
return $source['wechat']['qr_code_url'];
}
private function stripeCard($order, string $token)
{
$currency = config('v2board.stripe_currency', 'hkd');
$exchange = Helper::exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, '货币转换超时,请稍后再试');
}
Stripe::setApiKey(config('v2board.stripe_sk_live'));
try {
$charge = \Stripe\Charge::create([
'amount' => floor($order->total_amount * $exchange),
'currency' => $currency,
'source' => $token,
'metadata' => [
'user_id' => $order->user_id,
'out_trade_no' => $order->trade_no,
'identifier' => ''
]
]);
} catch (\Exception $e) {
abort(500, '遇到了点问题,请刷新页面稍后再试');
}
info($charge);
if (!$charge->paid) {
abort(500, '扣款失败,请检查信用卡信息');
}
return $charge->paid;
}
private function bitpayX($order)
{
$bitpayX = new BitpayX(config('v2board.bitpayx_appsecret'));

View File

@ -3,7 +3,9 @@
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Services\ServerService;
use App\Services\UserService;
use App\Utils\CacheKey;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use App\Models\Server;
@ -17,29 +19,15 @@ class ServerController extends Controller
public function fetch(Request $request)
{
$user = User::find($request->session()->get('id'));
$server = [];
$servers = [];
$userService = new UserService();
if ($userService->isAvailable($user)) {
$servers = Server::where('show', 1)
->orderBy('sort', 'ASC')
->get();
foreach ($servers as $item) {
$groupId = json_decode($item['group_id']);
if (in_array($user->group_id, $groupId)) {
array_push($server, $item);
}
}
}
for ($i = 0; $i < count($server); $i++) {
$server[$i]['link'] = Helper::buildVmessLink($server[$i], $user);
if ($server[$i]['parent_id']) {
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['parent_id']);
} else {
$server[$i]['last_check_at'] = Cache::get('server_last_check_at_' . $server[$i]['id']);
}
$serverService = new ServerService();
$servers = $serverService->getAllServers($user);
$servers = array_merge($servers['vmess'], $servers['trojan']);
}
return response([
'data' => $server
'data' => $servers
]);
}

View File

@ -107,7 +107,7 @@ class UserController extends Controller
public function resetSecurity(Request $request)
{
$user = User::find($request->session()->get('id'));
$user->v2ray_uuid = Helper::guid(true);
$user->uuid = Helper::guid(true);
$user->token = Helper::guid();
if (!$user->save()) {
abort(500, '重置失败');

View File

@ -44,6 +44,7 @@ class ConfigSave extends FormRequest
// stripe
'stripe_alipay_enable' => 'in:0,1',
'stripe_wepay_enable' => 'in:0,1',
'stripe_card_enable' => 'in:0,1',
'stripe_sk_live' => '',
'stripe_pk_live' => '',
'stripe_webhook_key' => '',

View File

@ -6,6 +6,16 @@ use Illuminate\Foundation\Http\FormRequest;
class CouponSave extends FormRequest
{
const RULES = [
'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' => ''
];
/**
* Get the validation rules that apply to the request.
*
@ -13,14 +23,7 @@ class CouponSave extends FormRequest
*/
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'
];
return self::RULES;
}
public function messages()
@ -35,7 +38,8 @@ class CouponSave extends FormRequest
'started_at.integer' => '开始时间格式有误',
'ended_at.required' => '结束时间不能为空',
'ended_at.integer' => '结束时间格式有误',
'limit_use.integer' => '使用次数格式有误'
'limit_use.integer' => '使用次数格式有误',
'limit_plan_ids.array' => '指定订阅格式有误'
];
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerTrojanSave extends FormRequest
{
CONST RULES = [
'show' => '',
'name' => 'required',
'group_id' => 'required|array',
'parent_id' => 'nullable|integer',
'host' => 'required',
'port' => 'required',
'server_port' => 'required',
'allow_insecure' => 'nullable|in:0,1',
'server_name' => 'nullable',
'tags' => 'nullable|array',
'rate' => 'required|numeric'
];
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return self::RULES;
}
public function messages()
{
return [
'name.required' => '节点名称不能为空',
'group_id.required' => '权限组不能为空',
'group_id.array' => '权限组格式不正确',
'parent_id.integer' => '父节点格式不正确',
'host.required' => '节点地址不能为空',
'port.required' => '连接端口不能为空',
'server_port.required' => '后端服务端口不能为空',
'allow_insecure.in' => '允许不安全格式不正确',
'tags.array' => '标签格式不正确',
'rate.required' => '倍率不能为空',
'rate.numeric' => '倍率格式不正确'
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerTrojanSort extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'server_ids' => 'required|array'
];
}
public function messages()
{
return [
'server_ids.required' => '服务器ID不能为空',
'server_ids.array' => '服务器ID格式有误'
];
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerTrojanUpdate extends FormRequest
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'show' => 'in:0,1'
];
}
public function messages()
{
return [
'show.in' => '显示状态格式不正确'
];
}
}

View File

@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerSave extends FormRequest
class ServerV2raySave extends FormRequest
{
CONST RULES = [
'show' => '',

View File

@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerSort extends FormRequest
class ServerV2raySort extends FormRequest
{
/**
* Get the validation rules that apply to the request.

View File

@ -4,7 +4,7 @@ namespace App\Http\Requests\Admin;
use Illuminate\Foundation\Http\FormRequest;
class ServerUpdate extends FormRequest
class ServerV2rayUpdate extends FormRequest
{
/**
* Get the validation rules that apply to the request.

View File

@ -23,16 +23,31 @@ class AdminRoute
$router->post('/plan/update', 'Admin\\PlanController@update');
$router->post('/plan/sort', 'Admin\\PlanController@sort');
// Server
$router->get ('/server/fetch', 'Admin\\ServerController@fetch');
$router->post('/server/save', 'Admin\\ServerController@save');
$router->get ('/server/group/fetch', 'Admin\\ServerController@groupFetch');
$router->post('/server/group/save', 'Admin\\ServerController@groupSave');
$router->post('/server/group/drop', 'Admin\\ServerController@groupDrop');
$router->post('/server/drop', 'Admin\\ServerController@drop');
$router->post('/server/update', 'Admin\\ServerController@update');
$router->post('/server/copy', 'Admin\\ServerController@copy');
$router->post('/server/viewConfig', 'Admin\\ServerController@viewConfig');
$router->post('/server/sort', 'Admin\\ServerController@sort');
$router->get ('/server/group/fetch', 'Admin\\Server\\GroupController@fetch');
$router->post('/server/group/save', 'Admin\\Server\\GroupController@save');
$router->post('/server/group/drop', 'Admin\\Server\\GroupController@drop');
$router->group([
'prefix' => 'server/trojan'
], function ($router) {
$router->get ('fetch', 'Admin\\Server\\TrojanController@fetch');
$router->post('save', 'Admin\\Server\\TrojanController@save');
$router->post('drop', 'Admin\\Server\\TrojanController@drop');
$router->post('update', 'Admin\\Server\\TrojanController@update');
$router->post('copy', 'Admin\\Server\\TrojanController@copy');
$router->post('sort', 'Admin\\Server\\TrojanController@sort');
$router->post('viewConfig', 'Admin\\Server\\TrojanController@viewConfig');
});
$router->group([
'prefix' => 'server/v2ray'
], function ($router) {
$router->get ('fetch', 'Admin\\Server\\V2rayController@fetch');
$router->post('save', 'Admin\\Server\\V2rayController@save');
$router->post('drop', 'Admin\\Server\\V2rayController@drop');
$router->post('update', 'Admin\\Server\\V2rayController@update');
$router->post('copy', 'Admin\\Server\\V2rayController@copy');
$router->post('sort', 'Admin\\Server\\V2rayController@sort');
$router->post('viewConfig', 'Admin\\Server\\V2rayController@viewConfig');
});
// Order
$router->get ('/order/fetch', 'Admin\\OrderController@fetch');
$router->post('/order/repair', 'Admin\\OrderController@repair');

View File

@ -14,8 +14,9 @@ class ClientRoute
// Client
$router->get('/subscribe', 'Client\\ClientController@subscribe');
// App
$router->get('/app/data', 'Client\\AppController@data');
$router->get('/app/config', 'Client\\AppController@config');
$router->get('/app/getConfig', 'Client\\AppController@getConfig');
$router->get('/app/getVersion', 'Client\\AppController@getVersion');
});
}
}

View File

@ -9,6 +9,4 @@ class ServerLog extends Model
{
protected $table = 'v2_server_log';
protected $dateFormat = 'U';
protected $dispatchesEvents = [
];
}

12
app/Models/ServerStat.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ServerStat extends Model
{
protected $table = 'v2_server_stat';
protected $dateFormat = 'U';
protected $guarded = ['id'];
}

View File

@ -0,0 +1,12 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ServerTrojan extends Model
{
protected $table = 'v2_server_trojan';
protected $dateFormat = 'U';
protected $guarded = ['id'];
}

View File

@ -43,6 +43,12 @@ 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;
}
}

View File

@ -119,25 +119,43 @@ class OrderService
'month_price' => 1,
'quarter_price' => 3,
'half_year_price' => 6,
'year_price' => 12,
'onetime_price' => 0
'year_price' => 12
];
$orderModel = Order::where('user_id', $user->id)
->where('cycle', '!=', 'reset_price')
->where('status', 3);
$surplusAmount = 0;
$orderSurplusMonth = 0;
$orderSurplusAmount = 0;
$userSurplusMonth = ($user->expired_at - time()) / 2678400;
foreach ($orderModel->get() as $item) {
$surplusMonth = strtotime("+ {$strToMonth[$item->cycle]}month", $item->created_at->format('U'));
if (!$surplusMonth) continue;
$surplusMonth = ($surplusMonth - time()) / 2678400 / $strToMonth[$item->cycle];
if ($surplusMonth > 0) {
$surplusAmount = $surplusAmount + ($item['total_amount'] + $item['balance_amount']) * $surplusMonth;
}
// 兼容历史余留问题
if ($item->cycle === 'onetime_price') continue;
$orderSurplusMonth = $orderSurplusMonth + $strToMonth[$item->cycle];
$orderSurplusAmount = $orderSurplusAmount + ($item['total_amount'] + $item['balance_amount']);
}
if (!$surplusAmount) {
if (!$orderSurplusMonth || !$orderSurplusAmount) return;
$monthUnitPrice = $orderSurplusAmount / $orderSurplusMonth;
// 如果用户过期月大于订单过期月
if ($userSurplusMonth > $orderSurplusMonth) {
$orderSurplusAmount = $orderSurplusMonth * $monthUnitPrice;
} else {
$orderSurplusAmount = $userSurplusMonth * $monthUnitPrice;
}
if (!$orderSurplusAmount) {
return;
}
$order->surplus_amount = $surplusAmount > 0 ? $surplusAmount : 0;
$order->surplus_amount = $orderSurplusAmount > 0 ? $orderSurplusAmount : 0;
$order->surplus_order_ids = json_encode(array_map(function ($v) { return $v['id'];}, $orderModel->get()->toArray()));
}
public function success(string $callbackNo)
{
$order = $this->order;
if ($order->status !== 0) {
return true;
}
$order->status = 1;
$order->callback_no = $callbackNo;
return $order->save();
}
}

View File

@ -5,11 +5,72 @@ namespace App\Services;
use App\Models\ServerLog;
use App\Models\User;
use App\Models\Server;
use App\Models\ServerTrojan;
use App\Utils\CacheKey;
use App\Utils\Helper;
use Illuminate\Support\Facades\Cache;
class ServerService
{
CONST SERVER_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"dns":{},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled":true,"destOverride":["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
CONST V2RAY_CONFIG = '{"api":{"services":["HandlerService","StatsService"],"tag":"api"},"dns":{},"stats":{},"inbound":{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled":true,"destOverride":["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},"inboundDetour":[{"listen":"0.0.0.0","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"outbound":{"protocol":"freedom","settings":{}},"outboundDetour":[{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"inboundTag":"api","outboundTag":"api","type":"field"}]},"policy":{"levels":{"0":{"handshake":4,"connIdle":300,"uplinkOnly":5,"downlinkOnly":30,"statsUserUplink":true,"statsUserDownlink":true}}}}';
CONST TROJAN_CONFIG = '{"run_type":"server","local_addr":"0.0.0.0","local_port":443,"remote_addr":"www.taobao.com","remote_port":80,"password":[],"ssl":{"cert":"server.crt","key":"server.key","sni":"domain.com"},"api":{"enabled":true,"api_addr":"127.0.0.1","api_port":10000}}';
public function getVmess(User $user, $all = false):array
{
$vmess = [];
$model = Server::orderBy('sort', 'ASC');
if (!$all) {
$model->where('show', 1);
}
$vmesss = $model->get();
foreach ($vmesss as $k => $v) {
$groupId = json_decode($vmesss[$k]['group_id']);
if (in_array($user->group_id, $groupId)) {
$vmesss[$k]['link'] = Helper::buildVmessLink($vmesss[$k], $user);
if ($vmesss[$k]['parent_id']) {
$vmesss[$k]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $vmesss[$k]['parent_id']));
} else {
$vmesss[$k]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $vmesss[$k]['id']));
}
array_push($vmess, $vmesss[$k]);
}
}
return $vmess;
}
public function getTrojan(User $user, $all = false)
{
$trojan = [];
$model = ServerTrojan::orderBy('sort', 'ASC');
if (!$all) {
$model->where('show', 1);
}
$trojans = $model->get();
foreach ($trojans as $k => $v) {
$groupId = json_decode($trojans[$k]['group_id']);
if (in_array($user->group_id, $groupId)) {
if ($trojans[$k]['parent_id']) {
$trojans[$k]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojans[$k]['parent_id']));
} else {
$trojans[$k]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojans[$k]['id']));
}
array_push($trojan, $trojans[$k]);
}
}
return $trojan;
}
public function getAllServers(User $user, $all = false)
{
return [
'vmess' => $this->getVmess($user, $all),
'trojan' => $this->getTrojan($user, $all)
];
}
public function getAvailableUsers($groupId)
{
@ -27,20 +88,20 @@ class ServerService
'u',
'd',
'transfer_enable',
'v2ray_uuid',
'uuid',
'v2ray_alter_id',
'v2ray_level'
])
->get();
}
public function getConfig(int $nodeId, int $localPort)
public function getVmessConfig(int $nodeId, int $localPort)
{
$server = Server::find($nodeId);
if (!$server) {
abort(500, '节点不存在');
}
$json = json_decode(self::SERVER_CONFIG);
$json = json_decode(self::V2RAY_CONFIG);
$json->log->loglevel = config('v2board.server_log_level', 'none');
$json->inboundDetour[0]->port = (int)$localPort;
$json->inbound->port = (int)$server->server_port;
@ -53,6 +114,22 @@ class ServerService
return $json;
}
public function getTrojanConfig(int $nodeId, int $localPort)
{
$server = ServerTrojan::find($nodeId);
if (!$server) {
abort(500, '节点不存在');
}
$json = json_decode(self::TROJAN_CONFIG);
$json->local_port = $server->server_port;
$json->ssl->sni = $server->server_name ? $server->server_name : $server->host;
$json->ssl->cert = "/root/.cert/server.crt";
$json->ssl->key = "/root/.cert/server.key";
$json->api->api_port = $localPort;
return $json;
}
private function setDns(Server $server, object $json)
{
if ($server->dnsSettings) {
@ -123,8 +200,8 @@ class ServerService
$tlsSettings = json_decode($server->tlsSettings);
$json->inbound->streamSettings->security = 'tls';
$tls = (object)[
'certificateFile' => '/home/v2ray.crt',
'keyFile' => '/home/v2ray.key'
'certificateFile' => '/root/.cert/server.crt',
'keyFile' => '/root/.cert/server.key'
];
$json->inbound->streamSettings->tlsSettings = new \StdClass();
if (isset($tlsSettings->serverName)) {
@ -137,7 +214,7 @@ class ServerService
}
}
public function log(int $userId, int $serverId, int $u, int $d, float $rate)
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'));
@ -146,6 +223,7 @@ class ServerService
->where('server_id', $serverId)
->where('user_id', $userId)
->where('rate', $rate)
->where('method', $method)
->first();
if ($serverLog) {
$serverLog->u = $serverLog->u + $u;
@ -159,6 +237,7 @@ class ServerService
$serverLog->d = $d;
$serverLog->rate = $rate;
$serverLog->log_at = $timestamp;
$serverLog->method = $method;
$serverLog->save();
}
}

View File

@ -6,7 +6,11 @@ class CacheKey
{
CONST KEYS = [
'EMAIL_VERIFY_CODE' => '邮箱验证吗',
'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间'
'LAST_SEND_EMAIL_VERIFY_TIMESTAMP' => '最后一次发送邮箱验证码时间',
'SERVER_V2RAY_ONLINE_USER' => '节点在线用户',
'SERVER_V2RAY_LAST_CHECK_AT' => '节点最后检查时间',
'SERVER_TROJAN_ONLINE_USER' => 'trojan节点在线用户',
'SERVER_TROJAN_LAST_CHECK_AT' => 'trojan节点最后检查时间'
];
public static function get(string $key, $uniqueValue)

52
app/Utils/Clash.php Normal file
View File

@ -0,0 +1,52 @@
<?php
namespace App\Utils;
class Clash
{
public static function buildVmess($uuid, $server)
{
$array = [];
$array['name'] = $server->name;
$array['type'] = 'vmess';
$array['server'] = $server->host;
$array['port'] = $server->port;
$array['uuid'] = $uuid;
$array['alterId'] = 2;
$array['cipher'] = 'auto';
if ($server->tls) {
$tlsSettings = json_decode($server->tlsSettings);
$array['tls'] = true;
if (isset($tlsSettings->allowInsecure)) $array['skip-cert-verify'] = ($tlsSettings->allowInsecure ? true : false );
}
if ($server->network == 'ws') {
$array['network'] = $server->network;
if ($server->networkSettings) {
$wsSettings = json_decode($server->networkSettings);
if (isset($wsSettings->path)) $array['ws-path'] = $wsSettings->path;
if (isset($wsSettings->headers->Host)) $array['ws-headers'] = [
'Host' => $wsSettings->headers->Host
];
}
}
return $array;
}
public static function buildTrojan($password, $server)
{
$array = [];
$array['name'] = $server->name;
$array['type'] = 'trojan';
$array['server'] = $server->host;
$array['port'] = $server->port;
$array['password'] = $password;
$array['sni'] = $server->server_name;
if ($server->allow_insecure) {
$array['skip-cert-verify'] = true;
} else {
$array['skip-cert-verify'] = false;
}
return $array;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Utils;
use App\Models\Server;
use App\Models\ServerTrojan;
use App\Models\User;
class Helper
@ -56,6 +57,18 @@ class Helper
return $str;
}
public static function buildTrojanLink(ServerTrojan $server, User $user)
{
$server->name = rawurlencode($server->name);
$query = http_build_query([
'allowInsecure' => $server->allow_insecure,
'peer' => $server->server_name
]);
$uri = "trojan://{$user->uuid}@{$server->host}:{$server->port}?{$query}#{$server->name}";
$uri .= "\r\n";
return $uri;
}
public static function buildVmessLink(Server $server, User $user)
{
$config = [
@ -63,7 +76,7 @@ class Helper
"ps" => $server->name,
"add" => $server->host,
"port" => $server->port,
"id" => $user->v2ray_uuid,
"id" => $user->uuid,
"aid" => "2",
"net" => $server->network,
"type" => "none",

51
app/Utils/QuantumultX.php Normal file
View File

@ -0,0 +1,51 @@
<?php
namespace App\Utils;
class QuantumultX
{
public static function buildVmess($uuid, $server)
{
$uri = "vmess=" . $server->host . ":" . $server->port . ", method=none, password=" . $uuid . ", fast-open=false, udp-relay=false, tag=" . $server->name;
if ($server->tls) {
$tlsSettings = json_decode($server->tlsSettings);
if ($server->network === 'tcp') $uri .= ', obfs=over-tls';
if (isset($tlsSettings->allowInsecure)) {
// Default: tls-verification=true
$uri .= ', tls-verification=' . ($tlsSettings->allowInsecure ? "false" : "true");
}
if (isset($tlsSettings->serverName)) {
$uri .= ', obfs-host=' . $tlsSettings->serverName;
}
}
if ($server->network === 'ws') {
$uri .= ', obfs=' . ($server->tls ? 'wss' : 'ws');
if ($server->networkSettings) {
$wsSettings = json_decode($server->networkSettings);
if (isset($wsSettings->path)) $uri .= ', obfs-uri=' . $wsSettings->path;
if (isset($wsSettings->headers->Host)) $uri .= ', obfs-host=' . $wsSettings->headers->Host;
}
}
$uri .= "\r\n";
return $uri;
}
public static function buildTrojan($password, $server)
{
$config = [
"trojan={$server->host}:{$server->port}",
"password={$password}",
"over-tls=true",
$server->server_name ? "tls-host={$server->server_name}" : "",
$server->allow_insecure ? 'tls-verification=true' : 'tls-verification=false',
"fast-open=false",
"udp-relay=false",
"tag={$server->name}"
];
$config = array_filter($config);
$uri = implode($config, ',');
$uri .= "\r\n";
return $uri;
}
}

46
app/Utils/Surge.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Utils;
class Surge
{
public static function buildVmess($uuid, $server)
{
$proxies = $server->name . ' = vmess, ' . $server->host . ', ' . $server->port . ', username=' . $uuid . ', tfo=true';
if ($server->tls) {
$tlsSettings = json_decode($server->tlsSettings);
$proxies .= ', tls=' . ($server->tls ? "true" : "false");
if (isset($tlsSettings->allowInsecure)) {
$proxies .= ', skip-cert-verify=' . ($tlsSettings->allowInsecure ? "true" : "false");
}
}
if ($server->network == 'ws') {
$proxies .= ', ws=true';
if ($server->networkSettings) {
$wsSettings = json_decode($server->networkSettings);
if (isset($wsSettings->path)) $proxies .= ', ws-path=' . $wsSettings->path;
if (isset($wsSettings->headers->Host)) $proxies .= ', ws-headers=host:' . $wsSettings->headers->Host;
}
}
$proxies .= "\r\n";
return $proxies;
}
public static function buildTrojan($password, $server)
{
$config = [
"{$server->name}=trojan",
"{$server->host}",
"{$server->port}",
"password={$password}",
$server->allow_insecure ? 'skip-cert-verify=true' : 'skip-cert-verify=false',
$server->server_name ? "sni={$server->server_name}" : "",
"tfo=true"
];
$config = array_filter($config);
$uri = implode($config, ',');
$uri .= "\r\n";
return $uri;
}
}

View File

@ -15,7 +15,7 @@
"laravel/tinker": "^1.0",
"lokielse/omnipay-alipay": "3.0.6",
"php-curl-class/php-curl-class": "^8.6",
"stripe/stripe-php": "^7.5",
"stripe/stripe-php": "^7.36.1",
"symfony/yaml": "^4.3"
},
"require-dev": {

View File

@ -236,5 +236,5 @@ return [
| The only modification by laravel config
|
*/
'version' => '1.3'
'version' => '1.3.1-r.1'
];

View File

@ -27,6 +27,7 @@ CREATE TABLE `v2_coupon` (
`type` tinyint(1) NOT NULL,
`value` int(11) NOT NULL,
`limit_use` int(11) DEFAULT NULL,
`limit_plan_ids` varchar(255) DEFAULT NULL,
`started_at` int(11) NOT NULL,
`ended_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
@ -165,6 +166,7 @@ CREATE TABLE `v2_server_log` (
`u` varchar(255) NOT NULL,
`d` varchar(255) NOT NULL,
`rate` decimal(10,2) NOT NULL,
`method` varchar(255) NOT NULL,
`log_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
@ -178,11 +180,33 @@ CREATE TABLE `v2_server_stat` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`server_id` int(11) NOT NULL,
`u` varchar(255) NOT NULL,
`d` varchar(25) NOT NULL,
`d` varchar(255) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_trojan`;
CREATE TABLE `v2_server_trojan` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '节点ID',
`group_id` varchar(255) NOT NULL COMMENT '节点组',
`parent_id` int(11) DEFAULT NULL COMMENT '父节点',
`tags` varchar(255) DEFAULT NULL COMMENT '节点标签',
`name` varchar(255) NOT NULL COMMENT '节点名称',
`rate` varchar(11) NOT NULL COMMENT '倍率',
`host` varchar(255) NOT NULL COMMENT '主机名',
`port` int(11) NOT NULL COMMENT '连接端口',
`server_port` int(11) NOT NULL COMMENT '服务端口',
`allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全',
`server_name` varchar(255) DEFAULT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='trojan伺服器表';
DROP TABLE IF EXISTS `v2_ticket`;
@ -241,11 +265,12 @@ CREATE TABLE `v2_user` (
`u` bigint(20) NOT NULL DEFAULT '0',
`d` bigint(20) NOT NULL DEFAULT '0',
`transfer_enable` bigint(20) NOT NULL DEFAULT '0',
`enable` tinyint(1) NOT NULL DEFAULT '1',
`banned` tinyint(1) NOT NULL DEFAULT '0',
`is_admin` tinyint(1) NOT NULL DEFAULT '0',
`last_login_at` int(11) DEFAULT NULL,
`last_login_ip` int(11) DEFAULT NULL,
`v2ray_uuid` varchar(36) NOT NULL,
`uuid` varchar(36) NOT NULL,
`v2ray_alter_id` tinyint(4) NOT NULL DEFAULT '2',
`v2ray_level` tinyint(4) NOT NULL DEFAULT '0',
`group_id` int(11) DEFAULT NULL,
@ -261,4 +286,4 @@ CREATE TABLE `v2_user` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 2020-05-12 12:31:04
-- 2020-07-01 07:01:59

View File

@ -1,36 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUsersTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
}

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
}

View File

@ -118,14 +118,6 @@ CREATE TABLE `v2_tutorial` (
`updated_at` int(11) NOT NULL
);
SET NAMES utf8mb4;
INSERT INTO `v2_tutorial` (`id`, `title`, `description`, `icon`, `steps`, `show`, `created_at`, `updated_at`) VALUES
(1, 'Windows', '兼容 Windows 7 以上的版本', 'fab fa-2x fa-windows', '[{\"default_area\":\"<div><div>下载 V2rayN 客户端。</div><div>下载完成后解压解压完成后运行V2rayN</div><div>运行时请右键,以管理员身份运行</div></div>\",\"download_url\":\"/downloads/V2rayN.zip\"},{\"default_area\":\"<div>点击订阅按钮,选择订阅设置点击添加,输入如下内容后点击确定保存</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url)<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UkcHNtERTnjLVS8.jpg\"},{\"default_area\":\"<div>点击订阅后,从服务器列表选择服务器</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/BgPGFQ3kCSuIRjJ.jpg\"},{\"default_area\":\"<div>点击参数设置找到Http代理选择PAC模式后按确定保存即启动代理。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/vnVykKEFT8Lzo3f.jpg\"}]', 1, 1577972408, 1577980882),
(2, 'Android', '兼容 Android 6 以上的版本', 'fab fa-2x fa-android', '[{\"default_area\":\"<div>下载 V2rayNG 客户端。</div>\",\"safe_area\":\"\",\"download_url\":\"/downloads/V2rayNG.apk\"},{\"default_area\":\"<div>打开 V2rayNG 点击左上角的菜单图标打开侧边栏,随后点击 订阅设置,点击右上角的➕按钮新增订阅。</div><div>按照下方内容进行填写,填写完毕后点击右上角的☑️按钮。</div>\",\"safe_area\":\"<div>备注:<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\\n<div>地址(url)<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\",\"download_url\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ghuVkTe6LBqRxSO.jpg\"},{\"default_area\":\"<div>再次从侧边栏进入 设置 页面,点击 路由模式 将其更改为 \\b绕过局域网及大陆地址。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/Tf1AGoXZuhJrwOq.jpg\"},{\"default_area\":\"<div>随后从侧边栏回到 配置文件 页面,点击右上角的省略号图标选择更新订阅。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/UtfPShQXupRmB4L.jpg\"},{\"img_url\":\"https://i.loli.net/2019/11/21/ZkbNsSrAg3m5Dny.jpg\",\"default_area\":\"<div>点击选择您需要的节点点击右下角的V字按钮即可连接。</div>\"}]', 1, 1577972534, 1577981610),
(3, 'macOS', '兼容 Yosemite 以上的版本', 'fab fa-2x fa-apple', '[{\"default_area\":\"<div>下载 ClashX 客户端,安装后运行。</div>\",\"download_url\":\"/downloads/ClashX.dmg\",\"img_url\":\"https://i.loli.net/2019/11/20/uNGrjl2noCL1f5B.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+M(订阅快捷键),在弹出的窗口点击添加输入下方信息</div>\",\"safe_area\":\"<div>Url<code onclick=\\\"safeAreaCopy(\'{{$subscribe_url}}\')\\\">{{$subscribe_url}}</code></div>\\n<div>Config Name<code onclick=\\\"safeAreaCopy(\'{{$app_name}}\')\\\">{{$app_name}}</code></div>\",\"img_url\":\"https://i.loli.net/2019/11/20/8eB13mRbFuszwxg.jpg\"},{\"default_area\":\"<div>点击通知栏 ClashX 图标保持选中状态,按快捷键 ⌘+S(设置为系统代理快捷键),即连接完成</div>\"}]', 1, 1577979855, 1577981646),
(4, 'iOS', '兼容 iOS 9 以上的版本', 'fab fa-2x fa-apple', '[{\"default_area\":\"<div>iOS上使用请在iOS浏览器中打开本页</div>\"},{\"default_area\":\"<div>在 App Store 登录本站提供的美区 Apple ID 下载客户端。</div><div>为了保护您的隐私,请勿在手机设置里直接登录,仅在 App Store 登录即可。</div><div>登陆完成后点击下方下载会自动唤起下载。</div>\",\"safe_area\":\"<div>Apple ID<code onclick=\\\"safeAreaCopy(\'{{$apple_id}}\')\\\">{{$apple_id}}</code></div><div>密码:<code onclick=\\\"safeAreaCopy(\'{{$apple_id_password}}\')\\\">点击复制密码</code></div>\",\"download_url\":\"https://apps.apple.com/us/app/shadowrocket/id932747118\",\"img_url\":\"https://i.loli.net/2019/11/21/5idkjJ61stWgREV.jpg\"},{\"default_area\":\"<div>待客户端安装完成后,点击下方一键订阅按钮会自动唤起并进行订阅</div>\",\"safe_area\":\"\",\"img_url\":\"https://i.loli.net/2019/11/21/ZcqlNMb3eg5Uhxd.jpg\",\"download_url\":\"shadowrocket://add/sub://{{$b64_subscribe_url}}?remark={{$app_name}}\"},{\"default_area\":\"<div>选择节点进行链接,首次链接过程授权窗口请一路允许。</div>\",\"img_url\":\"https://i.loli.net/2019/11/21/9Zdxksr7Ey6hjlm.jpg\"}]', 1, 1577982016, 1577983283);
ALTER TABLE `v2_server_log`
CHANGE `rate` `rate` decimal(10,2) NOT NULL AFTER `d`;
@ -256,3 +248,51 @@ ADD INDEX log_at (`log_at`);
ALTER TABLE `v2_user`
ADD `telegram_id` bigint NULL AFTER `invite_user_id`;
ALTER TABLE `v2_server_stat`
ADD `online` int(11) NOT NULL AFTER `d`;
ALTER TABLE `v2_server_stat`
ADD INDEX `created_at` (`created_at`);
CREATE TABLE `v2_server_trojan` (
`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
`group_id` varchar(255) NOT NULL,
`tags` varchar(255) NULL,
`name` varchar(255) NOT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0',
`sort` int(11) NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL
) COMMENT='trojan伺服器表' COLLATE 'utf8mb4_general_ci';
ALTER TABLE `v2_server_stat`
CHANGE `d` `d` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `u`,
DROP `online`;
ALTER TABLE `v2_user`
CHANGE `v2ray_uuid` `uuid` varchar(36) COLLATE 'utf8_general_ci' NOT NULL AFTER `last_login_ip`;
ALTER TABLE `v2_server_trojan`
ADD `rate` varchar(11) COLLATE 'utf8mb4_general_ci' NOT NULL AFTER `name`;
ALTER TABLE `v2_server_log`
ADD `method` varchar(255) NOT NULL AFTER `rate`;
ALTER TABLE `v2_coupon`
ADD `limit_plan_ids` varchar(255) NULL AFTER `limit_use`;
ALTER TABLE `v2_server_trojan`
ADD `server_port` int(11) NOT NULL AFTER `port`;
ALTER TABLE `v2_server_trojan`
ADD `parent_id` int(11) NULL AFTER `group_id`;
ALTER TABLE `v2_server_trojan`
ADD `allow_insecure` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否允许不安全' AFTER `server_port`,
CHANGE `show` `show` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否显示' AFTER `allow_insecure`;
ALTER TABLE `v2_server_trojan`
ADD `server_name` varchar(255) NULL AFTER `allow_insecure`;

View File

@ -23,6 +23,9 @@ class PayTaro
$curl = new Curl();
$curl->post('https://api.paytaro.com/v1/gateway/fetch', http_build_query($params));
$result = $curl->response;
if (!$result) {
abort(500, '网络异常');
}
if ($curl->error) {
$errors = (array)$result->errors;
abort(500, $errors[array_keys($errors)[0]][0]);

View File

@ -1,63 +0,0 @@
<?php
namespace Library;
class TomatoPay
{
private $mchid;
private $account;
private $key;
public function __construct($mchid, $account, $key)
{
$this->mchid = $mchid;
$this->account = $account;
$this->key = $key;
}
public function alipay($cny, $trade)
{
$params = [
'mchid' => $this->mchid,
'account' => $this->account,
'cny' => $cny,
'type' => '1',
'trade' => $trade
];
$params['signs'] = $this->sign($params);
return $this->buildHtml('https://b.fanqieui.com/gateways/alipay.php', $params);
}
public function wxpay($cny, $trade)
{
$params = [
'mchid' => $this->mchid,
'account' => $this->account,
'cny' => $cny,
'type' => '1',
'trade' => $trade
];
$params['signs'] = $this->sign($params);
return $this->buildHtml('https://b.fanqieui.com/gateways/wxpay.php', $params);
}
public function sign($params)
{
$o = '';
foreach ($params as $k => $v) {
$o .= "$k=" . ($v) . "&";
}
return md5(substr($o, 0, -1) . $this->key);
}
public function buildHtml($url, $params, $method = 'post', $target = '_self')
{
// return var_dump($params);
$html = "<form id='submit' name='submit' action='" . $url . "' method='$method' target='$target'>";
foreach ($params as $key => $value) {
$html .= "<input type='hidden' name='$key' value='$value'/>";
}
$html .= "</form><script>document.forms['submit'].submit();</script>";
return $html;
}
}

14
library/V2ray.php Normal file
View File

@ -0,0 +1,14 @@
<?php
namespace Library;
class V2ray
{
protected $config;
public function __construct()
{
$this->config = new \StdClass();
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,524 @@
port: 8890
socks-port: 8891
allow-lan: false
mode: rule
log-level: info
external-controller: 127.0.0.1:9091
experimental:
ignore-resolve-fail: true
dns:
enable: true
ipv6: false
enhanced-mode: redir-host
nameserver:
- 1.2.4.8
- 223.5.5.5
fallback:
- tls://1.0.0.1:853
- tls://dns.google:853
proxies:
proxy-groups:
- { name: "SELECT", type: select, proxies: ["自动选择", "故障转移"] }
- { name: "自动选择", type: url-test, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 86400 }
- { name: "故障转移", type: fallback, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 7200 }
rules:
# Apple
- DOMAIN,safebrowsing.urlsec.qq.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。
- DOMAIN,safebrowsing.googleapis.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。
- DOMAIN,ocsp.apple.com,SELECT
- DOMAIN-SUFFIX,digicert.com,SELECT
- DOMAIN-SUFFIX,entrust.net,SELECT
- DOMAIN,ocsp.verisign.net,SELECT
- DOMAIN-SUFFIX,apps.apple.com,SELECT
- DOMAIN,itunes.apple.com,SELECT
- DOMAIN-SUFFIX,blobstore.apple.com,SELECT
- DOMAIN-SUFFIX,music.apple.com,DIRECT
- DOMAIN-SUFFIX,mzstatic.com,DIRECT
- DOMAIN-SUFFIX,itunes.apple.com,DIRECT
- DOMAIN-SUFFIX,icloud.com,DIRECT
- DOMAIN-SUFFIX,icloud-content.com,DIRECT
- DOMAIN-SUFFIX,me.com,DIRECT
- DOMAIN-SUFFIX,mzstatic.com,DIRECT
- DOMAIN-SUFFIX,akadns.net,DIRECT
- DOMAIN-SUFFIX,aaplimg.com,DIRECT
- DOMAIN-SUFFIX,cdn-apple.com,DIRECT
- DOMAIN-SUFFIX,apple.com,DIRECT
- DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT
# - DOMAIN,e.crashlytics.com,REJECT //注释此选项有助于大多数App开发者分析崩溃信息如果您拒绝一切崩溃数据统计、搜集请取消 # 注释。
# 自定义规则
## 您可以在此处插入您补充的自定义规则(请注意保持缩进)
# 国内网站
- DOMAIN-SUFFIX,cn,DIRECT
- DOMAIN-KEYWORD,-cn,DIRECT
- DOMAIN-SUFFIX,126.com,DIRECT
- DOMAIN-SUFFIX,126.net,DIRECT
- DOMAIN-SUFFIX,127.net,DIRECT
- DOMAIN-SUFFIX,163.com,DIRECT
- DOMAIN-SUFFIX,360buyimg.com,DIRECT
- DOMAIN-SUFFIX,36kr.com,DIRECT
- DOMAIN-SUFFIX,acfun.tv,DIRECT
- DOMAIN-SUFFIX,air-matters.com,DIRECT
- DOMAIN-SUFFIX,aixifan.com,DIRECT
- DOMAIN-SUFFIX,akamaized.net,DIRECT
- DOMAIN-KEYWORD,alicdn,DIRECT
- DOMAIN-KEYWORD,alipay,DIRECT
- DOMAIN-KEYWORD,taobao,DIRECT
- DOMAIN-SUFFIX,amap.com,DIRECT
- DOMAIN-SUFFIX,autonavi.com,DIRECT
- DOMAIN-KEYWORD,baidu,DIRECT
- DOMAIN-SUFFIX,bdimg.com,DIRECT
- DOMAIN-SUFFIX,bdstatic.com,DIRECT
- DOMAIN-SUFFIX,bilibili.com,DIRECT
- DOMAIN-SUFFIX,bilivideo.com,DIRECT
- DOMAIN-SUFFIX,caiyunapp.com,DIRECT
- DOMAIN-SUFFIX,clouddn.com,DIRECT
- DOMAIN-SUFFIX,cnbeta.com,DIRECT
- DOMAIN-SUFFIX,cnbetacdn.com,DIRECT
- DOMAIN-SUFFIX,cootekservice.com,DIRECT
- DOMAIN-SUFFIX,csdn.net,DIRECT
- DOMAIN-SUFFIX,ctrip.com,DIRECT
- DOMAIN-SUFFIX,dgtle.com,DIRECT
- DOMAIN-SUFFIX,dianping.com,DIRECT
- DOMAIN-SUFFIX,douban.com,DIRECT
- DOMAIN-SUFFIX,doubanio.com,DIRECT
- DOMAIN-SUFFIX,duokan.com,DIRECT
- DOMAIN-SUFFIX,easou.com,DIRECT
- DOMAIN-SUFFIX,ele.me,DIRECT
- DOMAIN-SUFFIX,feng.com,DIRECT
- DOMAIN-SUFFIX,fir.im,DIRECT
- DOMAIN-SUFFIX,frdic.com,DIRECT
- DOMAIN-SUFFIX,g-cores.com,DIRECT
- DOMAIN-SUFFIX,godic.net,DIRECT
- DOMAIN-SUFFIX,gtimg.com,DIRECT
- DOMAIN,cdn.hockeyapp.net,DIRECT
- DOMAIN-SUFFIX,hdslb.com,DIRECT
- DOMAIN-SUFFIX,hongxiu.com,DIRECT
- DOMAIN-SUFFIX,hxcdn.net,DIRECT
- DOMAIN-SUFFIX,iciba.com,DIRECT
- DOMAIN-SUFFIX,ifeng.com,DIRECT
- DOMAIN-SUFFIX,ifengimg.com,DIRECT
- DOMAIN-SUFFIX,ipip.net,DIRECT
- DOMAIN-SUFFIX,iqiyi.com,DIRECT
- DOMAIN-SUFFIX,jd.com,DIRECT
- DOMAIN-SUFFIX,jianshu.com,DIRECT
- DOMAIN-SUFFIX,knewone.com,DIRECT
- DOMAIN-SUFFIX,le.com,DIRECT
- DOMAIN-SUFFIX,lecloud.com,DIRECT
- DOMAIN-SUFFIX,lemicp.com,DIRECT
- DOMAIN-SUFFIX,licdn.com,DIRECT
- DOMAIN-SUFFIX,linkedin.com,DIRECT
- DOMAIN-SUFFIX,luoo.net,DIRECT
- DOMAIN-SUFFIX,meituan.com,DIRECT
- DOMAIN-SUFFIX,meituan.net,DIRECT
- DOMAIN-SUFFIX,mi.com,DIRECT
- DOMAIN-SUFFIX,miaopai.com,DIRECT
- DOMAIN-SUFFIX,microsoft.com,DIRECT
- DOMAIN-SUFFIX,microsoftonline.com,DIRECT
- DOMAIN-SUFFIX,miui.com,DIRECT
- DOMAIN-SUFFIX,miwifi.com,DIRECT
- DOMAIN-SUFFIX,mob.com,DIRECT
- DOMAIN-SUFFIX,netease.com,DIRECT
- DOMAIN-SUFFIX,office.com,DIRECT
- DOMAIN-SUFFIX,office365.com,DIRECT
- DOMAIN-KEYWORD,officecdn,DIRECT
- DOMAIN-SUFFIX,oschina.net,DIRECT
- DOMAIN-SUFFIX,ppsimg.com,DIRECT
- DOMAIN-SUFFIX,pstatp.com,DIRECT
- DOMAIN-SUFFIX,qcloud.com,DIRECT
- DOMAIN-SUFFIX,qdaily.com,DIRECT
- DOMAIN-SUFFIX,qdmm.com,DIRECT
- DOMAIN-SUFFIX,qhimg.com,DIRECT
- DOMAIN-SUFFIX,qhres.com,DIRECT
- DOMAIN-SUFFIX,qidian.com,DIRECT
- DOMAIN-SUFFIX,qihucdn.com,DIRECT
- DOMAIN-SUFFIX,qiniu.com,DIRECT
- DOMAIN-SUFFIX,qiniucdn.com,DIRECT
- DOMAIN-SUFFIX,qiyipic.com,DIRECT
- DOMAIN-SUFFIX,qq.com,DIRECT
- DOMAIN-SUFFIX,qqurl.com,DIRECT
- DOMAIN-SUFFIX,rarbg.to,DIRECT
- DOMAIN-SUFFIX,ruguoapp.com,DIRECT
- DOMAIN-SUFFIX,segmentfault.com,DIRECT
- DOMAIN-SUFFIX,sinaapp.com,DIRECT
- DOMAIN-SUFFIX,smzdm.com,DIRECT
- DOMAIN-SUFFIX,snapdrop.net,DIRECT
- DOMAIN-SUFFIX,sogou.com,DIRECT
- DOMAIN-SUFFIX,sogoucdn.com,DIRECT
- DOMAIN-SUFFIX,sohu.com,DIRECT
- DOMAIN-SUFFIX,soku.com,DIRECT
- DOMAIN-SUFFIX,speedtest.net,DIRECT
- DOMAIN-SUFFIX,sspai.com,DIRECT
- DOMAIN-SUFFIX,suning.com,DIRECT
- DOMAIN-SUFFIX,taobao.com,DIRECT
- DOMAIN-SUFFIX,tencent.com,DIRECT
- DOMAIN-SUFFIX,tenpay.com,DIRECT
- DOMAIN-SUFFIX,tianyancha.com,DIRECT
- DOMAIN-SUFFIX,tmall.com,DIRECT
- DOMAIN-SUFFIX,tudou.com,DIRECT
- DOMAIN-SUFFIX,umetrip.com,DIRECT
- DOMAIN-SUFFIX,upaiyun.com,DIRECT
- DOMAIN-SUFFIX,upyun.com,DIRECT
- DOMAIN-SUFFIX,veryzhun.com,DIRECT
- DOMAIN-SUFFIX,weather.com,DIRECT
- DOMAIN-SUFFIX,weibo.com,DIRECT
- DOMAIN-SUFFIX,xiami.com,DIRECT
- DOMAIN-SUFFIX,xiami.net,DIRECT
- DOMAIN-SUFFIX,xiaomicp.com,DIRECT
- DOMAIN-SUFFIX,ximalaya.com,DIRECT
- DOMAIN-SUFFIX,xmcdn.com,DIRECT
- DOMAIN-SUFFIX,xunlei.com,DIRECT
- DOMAIN-SUFFIX,yhd.com,DIRECT
- DOMAIN-SUFFIX,yihaodianimg.com,DIRECT
- DOMAIN-SUFFIX,yinxiang.com,DIRECT
- DOMAIN-SUFFIX,ykimg.com,DIRECT
- DOMAIN-SUFFIX,youdao.com,DIRECT
- DOMAIN-SUFFIX,youku.com,DIRECT
- DOMAIN-SUFFIX,zealer.com,DIRECT
- DOMAIN-SUFFIX,zhihu.com,DIRECT
- DOMAIN-SUFFIX,zhimg.com,DIRECT
- DOMAIN-SUFFIX,zimuzu.tv,DIRECT
- DOMAIN-SUFFIX,zoho.com,DIRECT
# 抗 DNS 污染
- DOMAIN-KEYWORD,amazon,SELECT
- DOMAIN-KEYWORD,google,SELECT
- DOMAIN-KEYWORD,gmail,SELECT
- DOMAIN-KEYWORD,youtube,SELECT
- DOMAIN-KEYWORD,facebook,SELECT
- DOMAIN-SUFFIX,fb.me,SELECT
- DOMAIN-SUFFIX,fbcdn.net,SELECT
- DOMAIN-KEYWORD,twitter,SELECT
- DOMAIN-KEYWORD,instagram,SELECT
- DOMAIN-KEYWORD,dropbox,SELECT
- DOMAIN-SUFFIX,twimg.com,SELECT
- DOMAIN-KEYWORD,blogspot,SELECT
- DOMAIN-SUFFIX,youtu.be,SELECT
- DOMAIN-KEYWORD,whatsapp,SELECT
# 常见广告域名屏蔽
- DOMAIN-KEYWORD,admarvel,REJECT
- DOMAIN-KEYWORD,admaster,REJECT
- DOMAIN-KEYWORD,adsage,REJECT
- DOMAIN-KEYWORD,adsmogo,REJECT
- DOMAIN-KEYWORD,adsrvmedia,REJECT
- DOMAIN-KEYWORD,adwords,REJECT
- DOMAIN-KEYWORD,adservice,REJECT
- DOMAIN-KEYWORD,domob,REJECT
- DOMAIN-KEYWORD,duomeng,REJECT
- DOMAIN-KEYWORD,dwtrack,REJECT
- DOMAIN-KEYWORD,guanggao,REJECT
- DOMAIN-KEYWORD,lianmeng,REJECT
- DOMAIN-SUFFIX,mmstat.com,REJECT
- DOMAIN-KEYWORD,omgmta,REJECT
- DOMAIN-KEYWORD,openx,REJECT
- DOMAIN-KEYWORD,partnerad,REJECT
- DOMAIN-KEYWORD,pingfore,REJECT
- DOMAIN-KEYWORD,supersonicads,REJECT
- DOMAIN-KEYWORD,tracking,REJECT
- DOMAIN-KEYWORD,uedas,REJECT
- DOMAIN-KEYWORD,umeng,REJECT
- DOMAIN-KEYWORD,usage,REJECT
- DOMAIN-KEYWORD,wlmonitor,REJECT
- DOMAIN-KEYWORD,zjtoolbar,REJECT
# 国外网站
- DOMAIN-SUFFIX,9to5mac.com,SELECT
- DOMAIN-SUFFIX,abpchina.org,SELECT
- DOMAIN-SUFFIX,adblockplus.org,SELECT
- DOMAIN-SUFFIX,adobe.com,SELECT
- DOMAIN-SUFFIX,alfredapp.com,SELECT
- DOMAIN-SUFFIX,amplitude.com,SELECT
- DOMAIN-SUFFIX,ampproject.org,SELECT
- DOMAIN-SUFFIX,android.com,SELECT
- DOMAIN-SUFFIX,angularjs.org,SELECT
- DOMAIN-SUFFIX,aolcdn.com,SELECT
- DOMAIN-SUFFIX,apkpure.com,SELECT
- DOMAIN-SUFFIX,appledaily.com,SELECT
- DOMAIN-SUFFIX,appshopper.com,SELECT
- DOMAIN-SUFFIX,appspot.com,SELECT
- DOMAIN-SUFFIX,arcgis.com,SELECT
- DOMAIN-SUFFIX,archive.org,SELECT
- DOMAIN-SUFFIX,armorgames.com,SELECT
- DOMAIN-SUFFIX,aspnetcdn.com,SELECT
- DOMAIN-SUFFIX,att.com,SELECT
- DOMAIN-SUFFIX,awsstatic.com,SELECT
- DOMAIN-SUFFIX,azureedge.net,SELECT
- DOMAIN-SUFFIX,azurewebsites.net,SELECT
- DOMAIN-SUFFIX,bing.com,SELECT
- DOMAIN-SUFFIX,bintray.com,SELECT
- DOMAIN-SUFFIX,bit.com,SELECT
- DOMAIN-SUFFIX,bit.ly,SELECT
- DOMAIN-SUFFIX,bitbucket.org,SELECT
- DOMAIN-SUFFIX,bjango.com,SELECT
- DOMAIN-SUFFIX,bkrtx.com,SELECT
- DOMAIN-SUFFIX,blog.com,SELECT
- DOMAIN-SUFFIX,blogcdn.com,SELECT
- DOMAIN-SUFFIX,blogger.com,SELECT
- DOMAIN-SUFFIX,blogsmithmedia.com,SELECT
- DOMAIN-SUFFIX,blogspot.com,SELECT
- DOMAIN-SUFFIX,blogspot.hk,SELECT
- DOMAIN-SUFFIX,bloomberg.com,SELECT
- DOMAIN-SUFFIX,box.com,SELECT
- DOMAIN-SUFFIX,box.net,SELECT
- DOMAIN-SUFFIX,cachefly.net,SELECT
- DOMAIN-SUFFIX,chromium.org,SELECT
- DOMAIN-SUFFIX,cl.ly,SELECT
- DOMAIN-SUFFIX,cloudflare.com,SELECT
- DOMAIN-SUFFIX,cloudfront.net,SELECT
- DOMAIN-SUFFIX,cloudmagic.com,SELECT
- DOMAIN-SUFFIX,cmail19.com,SELECT
- DOMAIN-SUFFIX,cnet.com,SELECT
- DOMAIN-SUFFIX,cocoapods.org,SELECT
- DOMAIN-SUFFIX,comodoca.com,SELECT
- DOMAIN-SUFFIX,crashlytics.com,SELECT
- DOMAIN-SUFFIX,culturedcode.com,SELECT
- DOMAIN-SUFFIX,d.pr,SELECT
- DOMAIN-SUFFIX,danilo.to,SELECT
- DOMAIN-SUFFIX,dayone.me,SELECT
- DOMAIN-SUFFIX,db.tt,SELECT
- DOMAIN-SUFFIX,deskconnect.com,SELECT
- DOMAIN-SUFFIX,disq.us,SELECT
- DOMAIN-SUFFIX,disqus.com,SELECT
- DOMAIN-SUFFIX,disquscdn.com,SELECT
- DOMAIN-SUFFIX,dnsimple.com,SELECT
- DOMAIN-SUFFIX,docker.com,SELECT
- DOMAIN-SUFFIX,dribbble.com,SELECT
- DOMAIN-SUFFIX,droplr.com,SELECT
- DOMAIN-SUFFIX,duckduckgo.com,SELECT
- DOMAIN-SUFFIX,dueapp.com,SELECT
- DOMAIN-SUFFIX,dytt8.net,SELECT
- DOMAIN-SUFFIX,edgecastcdn.net,SELECT
- DOMAIN-SUFFIX,edgekey.net,SELECT
- DOMAIN-SUFFIX,edgesuite.net,SELECT
- DOMAIN-SUFFIX,engadget.com,SELECT
- DOMAIN-SUFFIX,entrust.net,SELECT
- DOMAIN-SUFFIX,eurekavpt.com,SELECT
- DOMAIN-SUFFIX,evernote.com,SELECT
- DOMAIN-SUFFIX,fabric.io,SELECT
- DOMAIN-SUFFIX,fast.com,SELECT
- DOMAIN-SUFFIX,fastly.net,SELECT
- DOMAIN-SUFFIX,fc2.com,SELECT
- DOMAIN-SUFFIX,feedburner.com,SELECT
- DOMAIN-SUFFIX,feedly.com,SELECT
- DOMAIN-SUFFIX,feedsportal.com,SELECT
- DOMAIN-SUFFIX,fiftythree.com,SELECT
- DOMAIN-SUFFIX,firebaseio.com,SELECT
- DOMAIN-SUFFIX,flexibits.com,SELECT
- DOMAIN-SUFFIX,flickr.com,SELECT
- DOMAIN-SUFFIX,flipboard.com,SELECT
- DOMAIN-SUFFIX,g.co,SELECT
- DOMAIN-SUFFIX,gabia.net,SELECT
- DOMAIN-SUFFIX,geni.us,SELECT
- DOMAIN-SUFFIX,gfx.ms,SELECT
- DOMAIN-SUFFIX,ggpht.com,SELECT
- DOMAIN-SUFFIX,ghostnoteapp.com,SELECT
- DOMAIN-SUFFIX,git.io,SELECT
- DOMAIN-KEYWORD,github,SELECT
- DOMAIN-SUFFIX,globalsign.com,SELECT
- DOMAIN-SUFFIX,gmodules.com,SELECT
- DOMAIN-SUFFIX,godaddy.com,SELECT
- DOMAIN-SUFFIX,golang.org,SELECT
- DOMAIN-SUFFIX,gongm.in,SELECT
- DOMAIN-SUFFIX,goo.gl,SELECT
- DOMAIN-SUFFIX,goodreaders.com,SELECT
- DOMAIN-SUFFIX,goodreads.com,SELECT
- DOMAIN-SUFFIX,gravatar.com,SELECT
- DOMAIN-SUFFIX,gstatic.com,SELECT
- DOMAIN-SUFFIX,gvt0.com,SELECT
- DOMAIN-SUFFIX,hockeyapp.net,SELECT
- DOMAIN-SUFFIX,hotmail.com,SELECT
- DOMAIN-SUFFIX,icons8.com,SELECT
- DOMAIN-SUFFIX,ifixit.com,SELECT
- DOMAIN-SUFFIX,ift.tt,SELECT
- DOMAIN-SUFFIX,ifttt.com,SELECT
- DOMAIN-SUFFIX,iherb.com,SELECT
- DOMAIN-SUFFIX,imageshack.us,SELECT
- DOMAIN-SUFFIX,img.ly,SELECT
- DOMAIN-SUFFIX,imgur.com,SELECT
- DOMAIN-SUFFIX,imore.com,SELECT
- DOMAIN-SUFFIX,instapaper.com,SELECT
- DOMAIN-SUFFIX,ipn.li,SELECT
- DOMAIN-SUFFIX,is.gd,SELECT
- DOMAIN-SUFFIX,issuu.com,SELECT
- DOMAIN-SUFFIX,itgonglun.com,SELECT
- DOMAIN-SUFFIX,itun.es,SELECT
- DOMAIN-SUFFIX,ixquick.com,SELECT
- DOMAIN-SUFFIX,j.mp,SELECT
- DOMAIN-SUFFIX,js.revsci.net,SELECT
- DOMAIN-SUFFIX,jshint.com,SELECT
- DOMAIN-SUFFIX,jtvnw.net,SELECT
- DOMAIN-SUFFIX,justgetflux.com,SELECT
- DOMAIN-SUFFIX,kat.cr,SELECT
- DOMAIN-SUFFIX,klip.me,SELECT
- DOMAIN-SUFFIX,libsyn.com,SELECT
- DOMAIN-SUFFIX,linode.com,SELECT
- DOMAIN-SUFFIX,lithium.com,SELECT
- DOMAIN-SUFFIX,littlehj.com,SELECT
- DOMAIN-SUFFIX,live.com,SELECT
- DOMAIN-SUFFIX,live.net,SELECT
- DOMAIN-SUFFIX,livefilestore.com,SELECT
- DOMAIN-SUFFIX,llnwd.net,SELECT
- DOMAIN-SUFFIX,macid.co,SELECT
- DOMAIN-SUFFIX,macromedia.com,SELECT
- DOMAIN-SUFFIX,macrumors.com,SELECT
- DOMAIN-SUFFIX,mashable.com,SELECT
- DOMAIN-SUFFIX,mathjax.org,SELECT
- DOMAIN-SUFFIX,medium.com,SELECT
- DOMAIN-SUFFIX,mega.co.nz,SELECT
- DOMAIN-SUFFIX,mega.nz,SELECT
- DOMAIN-SUFFIX,megaupload.com,SELECT
- DOMAIN-SUFFIX,microsofttranslator.com,SELECT
- DOMAIN-SUFFIX,mindnode.com,SELECT
- DOMAIN-SUFFIX,mobile01.com,SELECT
- DOMAIN-SUFFIX,modmyi.com,SELECT
- DOMAIN-SUFFIX,msedge.net,SELECT
- DOMAIN-SUFFIX,myfontastic.com,SELECT
- DOMAIN-SUFFIX,name.com,SELECT
- DOMAIN-SUFFIX,nextmedia.com,SELECT
- DOMAIN-SUFFIX,nsstatic.net,SELECT
- DOMAIN-SUFFIX,nssurge.com,SELECT
- DOMAIN-SUFFIX,nyt.com,SELECT
- DOMAIN-SUFFIX,nytimes.com,SELECT
- DOMAIN-SUFFIX,omnigroup.com,SELECT
- DOMAIN-SUFFIX,onedrive.com,SELECT
- DOMAIN-SUFFIX,onenote.com,SELECT
- DOMAIN-SUFFIX,ooyala.com,SELECT
- DOMAIN-SUFFIX,openvpn.net,SELECT
- DOMAIN-SUFFIX,openwrt.org,SELECT
- DOMAIN-SUFFIX,orkut.com,SELECT
- DOMAIN-SUFFIX,osxdaily.com,SELECT
- DOMAIN-SUFFIX,outlook.com,SELECT
- DOMAIN-SUFFIX,ow.ly,SELECT
- DOMAIN-SUFFIX,paddleapi.com,SELECT
- DOMAIN-SUFFIX,parallels.com,SELECT
- DOMAIN-SUFFIX,parse.com,SELECT
- DOMAIN-SUFFIX,pdfexpert.com,SELECT
- DOMAIN-SUFFIX,periscope.tv,SELECT
- DOMAIN-SUFFIX,pinboard.in,SELECT
- DOMAIN-SUFFIX,pinterest.com,SELECT
- DOMAIN-SUFFIX,pixelmator.com,SELECT
- DOMAIN-SUFFIX,pixiv.net,SELECT
- DOMAIN-SUFFIX,playpcesor.com,SELECT
- DOMAIN-SUFFIX,playstation.com,SELECT
- DOMAIN-SUFFIX,playstation.com.hk,SELECT
- DOMAIN-SUFFIX,playstation.net,SELECT
- DOMAIN-SUFFIX,playstationnetwork.com,SELECT
- DOMAIN-SUFFIX,pushwoosh.com,SELECT
- DOMAIN-SUFFIX,rime.im,SELECT
- DOMAIN-SUFFIX,servebom.com,SELECT
- DOMAIN-SUFFIX,sfx.ms,SELECT
- DOMAIN-SUFFIX,shadowsocks.org,SELECT
- DOMAIN-SUFFIX,sharethis.com,SELECT
- DOMAIN-SUFFIX,shazam.com,SELECT
- DOMAIN-SUFFIX,skype.com,SELECT
- DOMAIN-SUFFIX,smartdnsSELECT.com,SELECT
- DOMAIN-SUFFIX,smartmailcloud.com,SELECT
- DOMAIN-SUFFIX,sndcdn.com,SELECT
- DOMAIN-SUFFIX,sony.com,SELECT
- DOMAIN-SUFFIX,soundcloud.com,SELECT
- DOMAIN-SUFFIX,sourceforge.net,SELECT
- DOMAIN-SUFFIX,spotify.com,SELECT
- DOMAIN-SUFFIX,squarespace.com,SELECT
- DOMAIN-SUFFIX,sstatic.net,SELECT
- DOMAIN-SUFFIX,st.luluku.pw,SELECT
- DOMAIN-SUFFIX,stackoverflow.com,SELECT
- DOMAIN-SUFFIX,startpage.com,SELECT
- DOMAIN-SUFFIX,staticflickr.com,SELECT
- DOMAIN-SUFFIX,steamcommunity.com,SELECT
- DOMAIN-SUFFIX,symauth.com,SELECT
- DOMAIN-SUFFIX,symcb.com,SELECT
- DOMAIN-SUFFIX,symcd.com,SELECT
- DOMAIN-SUFFIX,tapbots.com,SELECT
- DOMAIN-SUFFIX,tapbots.net,SELECT
- DOMAIN-SUFFIX,tdesktop.com,SELECT
- DOMAIN-SUFFIX,techcrunch.com,SELECT
- DOMAIN-SUFFIX,techsmith.com,SELECT
- DOMAIN-SUFFIX,thepiratebay.org,SELECT
- DOMAIN-SUFFIX,theverge.com,SELECT
- DOMAIN-SUFFIX,time.com,SELECT
- DOMAIN-SUFFIX,timeinc.net,SELECT
- DOMAIN-SUFFIX,tiny.cc,SELECT
- DOMAIN-SUFFIX,tinypic.com,SELECT
- DOMAIN-SUFFIX,tmblr.co,SELECT
- DOMAIN-SUFFIX,todoist.com,SELECT
- DOMAIN-SUFFIX,trello.com,SELECT
- DOMAIN-SUFFIX,trustasiassl.com,SELECT
- DOMAIN-SUFFIX,tumblr.co,SELECT
- DOMAIN-SUFFIX,tumblr.com,SELECT
- DOMAIN-SUFFIX,tweetdeck.com,SELECT
- DOMAIN-SUFFIX,tweetmarker.net,SELECT
- DOMAIN-SUFFIX,twitch.tv,SELECT
- DOMAIN-SUFFIX,txmblr.com,SELECT
- DOMAIN-SUFFIX,typekit.net,SELECT
- DOMAIN-SUFFIX,ubertags.com,SELECT
- DOMAIN-SUFFIX,ublock.org,SELECT
- DOMAIN-SUFFIX,ubnt.com,SELECT
- DOMAIN-SUFFIX,ulyssesapp.com,SELECT
- DOMAIN-SUFFIX,urchin.com,SELECT
- DOMAIN-SUFFIX,usertrust.com,SELECT
- DOMAIN-SUFFIX,v.gd,SELECT
- DOMAIN-SUFFIX,v2ex.com,SELECT
- DOMAIN-SUFFIX,vimeo.com,SELECT
- DOMAIN-SUFFIX,vimeocdn.com,SELECT
- DOMAIN-SUFFIX,vine.co,SELECT
- DOMAIN-SUFFIX,vivaldi.com,SELECT
- DOMAIN-SUFFIX,vox-cdn.com,SELECT
- DOMAIN-SUFFIX,vsco.co,SELECT
- DOMAIN-SUFFIX,vultr.com,SELECT
- DOMAIN-SUFFIX,w.org,SELECT
- DOMAIN-SUFFIX,w3schools.com,SELECT
- DOMAIN-SUFFIX,webtype.com,SELECT
- DOMAIN-SUFFIX,wikiwand.com,SELECT
- DOMAIN-SUFFIX,wikileaks.org,SELECT
- DOMAIN-SUFFIX,wikimedia.org,SELECT
- DOMAIN-SUFFIX,wikipedia.com,SELECT
- DOMAIN-SUFFIX,wikipedia.org,SELECT
- DOMAIN-SUFFIX,windows.com,SELECT
- DOMAIN-SUFFIX,windows.net,SELECT
- DOMAIN-SUFFIX,wire.com,SELECT
- DOMAIN-SUFFIX,wordpress.com,SELECT
- DOMAIN-SUFFIX,workflowy.com,SELECT
- DOMAIN-SUFFIX,wp.com,SELECT
- DOMAIN-SUFFIX,wsj.com,SELECT
- DOMAIN-SUFFIX,wsj.net,SELECT
- DOMAIN-SUFFIX,xda-developers.com,SELECT
- DOMAIN-SUFFIX,xeeno.com,SELECT
- DOMAIN-SUFFIX,xiti.com,SELECT
- DOMAIN-SUFFIX,yahoo.com,SELECT
- DOMAIN-SUFFIX,yimg.com,SELECT
- DOMAIN-SUFFIX,ying.com,SELECT
- DOMAIN-SUFFIX,yoyo.org,SELECT
- DOMAIN-SUFFIX,ytimg.com,SELECT
# Telegram
- DOMAIN-SUFFIX,telegra.ph,SELECT
- DOMAIN-SUFFIX,telegram.org,SELECT
- IP-CIDR,91.108.4.0/22,SELECT,no-resolve
- IP-CIDR,91.108.8.0/22,SELECT,no-resolve
- IP-CIDR,91.108.12.0/22,SELECT,no-resolve
- IP-CIDR,91.108.16.0/22,SELECT,no-resolve
- IP-CIDR,91.108.56.0/22,SELECT,no-resolve
- IP-CIDR,149.154.160.0/22,SELECT,no-resolve
- IP-CIDR,149.154.164.0/22,SELECT,no-resolve
- IP-CIDR,149.154.168.0/22,SELECT,no-resolve
- IP-CIDR,149.154.172.0/22,SELECT,no-resolve
# LAN
- DOMAIN-SUFFIX,local,DIRECT
- IP-CIDR,127.0.0.0/8,DIRECT
- IP-CIDR,172.16.0.0/12,DIRECT
- IP-CIDR,192.168.0.0/16,DIRECT
- IP-CIDR,10.0.0.0/8,DIRECT
- IP-CIDR,17.0.0.0/8,DIRECT
- IP-CIDR,100.64.0.0/10,DIRECT
# 最终规则
- GEOIP,CN,DIRECT
- MATCH,SELECT

View File

@ -1,7 +1,7 @@
port: 7890
socks-port: 7891
allow-lan: false
mode: Rule
mode: rule
log-level: info
external-controller: 127.0.0.1:9090
experimental:
@ -16,14 +16,14 @@ dns:
fallback:
- tls://1.0.0.1:853
- tls://dns.google:853
Proxy:
proxies:
Proxy Group:
proxy-groups:
- { name: "$app_name", type: select, proxies: ["自动选择", "故障转移"] }
- { name: "自动选择", type: url-test, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 86400 }
- { name: "故障转移", type: fallback, proxies: [], url: "http://www.gstatic.com/generate_204", interval: 7200 }
Rule:
rules:
# Apple
- DOMAIN,safebrowsing.urlsec.qq.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。
- DOMAIN,safebrowsing.googleapis.com,DIRECT # 如果您并不信任此服务提供商或防止其下载消耗过多带宽资源,可以进入 Safari 设置,关闭 Fraudulent Website Warning 功能,并使用 REJECT 策略。

View File

@ -13,7 +13,7 @@ http-listen = 0.0.0.0:6152
socks5-listen = 0.0.0.0:6153
test-timeout = 4
network-framework = true
network-framework = false
proxy-test-url = http://www.gstatic.com/generate_204
external-controller-access = surgepasswd@0.0.0.0:6170