Merge pull request #508 from v2board/dev

1.5.5
This commit is contained in:
tokumeikoi 2022-03-08 22:25:19 +08:00 committed by GitHub
commit e40d9231e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
95 changed files with 2908 additions and 1776 deletions

View File

@ -118,6 +118,8 @@ class CheckCommission extends Command
return false;
}
$inviteUserId = $inviter->invite_user_id;
// update order actual commission balance
$order->actual_commission_balance = $order->actual_commission_balance + $commissionBalance;
}
return true;
}

View File

@ -2,25 +2,24 @@
namespace App\Console\Commands;
use App\Models\Ticket;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use App\Models\ServerLog;
class ResetServerLog extends Command
class CheckTicket extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'reset:serverLog';
protected $signature = 'check:ticket';
/**
* The console command description.
*
* @var string
*/
protected $description = '节点服务器日志重置';
protected $description = '工单检查任务';
/**
* Create a new command instance.
@ -39,6 +38,14 @@ class ResetServerLog extends Command
*/
public function handle()
{
ServerLog::truncate();
ini_set('memory_limit', -1);
$tickets = Ticket::where('status', 0)
->where('updated_at', '<=', time() - 24 * 3600)
->get();
foreach ($tickets as $ticket) {
if ($ticket->user_id === $ticket->last_reply_user_id) continue;
$ticket->status = 1;
$ticket->save();
}
}
}

View File

@ -44,18 +44,27 @@ class ResetTraffic extends Command
public function handle()
{
ini_set('memory_limit', -1);
foreach (Plan::get() as $plan) {
switch ($plan->reset_traffic_method) {
case null: {
$resetMethods = Plan::select(
DB::raw("GROUP_CONCAT(`id`) as plan_ids"),
DB::raw("reset_traffic_method as method")
)
->groupBy('reset_traffic_method')
->get()
->toArray();
foreach ($resetMethods as $resetMethod) {
$planIds = explode(',', $resetMethod['plan_ids']);
switch (true) {
case ($resetMethod['method'] === NULL): {
$resetTrafficMethod = config('v2board.reset_traffic_method', 0);
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
switch ((int)$resetTrafficMethod) {
// month first day
case 0:
$this->resetByMonthFirstDay($this->builder);
$this->resetByMonthFirstDay($builder);
break;
// expire day
case 1:
$this->resetByExpireDay($this->builder);
$this->resetByExpireDay($builder);
break;
// no action
case 2:
@ -63,17 +72,17 @@ class ResetTraffic extends Command
}
break;
}
case 0: {
$builder = with(clone($this->builder))->where('plan_id', $plan->id);
case ($resetMethod['method'] === 0): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByMonthFirstDay($builder);
break;
}
case 1: {
$builder = with(clone($this->builder))->where('plan_id', $plan->id);
case ($resetMethod['method'] === 1): {
$builder = with(clone($this->builder))->whereIn('plan_id', $planIds);
$this->resetByExpireDay($builder);
break;
}
case 2: {
case ($resetMethod['method'] === 2): {
break;
}
}

View File

@ -42,8 +42,8 @@ class V2boardStatistics extends Command
*/
public function handle()
{
ini_set('memory_limit', -1);
$this->statOrder();
$this->statServer();
}
private function statOrder()
@ -55,9 +55,9 @@ class V2boardStatistics extends Command
->whereNotIn('status', [0, 2]);
$orderCount = $builder->count();
$orderAmount = $builder->sum('total_amount');
$builder = $builder->where('commission_balance', '!=', 0);
$builder = $builder->whereNotNull('actual_commission_balance');
$commissionCount = $builder->count();
$commissionAmount = $builder->sum('commission_balance');
$commissionAmount = $builder->sum('actual_commission_balance');
$data = [
'order_count' => $orderCount,
'order_amount' => $orderAmount,
@ -75,26 +75,4 @@ class V2boardStatistics extends Command
}
StatOrder::create($data);
}
private function statServer()
{
$endAt = strtotime(date('Y-m-d'));
$startAt = strtotime('-1 day', $endAt);
$statistics = ServerLog::select([
'server_id',
'method as server_type',
DB::raw("sum(u) as u"),
DB::raw("sum(d) as d"),
])
->where('log_at', '>=', $startAt)
->where('log_at', '<', $endAt)
->groupBy('server_id', 'method')
->get()
->toArray();
foreach ($statistics as $statistic) {
$statistic['record_type'] = 'd';
$statistic['record_at'] = $startAt;
StatServerJob::dispatch($statistic);
}
}
}

View File

@ -2,8 +2,10 @@
namespace App\Console;
use App\Utils\CacheKey;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Facades\Cache;
class Kernel extends ConsoleKernel
{
@ -24,14 +26,15 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
Cache::put(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null), time());
// v2board
$schedule->command('v2board:statistics')->dailyAt('0:10');
// check
$schedule->command('check:order')->everyMinute();
$schedule->command('check:commission')->everyMinute();
$schedule->command('check:ticket')->everyMinute();
// reset
$schedule->command('reset:traffic')->daily();
$schedule->command('reset:serverLog')->quarterly()->at('0:15');
// send
$schedule->command('send:remindMail')->dailyAt('11:30');
// horizon metrics

View File

@ -30,6 +30,25 @@ class CouponController extends Controller
]);
}
public function show(Request $request)
{
if (empty($request->input('id'))) {
abort(500, '参数有误');
}
$coupon = Coupon::find($request->input('id'));
if (!$coupon) {
abort(500, '优惠券不存在');
}
$coupon->show = $coupon->show ? 0 : 1;
if (!$coupon->save()) {
abort(500, '保存失败');
}
return response([
'data' => true
]);
}
public function generate(CouponGenerate $request)
{
if ($request->input('generate_count')) {
@ -63,8 +82,6 @@ class CouponController extends Controller
$coupons = [];
$coupon = $request->validated();
$coupon['created_at'] = $coupon['updated_at'] = time();
$coupon['limit_plan_ids'] = json_encode($coupon['limit_plan_ids']);
$coupon['limit_period'] = json_encode($coupon['limit_period']);
unset($coupon['generate_count']);
for ($i = 0;$i < $request->input('generate_count');$i++) {
$coupon['code'] = Helper::randomChar(8);
@ -84,7 +101,7 @@ class CouponController extends Controller
$endTime = date('Y-m-d H:i:s', $coupon['ended_at']);
$limitUse = $coupon['limit_use'] ?? '不限制';
$createTime = date('Y-m-d H:i:s', $coupon['created_at']);
$limitPlanIds = implode("/", json_decode($coupon['limit_plan_ids'], true)) ?? '不限制';
$limitPlanIds = isset($coupon['limit_plan_ids']) ? implode("/", $coupon['limit_plan_ids']) : '不限制';
$data .= "{$coupon['name']},{$type},{$value},{$startTime},{$endTime},{$limitUse},{$limitPlanIds},{$coupon['code']},{$createTime}\r\n";
}
echo $data;

View File

@ -40,6 +40,27 @@ class NoticeController extends Controller
]);
}
public function show(Request $request)
{
if (empty($request->input('id'))) {
abort(500, '参数有误');
}
$notice = Notice::find($request->input('id'));
if (!$notice) {
abort(500, '公告不存在');
}
$notice->show = $notice->show ? 0 : 1;
if (!$notice->save()) {
abort(500, '保存失败');
}
return response([
'data' => true
]);
}
public function drop(Request $request)
{
if (empty($request->input('id'))) {

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
use App\Http\Requests\Admin\OrderAssign;
use App\Http\Requests\Admin\OrderUpdate;
use App\Http\Requests\Admin\OrderFetch;
use App\Models\CommissionLog;
use App\Services\OrderService;
use App\Services\UserService;
use App\Utils\Helper;
@ -36,6 +37,19 @@ class OrderController extends Controller
}
}
public function detail(Request $request)
{
$order = Order::find($request->input('id'));
if (!$order) abort(500, '订单不存在');
$order['commission_log'] = CommissionLog::where('trade_no', $order->trade_no)->get();
if ($order->surplus_order_ids) {
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
}
return response([
'data' => $order
]);
}
public function fetch(OrderFetch $request)
{
$current = $request->input('current') ? $request->input('current') : 1;

View File

@ -26,7 +26,12 @@ class PaymentController extends Controller
{
$payments = Payment::all();
foreach ($payments as $k => $v) {
$payments[$k]['notify_url'] = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}");
$notifyUrl = url("/api/v1/guest/payment/notify/{$v->payment}/{$v->uuid}");
if ($v->notify_domain) {
$parseUrl = parse_url($notifyUrl);
$notifyUrl = $v->notify_domain . $parseUrl['path'];
}
$payments[$k]['notify_url'] = $notifyUrl;
}
return response([
'data' => $payments
@ -58,22 +63,20 @@ class PaymentController extends Controller
'data' => true
]);
}
$request->validate([
$params = $request->validate([
'name' => 'required',
'icon' => 'nullable',
'payment' => 'required',
'config' => 'required'
'config' => 'required',
'notify_domain' => 'nullable|url'
], [
'name.required' => '显示名称不能为空',
'payment.required' => '网关参数不能为空',
'config.required' => '配置参数不能为空'
'config.required' => '配置参数不能为空',
'notify_domain.url' => '自定义通知域名格式有误'
]);
if (!Payment::create([
'name' => $request->input('name'),
'icon' => $request->input('icon'),
'payment' => $request->input('payment'),
'config' => $request->input('config'),
'uuid' => Helper::randomChar(8)
])) {
$params['uuid'] = Helper::randomChar(8);
if (!Payment::create($params)) {
abort(500, '保存失败');
}
return response([

View File

@ -31,9 +31,9 @@ class StatController extends Controller
'month_register_total' => User::where('created_at', '>=', strtotime(date('Y-m-1')))
->where('created_at', '<', time())
->count(),
'ticket_pendding_total' => Ticket::where('status', 0)
'ticket_pending_total' => Ticket::where('status', 0)
->count(),
'commission_pendding_total' => Order::where('commission_status', 0)
'commission_pending_total' => Order::where('commission_status', 0)
->where('invite_user_id', '!=', NULL)
->whereNotIn('status', [0, 2])
->where('commission_balance', '>', 0)

View File

@ -0,0 +1,52 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Models\ServerShadowsocks;
use App\Models\ServerTrojan;
use App\Services\ServerService;
use App\Utils\CacheKey;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\ServerGroup;
use App\Models\ServerV2ray;
use App\Models\Plan;
use App\Models\User;
use App\Models\Ticket;
use App\Models\Order;
use App\Models\StatOrder;
use App\Models\StatServer;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use Laravel\Horizon\Contracts\MasterSupervisorRepository;
class SystemController extends Controller
{
public function getStatus()
{
return response([
'data' => [
'schedule' => $this->getScheduleStatus(),
'horizon' => $this->getHorizonStatus()
]
]);
}
protected function getScheduleStatus():bool
{
return (time() - 120) < Cache::get(CacheKey::get('SCHEDULE_LAST_CHECK_AT', null));
}
protected function getHorizonStatus():bool
{
if (! $masters = app(MasterSupervisorRepository::class)->all()) {
return false;
}
return collect($masters)->contains(function ($master) {
return $master->status === 'paused';
}) ? false : true;
}
}

View File

@ -58,7 +58,11 @@ class UserController extends Controller
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
$sortType = in_array($request->input('sort_type'), ['ASC', 'DESC']) ? $request->input('sort_type') : 'DESC';
$sort = $request->input('sort') ? $request->input('sort') : 'created_at';
$userModel = User::orderBy($sort, $sortType);
$userModel = User::select(
DB::raw('*'),
DB::raw('(u+d) as total_used')
)
->orderBy($sort, $sortType);
$this->filter($request, $userModel);
$total = $userModel->count();
$res = $userModel->forPage($current, $pageSize)

View File

@ -13,10 +13,6 @@ use Symfony\Component\Yaml\Yaml;
class AppController extends Controller
{
CONST CLIENT_CONFIG = '{"policy":{"levels":{"0":{"uplinkOnly":0}}},"dns":{"servers":["114.114.114.114","8.8.8.8"]},"outboundDetour":[{"protocol":"freedom","tag":"direct","settings":{}}],"inbound":{"listen":"0.0.0.0","port":31211,"protocol":"socks","settings":{"auth":"noauth","udp":true,"ip":"127.0.0.1"}},"inboundDetour":[{"listen":"0.0.0.0","allocate":{"strategy":"always","refresh":5,"concurrency":3},"port":31210,"protocol":"http","tag":"httpDetour","domainOverride":["http","tls"],"streamSettings":{},"settings":{"timeout":0}}],"routing":{"strategy":"rules","settings":{"domainStrategy":"IPIfNonMatch","rules":[{"type":"field","ip":["geoip:cn"],"outboundTag":"direct"},{"type":"field","ip":["0.0.0.0/8","10.0.0.0/8","100.64.0.0/10","127.0.0.0/8","169.254.0.0/16","172.16.0.0/12","192.0.0.0/24","192.0.2.0/24","192.168.0.0/16","198.18.0.0/15","198.51.100.0/24","203.0.113.0/24","::1/128","fc00::/7","fe80::/10"],"outboundTag":"direct"}]}},"outbound":{"tag":"proxy","sendThrough":"0.0.0.0","mux":{"enabled":false,"concurrency":8},"protocol":"vmess","settings":{"vnext":[{"address":"server","port":443,"users":[{"id":"uuid","alterId":2,"security":"auto","level":0}],"remark":"remark"}]},"streamSettings":{"network":"tcp","tcpSettings":{"header":{"type":"none"}},"security":"none","tlsSettings":{"allowInsecure":true,"allowInsecureCiphers":true},"kcpSettings":{"header":{"type":"none"},"mtu":1350,"congestion":false,"tti":20,"uplinkCapacity":5,"writeBufferSize":1,"readBufferSize":1,"downlinkCapacity":20},"wsSettings":{"path":"","headers":{"Host":"server.cc"}}}}}';
CONST SOCKS_PORT = 10010;
CONST HTTP_PORT = 10011;
public function getConfig(Request $request)
{
$servers = [];

View File

@ -23,7 +23,7 @@ class Clash
$appName = config('v2board.app_name', 'V2Board');
header("subscription-userinfo: upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}");
header('profile-update-interval: 24');
header("content-disposition: filename={$appName}");
header("content-disposition:attachment;filename={$appName}");
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
if (\File::exists($customConfig)) {
@ -51,20 +51,21 @@ class Clash
$config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy);
foreach ($config['proxy-groups'] as $k => $v) {
if (!is_array($config['proxy-groups'][$k]['proxies'])) continue;
if (!is_array($config['proxy-groups'][$k]['proxies'])) $config['proxy-groups'][$k]['proxies'] = [];
$isFilter = false;
foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
foreach ($proxies as $dst) {
if (!$this->isRegex($src)) continue;
$isFilter = true;
$config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src]));
if ($this->isMatch($src, $dst)) {
$isFilter = true;
$config['proxy-groups'][$k]['proxies'] = array_diff($config['proxy-groups'][$k]['proxies'], [$src]);
array_push($config['proxy-groups'][$k]['proxies'], $dst);
}
}
if ($isFilter) continue;
}
if (!$isFilter) {
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
if ($isFilter) continue;
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
// Force the current subscription domain to be a direct rule
$subsDomain = $_SERVER['SERVER_NAME'];
@ -97,7 +98,7 @@ class Clash
$array['server'] = $server['host'];
$array['port'] = $server['port'];
$array['uuid'] = $uuid;
$array['alterId'] = $server['alter_id'];
$array['alterId'] = 0;
$array['cipher'] = 'auto';
$array['udp'] = true;
@ -115,6 +116,12 @@ class Clash
$array['network'] = 'ws';
if ($server['networkSettings']) {
$wsSettings = $server['networkSettings'];
$array['ws-opts'] = [];
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
$array['ws-opts']['path'] = $wsSettings['path'];
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
$array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
// TODO: 2022.06.01 remove it
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
$array['ws-path'] = $wsSettings['path'];
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
@ -124,9 +131,9 @@ class Clash
if ($server['network'] === 'grpc') {
$array['network'] = 'grpc';
if ($server['networkSettings']) {
$grpcObject = $server['networkSettings'];
$grpcSettings = $server['networkSettings'];
$array['grpc-opts'] = [];
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
$array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
}
}
@ -149,10 +156,11 @@ class Clash
private function isMatch($exp, $str)
{
try {
return preg_match($exp, $str);
} catch (\Exception $e) {
return false;
}
return @preg_match($exp, $str);
}
private function isRegex($exp)
{
return @preg_match($exp, null) !== false;
}
}

View File

@ -54,7 +54,7 @@ class Passwall
"add" => $server['host'],
"port" => (string)$server['port'],
"id" => $uuid,
"aid" => (string)$server['alter_id'],
"aid" => '0',
"net" => $server['network'],
"type" => "none",
"host" => "",

View File

@ -54,7 +54,7 @@ class SSRPlus
"add" => $server['host'],
"port" => (string)$server['port'],
"id" => $uuid,
"aid" => (string)$server['alter_id'],
"aid" => '0',
"net" => $server['network'],
"type" => "none",
"host" => "",

View File

@ -58,7 +58,7 @@ class Shadowrocket
$config = [
'tfo' => 1,
'remark' => $server['name'],
'alterId' => $server['alter_id']
'alterId' => 0
];
if ($server['tls']) {
$config['tls'] = 1;

View File

@ -56,16 +56,17 @@ class Stash
$isFilter = false;
foreach ($config['proxy-groups'][$k]['proxies'] as $src) {
foreach ($proxies as $dst) {
if (!$this->isRegex($src)) continue;
$isFilter = true;
$config['proxy-groups'][$k]['proxies'] = array_values(array_diff($config['proxy-groups'][$k]['proxies'], [$src]));
if ($this->isMatch($src, $dst)) {
$isFilter = true;
$config['proxy-groups'][$k]['proxies'] = array_diff($config['proxy-groups'][$k]['proxies'], [$src]);
array_push($config['proxy-groups'][$k]['proxies'], $dst);
}
}
if ($isFilter) continue;
}
if (!$isFilter) {
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
if ($isFilter) continue;
$config['proxy-groups'][$k]['proxies'] = array_merge($config['proxy-groups'][$k]['proxies'], $proxies);
}
// Force the current subscription domain to be a direct rule
$subsDomain = $_SERVER['SERVER_NAME'];
@ -98,7 +99,7 @@ class Stash
$array['server'] = $server['host'];
$array['port'] = $server['port'];
$array['uuid'] = $uuid;
$array['alterId'] = $server['alter_id'];
$array['alterId'] = 0;
$array['cipher'] = 'auto';
$array['udp'] = true;
@ -116,6 +117,12 @@ class Stash
$array['network'] = 'ws';
if ($server['networkSettings']) {
$wsSettings = $server['networkSettings'];
$array['ws-opts'] = [];
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
$array['ws-opts']['path'] = $wsSettings['path'];
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
$array['ws-opts']['headers'] = ['Host' => $wsSettings['headers']['Host']];
// TODO: 2022.06.01 remove it
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
$array['ws-path'] = $wsSettings['path'];
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
@ -125,9 +132,9 @@ class Stash
if ($server['network'] === 'grpc') {
$array['network'] = 'grpc';
if ($server['networkSettings']) {
$grpcObject = $server['networkSettings'];
$grpcSettings = $server['networkSettings'];
$array['grpc-opts'] = [];
$array['grpc-opts']['grpc-service-name'] = $grpcObject['serviceName'];
$array['grpc-opts']['grpc-service-name'] = $grpcSettings['serviceName'];
}
}

View File

@ -88,6 +88,7 @@ class Surfboard
"{$server['host']}",
"{$server['port']}",
"username={$uuid}",
"vmess-aead=true",
'tfo=true',
'udp-relay=true'
];

View File

@ -87,6 +87,7 @@ class Surge
"{$server['host']}",
"{$server['port']}",
"username={$uuid}",
"vmess-aead=true",
'tfo=true',
'udp-relay=true'
];

View File

@ -54,7 +54,7 @@ class V2rayN
"add" => $server['host'],
"port" => (string)$server['port'],
"id" => $uuid,
"aid" => (string)$server['alter_id'],
"aid" => '0',
"net" => $server['network'],
"type" => "none",
"host" => "",

View File

@ -54,7 +54,7 @@ class V2rayNG
"add" => $server['host'],
"port" => (string)$server['port'],
"id" => $uuid,
"aid" => (string)$server['alter_id'],
"aid" => '0',
"net" => $server['network'],
"type" => "none",
"host" => "",

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Guest;
use App\Utils\Dict;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Http;
class CommController extends Controller
{
@ -33,4 +34,11 @@ class CommController extends Controller
}
return $suffix;
}
public function getHitokoto()
{
return response([
'data' => Http::get('https://v1.hitokoto.cn/')->json()
]);
}
}

View File

@ -20,7 +20,7 @@ class PaymentController extends Controller
if (!$this->handle($verify['trade_no'], $verify['callback_no'])) {
abort(500, 'handle error');
}
die(isset($paymentService->customResult) ? $paymentService->customResult : 'success');
die(isset($verify['custom_result']) ? $verify['custom_result'] : 'success');
} catch (\Exception $e) {
abort(500, 'fail');
}

View File

@ -5,18 +5,16 @@ namespace App\Http\Controllers\Guest;
use App\Services\TelegramService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Models\User;
use App\Utils\Helper;
use App\Services\TicketService;
class TelegramController extends Controller
{
protected $msg;
protected $commands = [];
public function __construct(Request $request)
{
if ($request->input('access_token') !== md5(config('v2board.telegram_bot_token'))) {
abort(500, 'authentication failed');
abort(401);
}
}
@ -24,41 +22,34 @@ class TelegramController extends Controller
{
$this->msg = $this->getMessage($request->input());
if (!$this->msg) return;
$this->handle();
}
public function handle()
{
$msg = $this->msg;
try {
switch($this->msg->message_type) {
case 'send':
$this->fromSend();
break;
case 'reply':
$this->fromReply();
break;
foreach (glob(base_path('app//Plugins//Telegram//Commands') . '/*.php') as $file) {
$command = basename($file, '.php');
$class = '\\App\\Plugins\\Telegram\\Commands\\' . $command;
if (!class_exists($class)) continue;
$instance = new $class();
if ($msg->message_type === 'message') {
if (!isset($instance->command)) continue;
if ($msg->command !== $instance->command) continue;
$instance->handle($msg);
return;
}
if ($msg->message_type === 'reply_message') {
if (!isset($instance->regex)) continue;
if (!preg_match($instance->regex, $msg->reply_text, $match)) continue;
$instance->handle($msg, $match);
return;
}
}
} catch (\Exception $e) {
$telegramService = new TelegramService();
$telegramService->sendMessage($this->msg->chat_id, $e->getMessage());
}
}
private function fromSend()
{
switch($this->msg->command) {
case '/bind': $this->bind();
break;
case '/traffic': $this->traffic();
break;
case '/getlatesturl': $this->getLatestUrl();
break;
case '/unbind': $this->unbind();
break;
default: $this->help();
}
}
private function fromReply()
{
// ticket
if (preg_match("/[#](.*)/", $this->msg->reply_text, $match)) {
$this->replayTicket($match[1]);
$telegramService->sendMessage($msg->chat_id, $e->getMessage());
}
}
@ -66,135 +57,18 @@ class TelegramController extends Controller
{
if (!isset($data['message'])) return false;
$obj = new \StdClass();
$obj->is_private = $data['message']['chat']['type'] === 'private' ? true : false;
if (!isset($data['message']['text'])) return false;
$text = explode(' ', $data['message']['text']);
$obj->command = $text[0];
$obj->args = array_slice($text, 1);
$obj->chat_id = $data['message']['chat']['id'];
$obj->message_id = $data['message']['message_id'];
$obj->message_type = !isset($data['message']['reply_to_message']['text']) ? 'send' : 'reply';
$obj->message_type = !isset($data['message']['reply_to_message']['text']) ? 'message' : 'reply_message';
$obj->text = $data['message']['text'];
if ($obj->message_type === 'reply') {
$obj->is_private = $data['message']['chat']['type'] === 'private';
if ($obj->message_type === 'reply_message') {
$obj->reply_text = $data['message']['reply_to_message']['text'];
}
return $obj;
}
private function bind()
{
$msg = $this->msg;
if (!$msg->is_private) return;
if (!isset($msg->args[0])) {
abort(500, '参数有误,请携带订阅地址发送');
}
$subscribeUrl = $msg->args[0];
$subscribeUrl = parse_url($subscribeUrl);
parse_str($subscribeUrl['query'], $query);
$token = $query['token'];
if (!$token) {
abort(500, '订阅地址无效');
}
$user = User::where('token', $token)->first();
if (!$user) {
abort(500, '用户不存在');
}
if ($user->telegram_id) {
abort(500, '该账号已经绑定了Telegram账号');
}
$user->telegram_id = $msg->chat_id;
if (!$user->save()) {
abort(500, '设置失败');
}
$telegramService = new TelegramService();
$telegramService->sendMessage($msg->chat_id, '绑定成功');
}
private function unbind()
{
$msg = $this->msg;
if (!$msg->is_private) return;
$user = User::where('telegram_id', $msg->chat_id)->first();
$telegramService = new TelegramService();
if (!$user) {
$this->help();
$telegramService->sendMessage($msg->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
return;
}
$user->telegram_id = NULL;
if (!$user->save()) {
abort(500, '解绑失败');
}
$telegramService->sendMessage($msg->chat_id, '解绑成功', 'markdown');
}
private function help()
{
$msg = $this->msg;
if (!$msg->is_private) return;
$telegramService = new TelegramService();
$commands = [
'/bind 订阅地址 - 绑定你的' . config('v2board.app_name', 'V2Board') . '账号',
'/traffic - 查询流量信息',
'/getlatesturl - 获取最新的' . config('v2board.app_name', 'V2Board') . '网址',
'/unbind - 解除绑定'
];
$text = implode(PHP_EOL, $commands);
$telegramService->sendMessage($msg->chat_id, "你可以使用以下命令进行操作:\n\n$text", 'markdown');
}
private function traffic()
{
$msg = $this->msg;
if (!$msg->is_private) return;
$user = User::where('telegram_id', $msg->chat_id)->first();
$telegramService = new TelegramService();
if (!$user) {
$this->help();
$telegramService->sendMessage($msg->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
return;
}
$transferEnable = Helper::trafficConvert($user->transfer_enable);
$up = Helper::trafficConvert($user->u);
$down = Helper::trafficConvert($user->d);
$remaining = Helper::trafficConvert($user->transfer_enable - ($user->u + $user->d));
$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');
}
private function replayTicket($ticketId)
{
$msg = $this->msg;
if (!$msg->is_private) return;
$user = User::where('telegram_id', $msg->chat_id)->first();
if (!$user) {
abort(500, '用户不存在');
}
$ticketService = new TicketService();
if ($user->is_admin || $user->is_staff) {
$ticketService->replyByAdmin(
$ticketId,
$msg->text,
$user->id
);
}
$telegramService = new TelegramService();
$telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
$telegramService->sendMessageWithAdmin("#`{$ticketId}` 的工单已由 {$user->email} 进行回复", true);
}
}

View File

@ -34,6 +34,7 @@ class DeepbworkController extends Controller
// 后端获取用户
public function user(Request $request)
{
ini_set('memory_limit', -1);
$nodeId = $request->input('node_id');
$server = ServerV2ray::find($nodeId);
if (!$server) {
@ -47,7 +48,7 @@ class DeepbworkController extends Controller
$user->v2ray_user = [
"uuid" => $user->uuid,
"email" => sprintf("%s@v2board.user", $user->uuid),
"alter_id" => $server->alter_id,
"alter_id" => 0,
"level" => 0,
];
unset($user['uuid']);

View File

@ -30,6 +30,7 @@ class ShadowsocksTidalabController extends Controller
// 后端获取用户
public function user(Request $request)
{
ini_set('memory_limit', -1);
$nodeId = $request->input('node_id');
$server = ServerShadowsocks::find($nodeId);
if (!$server) {

View File

@ -34,6 +34,7 @@ class TrojanTidalabController extends Controller
// 后端获取用户
public function user(Request $request)
{
ini_set('memory_limit', -1);
$nodeId = $request->input('node_id');
$server = ServerTrojan::find($nodeId);
if (!$server) {

View File

@ -3,6 +3,7 @@
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Models\CommissionLog;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Order;
@ -27,15 +28,13 @@ class InviteController extends Controller
public function details(Request $request)
{
return response([
'data' => Order::where('invite_user_id', $request->session()->get('id'))
->where('commission_balance', '>', 0)
->whereIn('status', [3, 4])
'data' => CommissionLog::where('invite_user_id', $request->session()->get('id'))
->select([
'id',
'commission_status',
'commission_balance',
'created_at',
'updated_at'
'trade_no',
'order_amount',
'get_amount',
'created_at'
])
->get()
]);

View File

@ -13,7 +13,8 @@ class NoticeController extends Controller
{
$current = $request->input('current') ? $request->input('current') : 1;
$pageSize = 5;
$model = Notice::orderBy('created_at', 'DESC');
$model = Notice::orderBy('created_at', 'DESC')
->where('show', 1);
$total = $model->count();
$res = $model->forPage($current, $pageSize)
->get();

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\User\OrderSave;
use App\Models\CommissionLog;
use App\Models\Payment;
use App\Services\CouponService;
use App\Services\OrderService;
@ -46,7 +47,7 @@ class OrderController extends Controller
]);
}
public function details(Request $request)
public function detail(Request $request)
{
$order = Order::where('user_id', $request->session()->get('id'))
->where('trade_no', $request->input('trade_no'))
@ -59,6 +60,9 @@ class OrderController extends Controller
if (!$order['plan']) {
abort(500, __('Subscription plan does not exist'));
}
if ($order->surplus_order_ids) {
$order['surplus_orders'] = Order::whereIn('id', $order->surplus_order_ids)->get();
}
return response([
'data' => $order
]);

View File

@ -13,6 +13,7 @@ use App\Models\ServerLog;
use App\Models\User;
use App\Utils\Helper;
use Illuminate\Support\Facades\DB;
class ServerController extends Controller
{
@ -29,30 +30,4 @@ class ServerController extends Controller
'data' => $servers
]);
}
public function logFetch(Request $request)
{
$type = $request->input('type') ? $request->input('type') : 0;
$current = $request->input('current') ? $request->input('current') : 1;
$pageSize = $request->input('pageSize') >= 10 ? $request->input('pageSize') : 10;
$serverLogModel = ServerLog::where('user_id', $request->session()->get('id'))
->orderBy('log_at', 'DESC');
switch ($type) {
case 0:
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')));
break;
case 1:
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-d')) - 604800);
break;
case 2:
$serverLogModel->where('log_at', '>=', strtotime(date('Y-m-1')));
}
$total = $serverLogModel->count();
$res = $serverLogModel->forPage($current, $pageSize)
->get();
return response([
'data' => $res,
'total' => $total
]);
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Models\StatUser;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class StatController extends Controller
{
public function getTrafficLog(Request $request)
{
$builder = StatUser::select([
DB::raw('sum(u) as u'),
DB::raw('sum(d) as d'),
'record_at',
'user_id',
'server_rate'
])
->where('user_id', $request->session()->get('id'))
->where('record_at', '>=', strtotime(date('Y-m-1')))
->groupBy('record_at', 'user_id', 'server_rate')
->orderBy('record_at', 'DESC');
return response([
'data' => $builder->get()
]);
}
}

View File

@ -10,11 +10,9 @@ use App\Utils\CacheKey;
use Illuminate\Http\Request;
use App\Models\User;
use App\Models\Plan;
use App\Models\ServerV2ray;
use App\Models\Ticket;
use App\Utils\Helper;
use App\Models\Order;
use App\Models\ServerLog;
use Illuminate\Support\Facades\Cache;
class UserController extends Controller
@ -195,10 +193,14 @@ class UserController extends Controller
$today = date('d');
$lastDay = date('d', strtotime('last day of +0 months'));
if ((int)config('v2board.reset_traffic_method') === 0) {
if ((int)config('v2board.reset_traffic_method') === 0 ||
(isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 0))
{
return $lastDay - $today;
}
if ((int)config('v2board.reset_traffic_method') === 1) {
if ((int)config('v2board.reset_traffic_method') === 1 ||
(isset($user->plan->reset_traffic_method) && $user->plan->reset_traffic_method === 1))
{
if ((int)$day >= (int)$today && (int)$day >= (int)$lastDay) {
return $lastDay - $today;
}

View File

@ -24,7 +24,6 @@ class ServerV2raySave extends FormRequest
'tls' => 'required',
'tags' => 'nullable|array',
'rate' => 'required|numeric',
'alter_id' => 'required|integer',
'network' => 'required|in:tcp,kcp,ws,http,domainsocket,quic,grpc',
'networkSettings' => 'nullable|array',
'ruleSettings' => 'nullable|array',

View File

@ -68,6 +68,7 @@ class AdminRoute
$router->post('/order/assign', 'Admin\\OrderController@assign');
$router->post('/order/paid', 'Admin\\OrderController@paid');
$router->post('/order/cancel', 'Admin\\OrderController@cancel');
$router->post('/order/detail', 'Admin\\OrderController@detail');
// User
$router->get ('/user/fetch', 'Admin\\UserController@fetch');
$router->post('/user/update', 'Admin\\UserController@update');
@ -87,6 +88,7 @@ class AdminRoute
$router->post('/notice/save', 'Admin\\NoticeController@save');
$router->post('/notice/update', 'Admin\\NoticeController@update');
$router->post('/notice/drop', 'Admin\\NoticeController@drop');
$router->post('/notice/show', 'Admin\\NoticeController@show');
// Ticket
$router->get ('/ticket/fetch', 'Admin\\TicketController@fetch');
$router->post('/ticket/reply', 'Admin\\TicketController@reply');
@ -95,6 +97,7 @@ class AdminRoute
$router->get ('/coupon/fetch', 'Admin\\CouponController@fetch');
$router->post('/coupon/generate', 'Admin\\CouponController@generate');
$router->post('/coupon/drop', 'Admin\\CouponController@drop');
$router->post('/coupon/show', 'Admin\\CouponController@show');
// Knowledge
$router->get ('/knowledge/fetch', 'Admin\\KnowledgeController@fetch');
$router->get ('/knowledge/getCategory', 'Admin\\KnowledgeController@getCategory');
@ -108,6 +111,8 @@ class AdminRoute
$router->post('/payment/getPaymentForm', 'Admin\\PaymentController@getPaymentForm');
$router->post('/payment/save', 'Admin\\PaymentController@save');
$router->post('/payment/drop', 'Admin\\PaymentController@drop');
// System
$router->get ('/system/getStatus', 'Admin\\SystemController@getStatus');
});
}
}

View File

@ -18,6 +18,7 @@ class GuestRoute
$router->match(['get', 'post'], '/payment/notify/{method}/{uuid}', 'Guest\\PaymentController@notify');
// Comm
$router->get ('/comm/config', 'Guest\\CommController@config');
$router->get ('/comm/getHitokoto', 'Guest\\CommController@getHitokoto');
});
}
}

View File

@ -25,7 +25,10 @@ class UserRoute
$router->post('/order/save', 'User\\OrderController@save');
$router->post('/order/checkout', 'User\\OrderController@checkout');
$router->get ('/order/check', 'User\\OrderController@check');
$router->get ('/order/details', 'User\\OrderController@details');
// TODO: 1.5.6 remove
$router->get ('/order/details', 'User\\OrderController@detail');
// TODO: 1.5.6 remove
$router->get ('/order/detail', 'User\\OrderController@detail');
$router->get ('/order/fetch', 'User\\OrderController@fetch');
$router->get ('/order/getPaymentMethod', 'User\\OrderController@getPaymentMethod');
$router->post('/order/cancel', 'User\\OrderController@cancel');
@ -45,7 +48,6 @@ class UserRoute
$router->post('/ticket/withdraw', 'User\\TicketController@withdraw');
// Server
$router->get ('/server/fetch', 'User\\ServerController@fetch');
$router->get ('/server/log/fetch', 'User\\ServerController@logFetch');
// Coupon
$router->post('/coupon/check', 'User\\CouponController@check');
// Telegram
@ -56,6 +58,8 @@ class UserRoute
// Knowledge
$router->get ('/knowledge/fetch', 'User\\KnowledgeController@fetch');
$router->get ('/knowledge/getCategory', 'User\\KnowledgeController@getCategory');
// Stat
$router->get ('/stat/getTrafficLog', 'User\\StatController@getTrafficLog');
});
}
}

View File

@ -1,58 +0,0 @@
<?php
namespace App\Jobs;
use App\Services\ServerService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class ServerLogJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $u;
protected $d;
protected $userId;
protected $server;
protected $protocol;
public $tries = 3;
public $timeout = 3;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($u, $d, $userId, $server, $protocol)
{
$this->onQueue('server_log');
$this->u = $u;
$this->d = $d;
$this->userId = $userId;
$this->server = $server;
$this->protocol = $protocol;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$serverService = new ServerService();
if (!$serverService->log(
$this->userId,
$this->server->id,
$this->u,
$this->d,
$this->server->rate,
$this->protocol
)) {
throw new \Exception('日志记录失败');
}
}
}

View File

@ -12,20 +12,28 @@ use Illuminate\Queue\SerializesModels;
class StatServerJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $statistic;
protected $u;
protected $d;
protected $server;
protected $protocol;
protected $recordType;
public $tries = 3;
public $timeout = 5;
public $timeout = 60;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(array $statistic)
public function __construct($u, $d, $server, $protocol, $recordType = 'd')
{
$this->onQueue('stat_server');
$this->statistic = $statistic;
$this->onQueue('stat');
$this->u = $u;
$this->d = $d;
$this->server = $server;
$this->protocol = $protocol;
$this->recordType = $recordType;
}
/**
@ -35,18 +43,33 @@ class StatServerJob implements ShouldQueue
*/
public function handle()
{
$statistic = $this->statistic;
$data = StatServer::where('record_at', $statistic['record_at'])
->where('server_id', $statistic['server_id'])
$recordAt = strtotime(date('Y-m-d'));
if ($this->recordType === 'm') {
//
}
$data = StatServer::where('record_at', $recordAt)
->where('server_id', $this->server->id)
->lockForUpdate()
->first();
if ($data) {
try {
$data->update($statistic);
$data->update([
'u' => $data['u'] + $this->u,
'd' => $data['d'] + $this->d
]);
} catch (\Exception $e) {
abort(500, '节点统计数据更新失败');
}
} else {
if (!StatServer::create($statistic)) {
if (!StatServer::create([
'server_id' => $this->server->id,
'server_type' => $this->protocol,
'u' => $this->u,
'd' => $this->d,
'record_type' => $this->recordType,
'record_at' => $recordAt
])) {
abort(500, '节点统计数据创建失败');
}
}

82
app/Jobs/StatUserJob.php Normal file
View File

@ -0,0 +1,82 @@
<?php
namespace App\Jobs;
use App\Models\StatServer;
use App\Models\StatUser;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class StatUserJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $u;
protected $d;
protected $userId;
protected $server;
protected $protocol;
protected $recordType;
public $tries = 3;
public $timeout = 60;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct($u, $d, $userId, $server, $protocol, $recordType = 'd')
{
$this->onQueue('stat');
$this->u = $u;
$this->d = $d;
$this->userId = $userId;
$this->server = $server;
$this->protocol = $protocol;
$this->recordType = $recordType;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
$recordAt = strtotime(date('Y-m-d'));
if ($this->recordType === 'm') {
//
}
$data = StatUser::where('record_at', $recordAt)
->where('server_id', $this->server->id)
->where('user_id', $this->userId)
->first();
if ($data) {
try {
$data->update([
'u' => $data['u'] + $this->u,
'd' => $data['d'] + $this->d
]);
} catch (\Exception $e) {
abort(500, '用户统计数据更新失败');
}
} else {
if (!StatUser::create([
'user_id' => $this->userId,
'server_id' => $this->server->id,
'server_type' => $this->protocol,
'server_rate' => $this->server->rate,
'u' => $this->u,
'd' => $this->d,
'record_type' => $this->recordType,
'record_at' => $recordAt
])) {
abort(500, '用户统计数据创建失败');
}
}
}
}

16
app/Models/StatUser.php Normal file
View File

@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class StatUser extends Model
{
protected $table = 'v2_stat_user';
protected $dateFormat = 'U';
protected $guarded = ['id'];
protected $casts = [
'created_at' => 'timestamp',
'updated_at' => 'timestamp'
];
}

View File

@ -5,8 +5,6 @@
*/
namespace App\Payments;
use Omnipay\Omnipay;
class AlipayF2F {
public function __construct($config)
{
@ -36,43 +34,36 @@ class AlipayF2F {
public function pay($order)
{
$gateway = Omnipay::create('Alipay_AopF2F');
$gateway->setSignType('RSA2'); //RSA/RSA2
$gateway->setAppId($this->config['app_id']);
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
$gateway->setNotifyUrl($order['notify_url']);
$request = $gateway->purchase();
$request->setBizContent([
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
'out_trade_no' => $order['trade_no'],
'total_amount' => $order['total_amount'] / 100
]);
/** @var \Omnipay\Alipay\Responses\AopTradePreCreateResponse $response */
$response = $request->send();
$result = $response->getAlipayResponse();
if ($result['code'] !== '10000') {
abort(500, $result['sub_msg']);
try {
$gateway = new \Library\AlipayF2F();
$gateway->setMethod('alipay.trade.precreate');
$gateway->setAppId($this->config['app_id']);
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
$gateway->setNotifyUrl($order['notify_url']);
$gateway->setBizContent([
'subject' => config('v2board.app_name', 'V2Board') . ' - 订阅',
'out_trade_no' => $order['trade_no'],
'total_amount' => $order['total_amount'] / 100
]);
$gateway->send();
return [
'type' => 0, // 0:qrcode 1:url
'data' => $gateway->getQrCodeUrl()
];
} catch (\Exception $e) {
abort(500, $e->getMessage());
}
return [
'type' => 0, // 0:qrcode 1:url
'data' => $response->getQrCode()
];
}
public function notify($params)
{
$gateway = Omnipay::create('Alipay_AopF2F');
$gateway->setSignType('RSA2'); //RSA/RSA2
$gateway = new \Library\AlipayF2F();
$gateway->setAppId($this->config['app_id']);
$gateway->setPrivateKey($this->config['private_key']); // 可以是路径,也可以是密钥内容
$gateway->setAlipayPublicKey($this->config['public_key']); // 可以是路径,也可以是密钥内容
$request = $gateway->completePurchase();
$request->setParams($_POST); //Optional
try {
/** @var \Omnipay\Alipay\Responses\AopCompletePurchaseResponse $response */
$response = $request->send();
if ($response->isPaid()) {
if ($gateway->verify($params)) {
/**
* Payment is successful
*/

148
app/Payments/BTCPay.php Normal file
View File

@ -0,0 +1,148 @@
<?php
namespace App\Payments;
class BTCPay {
public function __construct($config) {
$this->config = $config;
}
public function form()
{
return [
'btcpay_url' => [
'label' => 'API接口所在网址(包含最后的斜杠)',
'description' => '',
'type' => 'input',
],
'btcpay_storeId' => [
'label' => 'storeId',
'description' => '',
'type' => 'input',
],
'btcpay_api_key' => [
'label' => 'API KEY',
'description' => '个人设置中的API KEY(非商店设置中的)',
'type' => 'input',
],
'btcpay_webhook_key' => [
'label' => 'WEBHOOK KEY',
'description' => '',
'type' => 'input',
],
];
}
public function pay($order) {
$params = [
'jsonResponse' => true,
'amount' => sprintf('%.2f', $order['total_amount'] / 100),
'currency' => 'CNY',
'metadata' => [
'orderId' => $order['trade_no']
]
];
$params_string = @json_encode($params);
$ret_raw = self::_curlPost($this->config['btcpay_url'] . 'api/v1/stores/' . $this->config['btcpay_storeId'] . '/invoices', $params_string);
$ret = @json_decode($ret_raw, true);
if(empty($ret['checkoutLink'])) {
abort(500, "error!");
}
return [
'type' => 1, // Redirect to url
'data' => $ret['checkoutLink'],
];
}
public function notify($params) {
$payload = trim(file_get_contents('php://input'));
$headers = getallheaders();
//IS Btcpay-Sig
//NOT BTCPay-Sig
//API doc is WRONG!
$headerName = 'Btcpay-Sig';
$signraturHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
$json_param = json_decode($payload, true);
$computedSignature = "sha256=" . \hash_hmac('sha256', $payload, $this->config['btcpay_webhook_key']);
if (!self::hashEqual($signraturHeader, $computedSignature)) {
abort(400, 'HMAC signature does not match');
return false;
}
//get order id store in metadata
$context = stream_context_create(array(
'http' => array(
'method' => 'GET',
'header' => "Authorization:" . "token " . $this->config['btcpay_api_key'] . "\r\n"
)
));
$invoiceDetail = file_get_contents($this->config['btcpay_url'] . 'api/v1/stores/' . $this->config['btcpay_storeId'] . '/invoices/' . $json_param['invoiceId'], false, $context);
$invoiceDetail = json_decode($invoiceDetail, true);
$out_trade_no = $invoiceDetail['metadata']["orderId"];
$pay_trade_no=$json_param['invoiceId'];
return [
'trade_no' => $out_trade_no,
'callback_no' => $pay_trade_no
];
http_response_code(200);
die('success');
}
private function _curlPost($url,$params=false){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt(
$ch, CURLOPT_HTTPHEADER, array('Authorization:' .'token '.$this->config['btcpay_api_key'], 'Content-Type: application/json')
);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* @param string $str1
* @param string $str2
* @return bool
*/
private function hashEqual($str1, $str2)
{
if (function_exists('hash_equals')) {
return \hash_equals($str1, $str2);
}
if (strlen($str1) != strlen($str2)) {
return false;
} else {
$res = $str1 ^ $str2;
$ret = 0;
for ($i = strlen($res) - 1; $i >= 0; $i--) {
$ret |= ord($res[$i]);
}
return !$ret;
}
}
}

View File

@ -0,0 +1,107 @@
<?php
namespace App\Payments;
class CoinPayments {
public function __construct($config) {
$this->config = $config;
}
public function form()
{
return [
'coinpayments_merchant_id' => [
'label' => 'Merchant ID',
'description' => '商户 ID填写您在 Account Settings 中得到的 ID',
'type' => 'input',
],
'coinpayments_ipn_secret' => [
'label' => 'IPN Secret',
'description' => '通知密钥,填写您在 Merchant Settings 中自行设置的值',
'type' => 'input',
],
'coinpayments_currency' => [
'label' => '货币代码',
'description' => '填写您的货币代码(大写),建议与 Merchant Settings 中的值相同',
'type' => 'input',
]
];
}
public function pay($order) {
// IPN notifications are slow, when the transaction is successful, we should return to the user center to avoid user confusion
$parseUrl = parse_url($order['return_url']);
$port = isset($parseUrl['port']) ? ":{$parseUrl['port']}" : '';
$successUrl = "{$parseUrl['scheme']}://{$parseUrl['host']}{$port}";
$params = [
'cmd' => '_pay_simple',
'reset' => 1,
'merchant' => $this->config['coinpayments_merchant_id'],
'item_name' => $order['trade_no'],
'item_number' => $order['trade_no'],
'want_shipping' => 0,
'currency' => $this->config['coinpayments_currency'],
'amountf' => sprintf('%.2f', $order['total_amount'] / 100),
'success_url' => $successUrl,
'cancel_url' => $order['return_url'],
'ipn_url' => $order['notify_url']
];
$params_string = http_build_query($params);
return [
'type' => 1, // Redirect to url
'data' => 'https://www.coinpayments.net/index.php?' . $params_string,
'custom_result' => 'IPN OK'
];
}
public function notify($params) {
if (!isset($params['merchant']) || $params['merchant'] != trim($this->config['coinpayments_merchant_id'])) {
abort(500, 'No or incorrect Merchant ID passed');
}
$headers = getallheaders();
ksort($params);
reset($params);
$request = stripslashes(http_build_query($params));
$headerName = 'Hmac';
$signHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
$hmac = hash_hmac("sha512", $request, trim($this->config['coinpayments_ipn_secret']));
// if (!hash_equals($hmac, $signHeader)) {
// if ($hmac != $_SERVER['HTTP_HMAC']) { <-- Use this if you are running a version of PHP below 5.6.0 without the hash_equals function
// $this->dieSendMessage(400, 'HMAC signature does not match');
// }
if ($hmac != $signHeader) {
abort(400, 'HMAC signature does not match');
}
// HMAC Signature verified at this point, load some variables.
$status = $params['status'];
if ($status >= 100 || $status == 2) {
// payment is complete or queued for nightly payout, success
return [
'trade_no' => $params['item_number'],
'callback_no' => $params['txn_id']
];
} else if ($status < 0) {
//payment error, this is usually final but payments will sometimes be reopened if there was no exchange rate conversion or with seller consent
abort(500, 'Payment Timed Out or Error');
} else {
//payment is pending, you can optionally add a note to the order page
die('IPN OK: pending');
}
}
}

129
app/Payments/Coinbase.php Normal file
View File

@ -0,0 +1,129 @@
<?php
namespace App\Payments;
class Coinbase {
public function __construct($config) {
$this->config = $config;
}
public function form()
{
return [
'coinbase_url' => [
'label' => '接口地址',
'description' => '',
'type' => 'input',
],
'coinbase_api_key' => [
'label' => 'API KEY',
'description' => '',
'type' => 'input',
],
'coinbase_webhook_key' => [
'label' => 'WEBHOOK KEY',
'description' => '',
'type' => 'input',
],
];
}
public function pay($order) {
$params = [
'name' => '订阅套餐',
'description' => '订单号 ' . $order['trade_no'],
'pricing_type' => 'fixed_price',
'local_price' => [
'amount' => sprintf('%.2f', $order['total_amount'] / 100),
'currency' => 'CNY'
],
'metadata' => [
"outTradeNo" => $order['trade_no'],
],
];
$params_string = http_build_query($params);
$ret_raw = self::_curlPost($this->config['coinbase_url'], $params_string);
$ret = @json_decode($ret_raw, true);
if(empty($ret['data']['hosted_url'])) {
abort(500, "error!");
}
return [
'type' => 1,
'data' => $ret['data']['hosted_url'],
];
}
public function notify($params) {
$payload = trim(file_get_contents('php://input'));
$json_param = json_decode($payload, true);
$headerName = 'X-Cc-Webhook-Signature';
$headers = getallheaders();
$signatureHeader = isset($headers[$headerName]) ? $headers[$headerName] : '';
$computedSignature = \hash_hmac('sha256', $payload, $this->config['coinbase_webhook_key']);
if (!self::hashEqual($signatureHeader, $computedSignature)) {
abort(400, 'HMAC signature does not match');
}
$out_trade_no = $json_param['event']['data']['metadata']['outTradeNo'];
$pay_trade_no=$json_param['event']['id'];
return [
'trade_no' => $out_trade_no,
'callback_no' => $pay_trade_no
];
http_response_code(200);
die('success');
}
private function _curlPost($url,$params=false){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 300);
curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
curl_setopt(
$ch, CURLOPT_HTTPHEADER, array('X-CC-Api-Key:' .$this->config['coinbase_api_key'], 'X-CC-Version: 2018-03-22')
);
$result = curl_exec($ch);
curl_close($ch);
return $result;
}
/**
* @param string $str1
* @param string $str2
* @return bool
*/
public function hashEqual($str1, $str2)
{
if (function_exists('hash_equals')) {
return \hash_equals($str1, $str2);
}
if (strlen($str1) != strlen($str2)) {
return false;
} else {
$res = $str1 ^ $str2;
$ret = 0;
for ($i = strlen($res) - 1; $i >= 0; $i--) {
$ret |= ord($res[$i]);
}
return !$ret;
}
}
}

View File

@ -50,6 +50,7 @@ class MGate {
$params['sign'] = md5($str);
$curl = new Curl();
$curl->setUserAgent('MGate');
$curl->setOpt(CURLOPT_SSL_VERIFYPEER, 0);
$curl->post($this->config['mgate_url'] . '/v1/gateway/fetch', http_build_query($params));
$result = $curl->response;
if (!$result) {

View File

@ -40,7 +40,7 @@ class StripeAlipay {
$currency = $this->config['currency'];
$exchange = $this->exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
abort(500, __('Currency conversion has timed out, please try again later'));
}
Stripe::setApiKey($this->config['stripe_sk_live']);
$source = Source::create([
@ -58,7 +58,7 @@ class StripeAlipay {
]
]);
if (!$source['redirect']['url']) {
abort(500, __('user.order.stripeAlipay.gateway_request_failed'));
abort(500, __('Payment gateway request failed'));
}
return [
'type' => 1,

View File

@ -46,7 +46,7 @@ class StripeCredit {
$currency = $this->config['currency'];
$exchange = $this->exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, __('user.order.stripeCard.currency_convert_timeout'));
abort(500, __('Currency conversion has timed out, please try again later'));
}
Stripe::setApiKey($this->config['stripe_sk_live']);
try {
@ -62,10 +62,10 @@ class StripeCredit {
]);
} catch (\Exception $e) {
info($e);
abort(500, __('user.order.stripeCard.was_problem'));
abort(500, __('Payment failed. Please check your credit card information'));
}
if (!$charge->paid) {
abort(500, __('user.order.stripeCard.deduction_failed'));
abort(500, __('Payment failed. Please check your credit card information'));
}
return [
'type' => 2,

View File

@ -40,7 +40,7 @@ class StripeWepay {
$currency = $this->config['currency'];
$exchange = $this->exchange('CNY', strtoupper($currency));
if (!$exchange) {
abort(500, __('user.order.stripeAlipay.currency_convert_timeout'));
abort(500, __('Currency conversion has timed out, please try again later'));
}
Stripe::setApiKey($this->config['stripe_sk_live']);
$source = Source::create([
@ -58,7 +58,7 @@ class StripeWepay {
]
]);
if (!$source['wechat']['qr_code_url']) {
abort(500, __('user.order.stripeWepay.gateway_request_failed'));
abort(500, __('Payment gateway request failed'));
}
return [
'type' => 0,

View File

@ -9,7 +9,6 @@ class WechatPayNative {
public function __construct($config)
{
$this->config = $config;
$this->customResult = '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';
}
public function form()
@ -57,7 +56,8 @@ class WechatPayNative {
}
return [
'type' => 0,
'data' => $response['code_url']
'data' => $response['code_url'],
'custom_result' => '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>'
];
}

View File

@ -0,0 +1,38 @@
<?php
namespace App\Plugins\Telegram\Commands;
use App\Models\User;
use App\Plugins\Telegram\Telegram;
class Bind extends Telegram {
public $command = '/bind';
public $description = '将Telegram账号绑定到网站';
public function handle($message, $match = []) {
if (!$message->is_private) return;
if (!isset($message->args[0])) {
abort(500, '参数有误,请携带订阅地址发送');
}
$subscribeUrl = $message->args[0];
$subscribeUrl = parse_url($subscribeUrl);
parse_str($subscribeUrl['query'], $query);
$token = $query['token'];
if (!$token) {
abort(500, '订阅地址无效');
}
$user = User::where('token', $token)->first();
if (!$user) {
abort(500, '用户不存在');
}
if ($user->telegram_id) {
abort(500, '该账号已经绑定了Telegram账号');
}
$user->telegram_id = $message->chat_id;
if (!$user->save()) {
abort(500, '设置失败');
}
$telegramService = $this->telegramService;
$telegramService->sendMessage($message->chat_id, '绑定成功');
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Plugins\Telegram\Commands;
use App\Models\User;
use App\Plugins\Telegram\Telegram;
class GetLatestUrl extends Telegram {
public $command = '/getlatesturl';
public $description = '将Telegram账号绑定到网站';
public function handle($message, $match = []) {
$telegramService = $this->telegramService;
$text = sprintf(
"%s的最新网址是%s",
config('v2board.app_name', 'V2Board'),
config('v2board.app_url')
);
$telegramService->sendMessage($message->chat_id, $text, 'markdown');
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace App\Plugins\Telegram\Commands;
use App\Models\User;
use App\Plugins\Telegram\Telegram;
use App\Services\TicketService;
class ReplyTicket extends Telegram {
public $regex = '/[#](.*)/';
public $description = '快速工单回复';
public function handle($message, $match = []) {
if (!$message->is_private) return;
$this->replayTicket($message, $match[1]);
}
private function replayTicket($msg, $ticketId)
{
$user = User::where('telegram_id', $msg->chat_id)->first();
if (!$user) {
abort(500, '用户不存在');
}
if (!$msg->text) return;
if (!($user->is_admin || $user->is_staff)) return;
$ticketService = new TicketService();
$ticketService->replyByAdmin(
$ticketId,
$msg->text,
$user->id
);
$telegramService = $this->telegramService;
$telegramService->sendMessage($msg->chat_id, "#`{$ticketId}` 的工单已回复成功", 'markdown');
$telegramService->sendMessageWithAdmin("#`{$ticketId}` 的工单已由 {$user->email} 进行回复", true);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace App\Plugins\Telegram\Commands;
use App\Models\User;
use App\Plugins\Telegram\Telegram;
use App\Utils\Helper;
class Traffic extends Telegram {
public $command = '/traffic';
public $description = '查询流量信息';
public function handle($message, $match = []) {
$telegramService = $this->telegramService;
if (!$message->is_private) return;
$user = User::where('telegram_id', $message->chat_id)->first();
if (!$user) {
$telegramService->sendMessage($message->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
return;
}
$transferEnable = Helper::trafficConvert($user->transfer_enable);
$up = Helper::trafficConvert($user->u);
$down = Helper::trafficConvert($user->d);
$remaining = Helper::trafficConvert($user->transfer_enable - ($user->u + $user->d));
$text = "🚥流量查询\n———————————————\n计划流量:`{$transferEnable}`\n已用上行:`{$up}`\n已用下行:`{$down}`\n剩余流量:`{$remaining}`";
$telegramService->sendMessage($message->chat_id, $text, 'markdown');
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Plugins\Telegram\Commands;
use App\Models\User;
use App\Plugins\Telegram\Telegram;
class UnBind extends Telegram {
public $command = '/unbind';
public $description = '将Telegram账号从网站解绑';
public function handle($message, $match = []) {
if (!$message->is_private) return;
$user = User::where('telegram_id', $message->chat_id)->first();
$telegramService = $this->telegramService;
if (!$user) {
$telegramService->sendMessage($message->chat_id, '没有查询到您的用户信息,请先绑定账号', 'markdown');
return;
}
$user->telegram_id = NULL;
if (!$user->save()) {
abort(500, '解绑失败');
}
$telegramService->sendMessage($message->chat_id, '解绑成功', 'markdown');
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace App\Plugins\Telegram;
use App\Services\TelegramService;
abstract class Telegram {
abstract protected function handle($message, $match);
public $telegramService;
public function __construct()
{
$this->telegramService = new TelegramService();
}
}

View File

@ -81,7 +81,7 @@ class CouponService
public function check()
{
if (!$this->coupon) {
if (!$this->coupon || !$this->coupon->show) {
abort(500, __('Invalid coupon'));
}
if ($this->coupon->limit_use <= 0 && $this->coupon->limit_use !== NULL) {

View File

@ -117,30 +117,29 @@ class OrderService
public function setInvite(User $user):void
{
$order = $this->order;
if ($user->invite_user_id && $order->total_amount > 0) {
$order->invite_user_id = $user->invite_user_id;
$isCommission = false;
switch ((int)$user->commission_type) {
case 0:
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
$isCommission = (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user)));
break;
case 1:
$isCommission = true;
break;
case 2:
$isCommission = !$this->haveValidOrder($user);
break;
}
if ($user->invite_user_id && ($order->total_amount <= 0)) return;
$order->invite_user_id = $user->invite_user_id;
$inviter = User::find($user->invite_user_id);
if (!$inviter) return;
$isCommission = false;
switch ((int)$inviter->commission_type) {
case 0:
$commissionFirstTime = (int)config('v2board.commission_first_time_enable', 1);
$isCommission = (!$commissionFirstTime || ($commissionFirstTime && !$this->haveValidOrder($user)));
break;
case 1:
$isCommission = true;
break;
case 2:
$isCommission = !$this->haveValidOrder($user);
break;
}
if ($isCommission) {
$inviter = User::find($user->invite_user_id);
if ($inviter && $inviter->commission_rate) {
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
} else {
$order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
}
}
if (!$isCommission) return;
if ($inviter && $inviter->commission_rate) {
$order->commission_balance = $order->total_amount * ($inviter->commission_rate / 100);
} else {
$order->commission_balance = $order->total_amount * (config('v2board.invite_commission', 10) / 100);
}
}
@ -163,7 +162,14 @@ class OrderService
private function getSurplusValueByOneTime(User $user, Order $order)
{
$plan = Plan::find($user->plan_id);
$lastOneTimeOrder = Order::where('user_id', $user->id)
->where('period', 'onetime')
->where('status', 3)
->orderBy('id', 'DESC')
->first();
if (!$lastOneTimeOrder) return;
$plan = Plan::find($lastOneTimeOrder->plan_id);
if (!$plan) return;
$trafficUnitPrice = $plan->onetime_price / $plan->transfer_enable;
if ($user->discount && $trafficUnitPrice) {
$trafficUnitPrice = $trafficUnitPrice - ($trafficUnitPrice * $user->discount / 100);

View File

@ -8,7 +8,6 @@ use App\Models\Payment;
class PaymentService
{
public $method;
public $customResult;
protected $class;
protected $config;
protected $payment;
@ -26,9 +25,9 @@ class PaymentService
$this->config['enable'] = $payment['enable'];
$this->config['id'] = $payment['id'];
$this->config['uuid'] = $payment['uuid'];
$this->config['notify_domain'] = $payment['notify_domain'];
};
$this->payment = new $this->class($this->config);
if (isset($this->payment->customResult)) $this->customResult = $this->payment->customResult;
}
public function notify($params)
@ -39,8 +38,15 @@ class PaymentService
public function pay($order)
{
// custom notify domain name
$notifyUrl = url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}");
if ($this->config['notify_domain']) {
$parseUrl = parse_url($notifyUrl);
$notifyUrl = $this->config['notify_domain'] . $parseUrl['path'];
}
return $this->payment->pay([
'notify_url' => url("/api/v1/guest/payment/notify/{$this->method}/{$this->config['uuid']}"),
'notify_url' => $notifyUrl,
'return_url' => config('v2board.app_url', env('APP_URL')) . '/#/order/' . $order['trade_no'],
'trade_no' => $order['trade_no'],
'total_amount' => $order['total_amount'],

View File

@ -8,12 +8,13 @@ use App\Models\User;
use App\Models\ServerV2ray;
use App\Models\ServerTrojan;
use App\Utils\CacheKey;
use App\Utils\Helper;
use Illuminate\Support\Facades\Cache;
class ServerService
{
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":"127.0.0.1","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 = '{"log":{"loglevel":"debug","access":"access.log","error":"error.log"},"api":{"services":["HandlerService","StatsService"],"tag":"api"},"dns":{},"stats":{},"inbounds":[{"port":443,"protocol":"vmess","settings":{"clients":[]},"sniffing":{"enabled":true,"destOverride":["http","tls"]},"streamSettings":{"network":"tcp"},"tag":"proxy"},{"listen":"127.0.0.1","port":23333,"protocol":"dokodemo-door","settings":{"address":"0.0.0.0"},"tag":"api"}],"outbounds":[{"protocol":"freedom","settings":{}},{"protocol":"blackhole","settings":{},"tag":"block"}],"routing":{"rules":[{"type":"field","inboundTag":"api","outboundTag":"api"}]},"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 getV2ray(User $user, $all = false):array
{
@ -26,14 +27,16 @@ class ServerService
for ($i = 0; $i < count($v2ray); $i++) {
$v2ray[$i]['type'] = 'v2ray';
$groupId = $v2ray[$i]['group_id'];
if (in_array($user->group_id, $groupId)) {
if ($v2ray[$i]['parent_id']) {
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
} else {
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['id']));
}
array_push($servers, $v2ray[$i]->toArray());
if (!in_array($user->group_id, $groupId)) continue;
if (strpos($v2ray[$i]['port'], '-') !== false) {
$v2ray[$i]['port'] = Helper::randomPort($v2ray[$i]['port']);
}
if ($v2ray[$i]['parent_id']) {
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['parent_id']));
} else {
$v2ray[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_V2RAY_LAST_CHECK_AT', $v2ray[$i]['id']));
}
array_push($servers, $v2ray[$i]->toArray());
}
@ -51,14 +54,16 @@ class ServerService
for ($i = 0; $i < count($trojan); $i++) {
$trojan[$i]['type'] = 'trojan';
$groupId = $trojan[$i]['group_id'];
if (in_array($user->group_id, $groupId)) {
if ($trojan[$i]['parent_id']) {
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
} else {
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['id']));
}
array_push($servers, $trojan[$i]->toArray());
if (!in_array($user->group_id, $groupId)) continue;
if (strpos($trojan[$i]['port'], '-') !== false) {
$trojan[$i]['port'] = Helper::randomPort($trojan[$i]['port']);
}
if ($trojan[$i]['parent_id']) {
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['parent_id']));
} else {
$trojan[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_TROJAN_LAST_CHECK_AT', $trojan[$i]['id']));
}
array_push($servers, $trojan[$i]->toArray());
}
return $servers;
}
@ -74,15 +79,16 @@ class ServerService
for ($i = 0; $i < count($shadowsocks); $i++) {
$shadowsocks[$i]['type'] = 'shadowsocks';
$groupId = $shadowsocks[$i]['group_id'];
if (in_array($user->group_id, $groupId)) {
if ($shadowsocks[$i]['parent_id']) {
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
} else {
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['id']));
}
array_push($servers, $shadowsocks[$i]->toArray());
if (!in_array($user->group_id, $groupId)) continue;
if (strpos($shadowsocks[$i]['port'], '-') !== false) {
$shadowsocks[$i]['port'] = Helper::randomPort($shadowsocks[$i]['port']);
}
if ($shadowsocks[$i]['parent_id']) {
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['parent_id']));
} else {
$shadowsocks[$i]['last_check_at'] = Cache::get(CacheKey::get('SERVER_SHADOWSOCKS_LAST_CHECK_AT', $shadowsocks[$i]['id']));
}
array_push($servers, $shadowsocks[$i]->toArray());
}
return $servers;
}
@ -129,9 +135,9 @@ class ServerService
}
$json = json_decode(self::V2RAY_CONFIG);
$json->log->loglevel = (int)config('v2board.server_log_enable') ? 'debug' : 'none';
$json->inboundDetour[0]->port = (int)$localPort;
$json->inbound->port = (int)$server->server_port;
$json->inbound->streamSettings->network = $server->network;
$json->inbounds[1]->port = (int)$localPort;
$json->inbounds[0]->port = (int)$server->server_port;
$json->inbounds[0]->streamSettings->network = $server->network;
$this->setDns($server, $json);
$this->setNetwork($server, $json);
$this->setRule($server, $json);
@ -165,7 +171,7 @@ class ServerService
array_push($dns->servers, 'localhost');
}
$json->dns = $dns;
$json->outbound->settings->domainStrategy = 'UseIP';
$json->outbounds[0]->settings->domainStrategy = 'UseIP';
}
}
@ -174,25 +180,25 @@ class ServerService
if ($server->networkSettings) {
switch ($server->network) {
case 'tcp':
$json->inbound->streamSettings->tcpSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->tcpSettings = $server->networkSettings;
break;
case 'kcp':
$json->inbound->streamSettings->kcpSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->kcpSettings = $server->networkSettings;
break;
case 'ws':
$json->inbound->streamSettings->wsSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->wsSettings = $server->networkSettings;
break;
case 'http':
$json->inbound->streamSettings->httpSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->httpSettings = $server->networkSettings;
break;
case 'domainsocket':
$json->inbound->streamSettings->dsSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->dsSettings = $server->networkSettings;
break;
case 'quic':
$json->inbound->streamSettings->quicSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->quicSettings = $server->networkSettings;
break;
case 'grpc':
$json->inbound->streamSettings->grpcSettings = $server->networkSettings;
$json->inbounds[0]->streamSettings->grpcSettings = $server->networkSettings;
break;
}
}
@ -234,7 +240,7 @@ class ServerService
array_push($json->routing->rules, $protocolObj);
}
if (empty($domainRules) && empty($protocolRules)) {
$json->inbound->sniffing->enabled = false;
$json->inbounds[0]->sniffing->enabled = false;
}
}
@ -242,19 +248,19 @@ class ServerService
{
if ((int)$server->tls) {
$tlsSettings = $server->tlsSettings;
$json->inbound->streamSettings->security = 'tls';
$json->inbounds[0]->streamSettings->security = 'tls';
$tls = (object)[
'certificateFile' => '/root/.cert/server.crt',
'keyFile' => '/root/.cert/server.key'
];
$json->inbound->streamSettings->tlsSettings = new \StdClass();
$json->inbounds[0]->streamSettings->tlsSettings = new \StdClass();
if (isset($tlsSettings->serverName)) {
$json->inbound->streamSettings->tlsSettings->serverName = (string)$tlsSettings->serverName;
$json->inbounds[0]->streamSettings->tlsSettings->serverName = (string)$tlsSettings->serverName;
}
if (isset($tlsSettings->allowInsecure)) {
$json->inbound->streamSettings->tlsSettings->allowInsecure = (int)$tlsSettings->allowInsecure ? true : false;
$json->inbounds[0]->streamSettings->tlsSettings->allowInsecure = (int)$tlsSettings->allowInsecure ? true : false;
}
$json->inbound->streamSettings->tlsSettings->certificates[0] = $tls;
$json->inbounds[0]->streamSettings->tlsSettings->certificates[0] = $tls;
}
}

View File

@ -12,15 +12,12 @@ use Illuminate\Support\Facades\DB;
class TicketService {
public function replyByAdmin($ticketId, $message, $userId):void
{
if ($message)
$ticket = Ticket::where('id', $ticketId)
->first();
if (!$ticket) {
abort(500, '工单不存在');
}
if ($ticket->status) {
abort(500, '工单已关闭,无法回复');
}
$ticket->status = 0;
DB::beginTransaction();
$ticketMessage = TicketMessage::create([
'user_id' => $userId,

View File

@ -3,6 +3,8 @@
namespace App\Services;
use App\Jobs\ServerLogJob;
use App\Jobs\StatServerJob;
use App\Jobs\StatUserJob;
use App\Jobs\TrafficFetchJob;
use App\Models\InviteCode;
use App\Models\Order;
@ -85,6 +87,7 @@ class UserService
public function trafficFetch(int $u, int $d, int $userId, object $server, string $protocol)
{
TrafficFetchJob::dispatch($u, $d, $userId, $server, $protocol);
ServerLogJob::dispatch($u, $d, $userId, $server, $protocol);
StatServerJob::dispatch($u, $d, $server, $protocol, 'd');
StatUserJob::dispatch($u, $d, $userId, $server, $protocol, 'd');
}
}

View File

@ -17,7 +17,8 @@ class CacheKey
'SERVER_SHADOWSOCKS_LAST_CHECK_AT' => 'ss节点最后检查时间',
'SERVER_SHADOWSOCKS_LAST_PUSH_AT' => 'ss节点最后推送时间',
'TEMP_TOKEN' => '临时令牌',
'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒'
'LAST_SEND_EMAIL_REMIND_TRAFFIC' => '最后发送流量邮件提醒',
'SCHEDULE_LAST_CHECK_AT' => '计划任务最后检查时间'
];
public static function get(string $key, $uniqueValue)

View File

@ -112,4 +112,9 @@ class Helper
}
return $subscribeUrl;
}
public static function randomPort($range) {
$portRange = explode('-', $range);
return rand($portRange[0], $portRange[1]);
}
}

View File

@ -20,7 +20,6 @@
"laravel/horizon": "^4.3.5",
"laravel/tinker": "^2.5",
"linfo/linfo": "^4.0",
"lokielse/omnipay-alipay": "3.1.2",
"lokielse/omnipay-wechatpay": "^3.0",
"php-curl-class/php-curl-class": "^8.6",
"stripe/stripe-php": "^7.36.1",

View File

@ -237,5 +237,5 @@ return [
| The only modification by laravel config
|
*/
'version' => '1.5.4.1640966110216'
'version' => '1.5.5.1646749167674'
];

View File

@ -173,13 +173,12 @@ return [
'V2board' => [
'connection' => 'redis',
'queue' => [
'order_handle',
'traffic_fetch',
'server_log',
'stat',
'send_email',
'send_email_mass',
'send_telegram',
'stat_server',
'order_handle',
],
'balance' => 'auto',
'minProcesses' => 1,

View File

@ -40,6 +40,7 @@ CREATE TABLE `v2_coupon` (
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`type` tinyint(1) NOT NULL,
`value` int(11) NOT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0',
`limit_use` int(11) DEFAULT NULL,
`limit_use_with_user` int(11) DEFAULT NULL,
`limit_plan_ids` varchar(255) DEFAULT NULL,
@ -98,6 +99,7 @@ CREATE TABLE `v2_notice` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`content` text NOT NULL,
`show` tinyint(1) NOT NULL DEFAULT '0',
`img_url` varchar(255) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
@ -126,6 +128,7 @@ CREATE TABLE `v2_order` (
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待支付1开通中2已取消3已完成4已折抵',
`commission_status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '0待确认1发放中2有效3无效',
`commission_balance` int(11) NOT NULL DEFAULT '0',
`actual_commission_balance` int(11) DEFAULT NULL COMMENT '实际支付佣金',
`paid_at` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
@ -141,6 +144,7 @@ CREATE TABLE `v2_payment` (
`name` varchar(255) NOT NULL,
`icon` varchar(255) DEFAULT NULL,
`config` text NOT NULL,
`notify_domain` varchar(128) DEFAULT NULL,
`enable` tinyint(1) NOT NULL DEFAULT '0',
`sort` int(11) DEFAULT NULL,
`created_at` int(11) NOT NULL,
@ -184,25 +188,6 @@ CREATE TABLE `v2_server_group` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_log`;
CREATE TABLE `v2_server_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`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,
PRIMARY KEY (`id`),
KEY `log_at` (`log_at`),
KEY `user_id` (`user_id`),
KEY `server_id` (`server_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `v2_server_shadowsocks`;
CREATE TABLE `v2_server_shadowsocks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@ -251,13 +236,12 @@ CREATE TABLE `v2_server_v2ray` (
`name` varchar(255) CHARACTER SET utf8mb4 NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`host` varchar(255) NOT NULL,
`port` int(11) NOT NULL,
`port` char(11) NOT NULL,
`server_port` int(11) NOT NULL,
`tls` tinyint(4) NOT NULL DEFAULT '0',
`tags` varchar(255) DEFAULT NULL,
`rate` varchar(11) NOT NULL,
`network` text NOT NULL,
`alter_id` int(11) NOT NULL DEFAULT '1',
`settings` text,
`rules` text,
`networkSettings` text,
@ -306,6 +290,26 @@ CREATE TABLE `v2_stat_server` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='节点数据统计';
DROP TABLE IF EXISTS `v2_stat_user`;
CREATE TABLE `v2_stat_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`server_type` char(11) NOT NULL,
`server_rate` decimal(10,2) NOT NULL,
`u` bigint(20) NOT NULL,
`d` bigint(20) NOT NULL,
`record_type` char(2) NOT NULL,
`record_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `server_id` (`server_id`),
KEY `user_id` (`user_id`),
KEY `record_at` (`record_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
DROP TABLE IF EXISTS `v2_ticket`;
CREATE TABLE `v2_ticket` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@ -343,7 +347,7 @@ CREATE TABLE `v2_user` (
`password_salt` char(10) DEFAULT NULL,
`balance` int(11) NOT NULL DEFAULT '0',
`discount` int(11) DEFAULT NULL,
`commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: cycle 2: onetime',
`commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: period 2: onetime',
`commission_rate` int(11) DEFAULT NULL,
`commission_balance` int(11) NOT NULL DEFAULT '0',
`t` int(11) NOT NULL DEFAULT '0',
@ -370,4 +374,4 @@ CREATE TABLE `v2_user` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- 2021-12-27 17:37:09
-- 2022-03-04 16:25:43

View File

@ -471,3 +471,45 @@ ALTER TABLE `v2_coupon`
ALTER TABLE `v2_order`
CHANGE `cycle` `period` varchar(255) COLLATE 'utf8_general_ci' NOT NULL AFTER `type`;
ALTER TABLE `v2_server_v2ray`
DROP `alter_id`;
ALTER TABLE `v2_user`
CHANGE `commission_type` `commission_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0: system 1: period 2: onetime' AFTER `discount`;
ALTER TABLE `v2_coupon`
ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `value`;
ALTER TABLE `v2_notice`
ADD `show` tinyint(1) NOT NULL DEFAULT '0' AFTER `content`;
ALTER TABLE `v2_order`
ADD `actual_commission_balance` int(11) NULL COMMENT '实际支付佣金' AFTER `commission_balance`;
ALTER TABLE `v2_server_v2ray`
CHANGE `port` `port` char(11) NOT NULL AFTER `host`;
CREATE TABLE `v2_stat_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`server_id` int(11) NOT NULL,
`server_type` char(11) NOT NULL,
`server_rate` decimal(10,2) NOT NULL,
`u` bigint(20) NOT NULL,
`d` bigint(20) NOT NULL,
`record_type` char(2) NOT NULL,
`record_at` int(11) NOT NULL,
`created_at` int(11) NOT NULL,
`updated_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `v2_payment`
ADD `notify_domain` varchar(128) COLLATE 'utf8mb4_general_ci' NULL AFTER `config`;
ALTER TABLE `v2_stat_user`
ADD INDEX `server_id` (`server_id`),
ADD INDEX `user_id` (`user_id`),
ADD INDEX `record_at` (`record_at`);

164
library/AlipayF2F.php Normal file
View File

@ -0,0 +1,164 @@
<?php
namespace Library;
use Illuminate\Support\Facades\Http;
class AlipayF2F {
private $appId;
private $privateKey;
private $alipayPublicKey;
private $signType = 'RSA2';
public $bizContent;
public $method;
public $notifyUrl;
public $response;
public function __construct()
{
}
public function verify($data): bool
{
if (is_string($data)) {
parse_str($data, $data);
}
$sign = $data['sign'];
unset($data['sign']);
unset($data['sign_type']);
ksort($data);
$data = $this->buildQuery($data);
$res = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap($this->alipayPublicKey, 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
if ("RSA2" == $this->signType) {
$result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
} else {
$result = (openssl_verify($data, base64_decode($sign), $res) === 1);
}
openssl_free_key(openssl_get_publickey($res));
return $result;
}
public function setBizContent($bizContent = [])
{
$this->bizContent = json_encode($bizContent);
}
public function setMethod($method)
{
$this->method = $method;
}
public function setAppId($appId)
{
$this->appId = $appId;
}
public function setPrivateKey($privateKey)
{
$this->privateKey = $privateKey;
}
public function setAlipayPublicKey($alipayPublicKey)
{
$this->alipayPublicKey = $alipayPublicKey;
}
public function setNotifyUrl($url)
{
$this->notifyUrl = $url;
}
public function send()
{
$response = Http::get('https://openapi.alipay.com/gateway.do', $this->buildParam())->json();
$resKey = str_replace('.', '_', $this->method) . '_response';
if (!isset($response[$resKey])) throw new \Exception('从支付宝请求失败');
$response = $response[$resKey];
if ($response['msg'] !== 'Success') throw new \Exception($response['sub_msg']);
$this->response = $response;
}
public function getQrCodeUrl()
{
$response = $this->response;
if (!isset($response['qr_code'])) throw new \Exception('获取付款二维码失败');
return $response['qr_code'];
}
public function getResponse()
{
return $this->response;
}
public function buildParam(): array
{
$params = [
'app_id' => $this->appId,
'method' => $this->method,
'charset' => 'UTF-8',
'sign_type' => $this->signType,
'timestamp' => date('Y-m-d H:m:s'),
'biz_content' => $this->bizContent,
'version' => '1.0',
'_input_charset' => 'UTF-8'
];
if ($this->notifyUrl) $params['notify_url'] = $this->notifyUrl;
ksort($params);
$params['sign'] = $this->buildSign($this->buildQuery($params));
return $params;
}
public function buildQuery($query)
{
if (!$query) {
throw new \Exception('参数构造错误');
}
//将要 参数 排序
ksort($query);
//重新组装参数
$params = array();
foreach ($query as $key => $value) {
$params[] = $key . '=' . $value;
}
$data = implode('&', $params);
return $data;
}
private function buildSign(string $signData): string
{
$privateKey = $this->privateKey;
$p_key = array();
//如果私钥是 1行
if (!stripos($privateKey, "\n")) {
$i = 0;
while ($key_str = substr($privateKey, $i * 64, 64)) {
$p_key[] = $key_str;
$i++;
}
}
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . implode("\n", $p_key);
$privateKey = $privateKey . "\n-----END RSA PRIVATE KEY-----";
//私钥
$privateId = openssl_pkey_get_private($privateKey, '');
// 签名
$signature = '';
if ("RSA2" == $this->signType) {
openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA256);
} else {
openssl_sign($signData, $signature, $privateId, OPENSSL_ALGO_SHA1);
}
openssl_free_key($privateId);
//加密后的内容通常含有特殊字符,需要编码转换下
$signature = base64_encode($signature);
return $signature;
}
}

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

File diff suppressed because one or more lines are too long

3
public/theme/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!*v2board
!.gitignore

File diff suppressed because one or more lines are too long

View File

@ -14,5 +14,13 @@ window.settings = {
// 背景
background_url: '',
// crisp
crisp_id: ''
crisp_id: '',
i18n: [
'zh-CN',
'en-US',
'ja-JP',
'vi-VN',
'ko-KR',
'zh-TW'
]
}

View File

@ -1,6 +0,0 @@
window.settings.i18n = [
'zh-CN',
'en-US',
'ja-JP',
'vi-VN'
]

View File

@ -1,286 +1,248 @@
window.settings.i18n['en-US'] = {
'request.error': 'Request failed',
// period_text
'period.month_price': 'Monthly',
'period.quarter_price': 'Quarterly',
'period.half_year_price': 'Semi-Annually',
'period.year_price': 'Annually',
'period.two_year_price': 'Biennially',
'period.three_year_price': 'Triennially',
'period.onetime_price': 'One Time',
'period.reset_price': 'Data Reset Package',
// order_status
'order_status.no_paid': 'Pending Payment',
'order_status.opening': 'Pending Active',
'order_status.cancel': 'Cancelled',
'order_status.done': 'Completed',
'order_status.surplus': 'Converted',
// commission_status
'commission_status.pending_confirm': 'Pending',
'commission_status.confirm': 'Confirming',
'commission_status.done': 'Completed',
'commission_status.reject': 'Invalid',
// header
'header.user_center': 'User Center',
'header.logout': 'Logout',
'header.search': 'Search',
// nav
'nav.dashboard': 'Dashboard',
'nav.subscribe': 'Subscription',
'nav.my_subscribe': 'My Subscription',
'nav.buy_subscribe': 'Purchase Subscription',
'nav.bill': 'Billing',
'nav.my_order': 'My Orders',
'nav.my_invite': 'My Invitation',
'nav.user': 'Account',
'nav.user_center': 'User Center',
'nav.my_ticket': 'My Tickets',
'nav.traffic_detail': 'Transfer Data Details',
'nav.knowledge': 'Knowledge Base',
// dashboard
'dashboard.not_bind_telegram': 'Not link to Telegram yet',
'dashboard.click_here_bind': 'Please click here to link to Telegram',
'dashboard.announcement': 'Announcements',
'dashboard.override': 'Overview',
'dashboard.pending_order': 'Pending Orders',
'dashboard.pending_ticket': 'Pending Tickets',
'dashboard.my_invite': 'My Invitation',
'dashboard.my_subscribe': 'My Subscription',
'dashboard.add_subscribe': 'Order a subscription',
'dashboard.not_expire': 'The subscription is valid for unlimited time',
'dashboard.expired': 'Expired',
'dashboard.subscribe_info': 'Will expire on {date}, {day} days before expiration, after {reset_day} days reset transfer data',
'dashboard.traffic_info': '{used} Used / Total {total}',
'dashboard.view_subscribe': 'View Subscription',
'dashboard.renew': 'Renew',
// forgetPassword
'forgetPassword.email': 'Email',
'forgetPassword.email_verify_code': 'Email verification code',
'forgetPassword.send': 'Send',
'forgetPassword.password': 'Password',
'forgetPassword.reset_password': 'Reset Password',
'forgetPassword.back_login': 'Back to Login',
// invite
'invite.table_code': 'Invitation Code',
'invite.table_create_time': 'Creation Time',
'invite.table_action': 'Action',
'invite.copy_link': 'Copy Link',
'invite.table_complete_time': 'Complete Time',
'invite.table_commission_balance': 'Commission',
'invite.table_commission_status': 'Commission Status',
'invite.override': 'Invitation Overview',
'invite.register_count': 'Registered users',
'invite.commission_rate': 'Commission rate',
'invite.confirm_commission': 'Pending commission',
'invite.confirm_commission_tip': 'The commission will reach your commission account after review.',
'invite.invite_code_manage': 'Invitation Code Management',
'invite.generate_code': 'Generate invitation code',
'invite.detail': 'Invitation Details',
'invite.copy_success': 'Copied successfully',
// login
'login.email': 'Email',
'login.password': 'Password',
'login': 'Login',
'login.register': 'Register',
'login.forget_password': 'Forgot password',
// order
'order.table_no': 'Order Number #',
'order.table_period': 'Type / period',
'order.table_amount': 'Order Amount',
'order.table_status': 'Order Status',
'order.table_create_time': 'Creation Time',
'order.table_action': 'Action',
'order.view_detail': 'View Details',
'order.cancel': 'Cancel',
'order.order_manage': 'Order Management',
'order.please_select_pay_method': 'Please select a payment method',
'order.please_check_credit_card': 'Please check credit card payment information',
'order.order_detail': 'Order Details',
'order.product': 'Product',
'order.type_period': 'Type / period',
'order.amount': 'Amount',
'order.traffic': 'Transfer Data',
'order.discount': 'Discount',
'order.surplus': 'Converted',
'order.refund': 'Refund',
'order.balance_pay': 'Pay with Balance',
'order.pay_method': 'Payment Method',
'order.enter_credit_info': 'Fill in credit card payment information',
'order.credit_alert': 'We will not collect your credit card information, credit card number and other details only use to verify the current transaction.',
'order.total_amount': 'Order Total',
'order.total': 'Total',
'order.checkout': 'Checkout',
'order.waiting_payment': 'Waiting for payment',
'order.result_opening_title': 'Opening',
'order.result_opening_sub_title': 'The order system is processing, please wait 1-3 minutes.',
'order.result_cancel_title': 'Cancelled',
'order.result_cancel_sub_title': 'The order has been cancelled due to overtime payment.',
'order.result_success_title': 'Success',
'order.result_success_sub_title': 'The order has been paid and opened.',
// plan
'plan.select_subscribe': 'Select a Subscription',
'plan.buy': 'Subscribe now',
'plan.setting_subscribe': 'Configure Subscription',
'plan.discount': 'Discount',
'plan.period': 'Payment period',
'plan.have_coupon': 'Have coupons?',
'plan.coupon_verify': 'Verify',
'plan.order_total_amount': 'Order Total',
'plan.place_order': 'Order',
'plan.total': 'Total',
'plan.subscribe_change_title': 'Attention subscription changes',
'plan.subscribe_change_content': 'Attention please, changing subscription will overwrite your current subscription.',
'plan.subscribe_not_renew': 'This subscription cannot be renewed',
'plan.choose_another_subscribe': 'Choose another subscription',
// profile
'profile.user_center': 'User Center',
'profile.my_wallet': 'My Wallet',
'profile.balance_tip': 'Account Balance (For billing only)',
'profile.commission_balance_tip': 'Invitation Commission (Can be used to withdraw)',
'profile.wallet_component': 'Wallet Details',
'profile.transfer': 'Transfer',
'profile.commission_withdraw': 'Invitation Commission Withdrawal',
'profile.change_password': 'Change Password',
'profile.save': 'Save',
'profile.old_password': 'Old Password',
'profile.new_password': 'New Password',
'profile.re_new_password': 'New Password',
'profile.please_enter_old_password': 'Please enter the old password',
'profile.please_enter_new_password': 'Please enter the new password',
'profile.please_enter_re_new_password': 'Please enter the new password again',
'profile.notice': 'Notification',
'profile.expire_remind': 'Subscription expiration email reminder',
'profile.traffic_remind': 'Insufficient transfer data email alert',
'profile.bind_telegram': 'Link to Telegram',
'profile.start': 'Start Now',
'profile.reset_subscribe': 'Reset Subscription',
'profile.reset': 'Reset',
'profile.reset_info_notify_title': 'Do you want to reset subscription?',
'profile.reset_info_notify_content': 'In case of account information or your subscription leak this option is used to reset your UUID and subscription will be changed after reset, you need to re-subscribe.',
'profile.reset_info_notify_success': 'Reset successfully',
'profile.two_password_error': 'Two new passwords entered do not match',
// reg
'register.two_password_error': 'The passwords entered do not match',
'register.email': 'Email',
'register.verify_code': 'Email verification code',
'register.password': 'Password',
'register.re_password': 'Password',
'register.send': 'Send',
'register.invite_code_require': 'Invitation code',
'register.invite_code': 'Invitation code (Optional)',
'register': 'Register',
'register.back_login': 'Back to Login',
'register.tos_url': 'I have read and agree to the <a target="_blank" href="{url}">terms of service</a>',
'register.please_agree_tos': 'Please agree to the terms of service',
// subscribe
'subscribe.table_name': 'Name',
'subscribe.table_tag': 'Tags',
'subscribe.table_action': 'Action',
'subscribe.table_status': 'Status',
'subscribe.table_status_tip': 'Access Point online status in the last 5 minutes',
'subscribe.table_rate': 'Rate',
'subscribe.table_rate_tip': 'The transfer data used is multiplied by the transfer data rate deducted',
'subscribe.more_action': 'Action',
'subscribe.copy_success': 'Copied successfully',
'subscribe.copy_link': 'Copy Link',
'subscribe.my_subscribe': 'My Subscription',
'subscribe.add_subscribe': 'Order a Subscription',
'subscribe.not_expire': 'The subscription is valid for unlimited time',
'subscribe.expired': 'Expired',
'subscribe.subscribe_info': 'Will expire on {date}, {day} days before expiration, after {reset_day} days reset transfer data',
'subscribe.traffic_info': '{used} Used / Total {total}',
'subscribe.renew': 'Renew',
'subscribe.action': 'Action',
'subscribe.reset_traffic': 'Reset Current Transfer Data',
'subscribe.reset_info': 'Reset Subscription',
'subscribe.node_status': 'Access Point Status',
'subscribe.no_node': 'No access points available, if you have not subscribed a subscription or the subscription has been expired, please',
'subscribe.no_node_renew': 'Renew',
'subscribe.no_node_go': 'Subscribe',
'subscribe.reset_traffic_notify_title': 'Are you sure to reset the current month\'s transfer data?',
'subscribe.reset_traffic_notify_content': 'Click "Confirm" and you will be redirected to the payment page. After the order is paid, the system will clear your used data for the month.',
'subscribe.reset_traffic_notify_confirm': 'Confirm',
'subscribe.reset_traffic_notify_cancel': 'Cancel',
'subscribe.reset_info_notify_title': 'Are you sure to reset subscription?',
'subscribe.reset_info_notify_content': 'In case of account information or your subscription leak this option is used to reset your UUID and subscription will be changed after reset, you need to re-subscribe.',
'subscribe.reset_info_notify_success': 'Reset successfully',
// ticket
'ticket.low': 'Low',
'ticket.middle': 'Medium',
'ticket.high': 'High',
'ticket.table_subject': 'Subject',
'ticket.table_level': 'Ticket Priority',
'ticket.table_status': 'Ticket Status',
'ticket.table_create_time': 'Creation Time',
'ticket.table_last_reply': 'Last Reply',
'ticket.table_action': 'Action',
'ticket.status_closed': 'Closed',
'ticket.status_pending': 'Pending Reply',
'ticket.status_reply': 'Replied',
'ticket.action_view': 'View',
'ticket.action_close': 'Close',
'ticket.my_ticket': 'My Tickets',
'ticket.new_ticket': 'New Ticket',
'ticket.new_ticket_modal_title': 'New Ticket',
'ticket.new_ticket_modal_confirm': 'Confirm',
'ticket.new_ticket.modal_cancel': 'Cancel',
'ticket.subject': 'Subject',
'ticket.please_enter_subject': 'Please enter a subject',
'ticket.level': 'Ticket Priority',
'ticket.please_select_level': 'Please select the ticket priority',
'ticket.message': 'Message',
'ticket.please_enter_issue': 'Please describe the problem you encountered',
// traffic
'traffic.table_record_time': 'Record Time',
'traffic.table_tx': 'Actual Upload',
'traffic.table_rx': 'Actual Download',
'traffic.table_rate': 'Deduction Rate',
'traffic.table_total': 'Total',
'traffic.table_total_tip': 'Formula: (Actual Upload + Actual Download) x Deduction Rate = Deduct Transfer Data',
'traffic.traffic_detail': 'Transfer Data Details',
'traffic.today': 'Today',
'traffic.month': 'Month',
'traffic.week': 'Week',
// one_click_subscribe
'one_click_subscribe.copy_success': 'Copied successfully',
'one_click_subscribe.copy_subscribe_url': 'Copy Subscription URL',
'one_click_subscribe.import': 'Export to',
'one_click_subscribe': 'Quick Subscription',
'one_click_subscribe.copy_subscribe': 'Copy Subscription URL',
// transfer_modal
'transfer_modal.title': 'Transfer Invitation Commission to Account Balance',
'transfer_modal.confirm': 'Confirm',
'transfer_modal.cancel': 'Cancel',
'transfer_modal.alert': 'The transferred balance will only be used for {title} payment',
'transfer_modal.commission_balance': 'Current invitation balance',
'transfer_modal.transfer_balance': 'Transfer amount',
'transfer_modal.please_enter_transfer_balance': 'Please enter the amount to be transferred to the balance',
// chat
'chat.please_enter_message': 'Please enter to reply to the ticket...',
// withdraw_modal
'withdraw_modal.title': 'Apply For Withdrawal',
'withdraw_modal.confirm': 'Confirm',
'withdraw_modal.cancel': 'Cancel',
'withdraw_modal.withdraw_method': 'Withdrawal Method',
'withdraw_modal.please_select_method': 'Please select a withdrawal method',
'withdraw_modal.withdraw_account': 'Withdrawal Account',
'withdraw_modal.please_enter_account': 'Please enter the withdrawal account',
// bind_telegram
'bind_telegram_modal.confirm': 'I got it',
'bind_telegram_modal.title': 'Link to Telegram',
'bind_telegram_modal.step_1': 'First Step',
'bind_telegram_modal.step_2': 'Second Step',
'bind_telegram_modal.open_telegram_search': 'Open Telegram and Search ',
'bind_telegram_modal.send_bot_message': 'Send the following command to bot',
// knowledge
'knowledge': 'Knowledge Base',
'knowledge.last_release_date': 'Last Updated: {date}',
'knowledge.copy_success' : 'Copy succeeded',
'Reset flow after {reset_day} day': 'Reset flow after {reset_day} day',
'Due on {date}, {day} days before expiration': 'Due on {date}, {day} days before expiration. ',
'Telegram discuss': 'Telegram discuss',
'Join now': 'Join now',
'Renew': 'Renew',
'Buy': 'Buy',
'This subscription cannot be renewed. Only new users are allowed to purchase it': 'This subscription cannot be renewed. Only new users are allowed to purchase it'
}
'请求失败': 'Request failed',
'月付': 'Monthly',
'季付': 'Quarterly',
'半年付': 'Semi-Annually',
'年付': 'Annually',
'两年付': 'Biennially',
'三年付': 'Triennially',
'一次性': 'One Time',
'重置流量包': 'Data Reset Package',
'待支付': 'Pending Payment',
'开通中': 'Pending Active',
'已取消': 'Cancelled',
'已完成': 'Completed',
'已折抵': 'Converted',
'待确认': 'Pending',
'发放中': 'Confirming',
'已发放': 'Completed',
'无效': 'Invalid',
'个人中心': 'User Center',
'登出': 'Logout',
'搜索': 'Search',
'仪表盘': 'Dashboard',
'订阅': 'Subscription',
'我的订阅': 'My Subscription',
'购买订阅': 'Purchase Subscription',
'财务': 'Billing',
'我的订单': 'My Orders',
'我的邀请': 'My Invitation',
'用户': 'Account',
'我的工单': 'My Tickets',
'流量明细': 'Transfer Data Details',
'使用文档': 'Knowledge Base',
'绑定Telegram获取更多服务': 'Not link to Telegram yet',
'点击这里进行绑定': 'Please click here to link to Telegram',
'公告': 'Announcements',
'总览': 'Overview',
'该订阅长期有效': 'The subscription is valid for an unlimited time',
'已过期': 'Expired',
'已用 {used} / 总计 {total}': '{used} Used / Total {total}',
'查看订阅': 'View Subscription',
'邮箱': 'Email',
'邮箱验证码': 'Email verification code',
'发送': 'Send',
'重置密码': 'Reset Password',
'返回登入': 'Back to Login',
'邀请码': 'Invitation Code',
'复制链接': 'Copy Link',
'完成时间': 'Complete Time',
'佣金': 'Commission',
'已注册用户数': 'Registered users',
'佣金比例': 'Commission rate',
'确认中的佣金': 'Pending commission',
'佣金将会在确认后会到达你的佣金账户。': 'The commission will reach your commission account after review.',
'邀请码管理': 'Invitation Code Management',
'生成邀请码': 'Generate invitation code',
'邀请明细': 'Invitation Details',
'复制成功': 'Copied successfully',
'密码': 'Password',
'登入': 'Login',
'注册': 'Register',
'忘记密码': 'Forgot password',
'# 订单号': 'Order Number #',
'周期': 'Type / Cycle',
'订单金额': 'Order Amount',
'订单状态': 'Order Status',
'创建时间': 'Creation Time',
'操作': 'Action',
'查看详情': 'View Details',
'请选择支付方式': 'Please select a payment method',
'请检查信用卡支付信息': 'Please check credit card payment information',
'订单详情': 'Order Details',
'折扣': 'Discount',
'折抵': 'Converted',
'退款': 'Refund',
'支付方式': 'Payment Method',
'填写信用卡支付信息': 'Please fill in credit card payment information',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': 'We will not collect your credit card information, credit card number and other details only use to verify the current transaction.',
'订单总额': 'Order Total',
'总计': 'Total',
'结账': 'Checkout',
'等待支付中': 'Waiting for payment',
'开通中': 'Pending',
'订单系统正在进行处理请稍等1-3分钟。': 'Order system is being processed, please wait 1 to 3 minutes.',
'已取消': 'Cancelled',
'订单由于超时支付已被取消。': 'The order has been cancelled due to overtime payment.',
'已完成': 'Success',
'订单已支付并开通。': 'The order has been paid and the service is activated.',
'选择订阅': 'Select a Subscription',
'立即订阅': 'Subscribe now',
'配置订阅': 'Configure Subscription',
'折扣': 'Discount',
'付款周期': 'Payment Cycle',
'有优惠券?': 'Have coupons?',
'验证': 'Verify',
'订单总额': 'Order Total',
'下单': 'Order',
'总计': 'Total',
'订阅变更须知': 'Attention subscription changes',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': 'Attention please, change subscription will overwrite your current subscription.',
'该订阅无法续费': 'This subscription cannot be renewed',
'选择其他订阅': 'Choose another subscription',
'我的钱包': 'My Wallet',
'账户余额(仅消费)': 'Account Balance (For billing only)',
'推广佣金(可提现)': 'Invitation Commission (Can be used to withdraw)',
'钱包组成部分': 'Wallet Details',
'划转': 'Transfer',
'推广佣金提现': 'Invitation Commission Withdrawal',
'修改密码': 'Change Password',
'保存': 'Save',
'旧密码': 'Old Password',
'新密码': 'New Password',
'请输入旧密码': 'Please enter the old password',
'请输入新密码': 'Please enter the new password',
'通知': 'Notification',
'到期邮件提醒': 'Subscription expiration email reminder',
'流量邮件提醒': 'Insufficient transfer data email alert',
'绑定Telegram': 'Link to Telegram',
'立即开始': 'Start Now',
'重置订阅信息': 'Reset Subscription',
'重置': 'Reset',
'确定要重置订阅信息?': 'Do you want to reset subscription?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'In case of your account information or subscription leak, this option is for reset. After resetting your UUID and subscription will change, you need to re-subscribe.',
'重置成功': 'Reset successfully',
'两次新密码输入不同': 'Two new passwords entered do not match',
'两次密码输入不同': 'The passwords entered do not match',
'邮箱': 'Email',
'邮箱验证码': 'Email verification code',
'发送': 'Send',
'邀请码': 'Invitation code',
'邀请码(选填)': 'Invitation code (Optional)',
'注册': 'Register',
'返回登入': 'Back to Login',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': 'I have read and agree to the <a target="_blank" href="{url}">terms of service</a>',
'请同意服务条款': 'Please agree to the terms of service',
'名称': 'Name',
'标签': 'Tags',
'状态': 'Status',
'节点五分钟内节点在线情况': 'Access Point online status in the last 5 minutes',
'倍率': 'Rate',
'使用的流量将乘以倍率进行扣除': 'The transfer data usage will be multiplied by the transfer data rate deducted.',
'更多操作': 'Action',
'复制成功': 'Copied successfully',
'复制链接': 'Copy Link',
'该订阅长期有效': 'The subscription is valid for an unlimited time',
'已过期': 'Expired',
'已用 {used} / 总计 {total}': '{used} Used / Total {total}',
'重置订阅信息': 'Reset Subscription',
'没有可用节点,如果您未订阅或已过期请': 'No access points are available. If you have not subscribed or the subscription has expired, please',
'订阅': 'Subscribe',
'确定要重置当月流量?': 'Are you sure to reset your usage for the current month?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': 'Click "Confirm" and you will be redirected to the payment page. The system will empty your current month\'s usage after your purchase.',
'确定': 'Confirm',
'确定要重置订阅信息?': 'Are you sure to reset your subscription?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'In case of your account information or subscription leak, this option is for reset. After resetting your UUID and subscription will change, you need to re-subscribe.',
'重置成功': 'Reset successfully',
'低': 'Low',
'中': 'Medium',
'高': 'High',
'主题': 'Subject',
'工单级别': 'Ticket Priority',
'工单状态': 'Ticket Status',
'最后回复': 'Last Reply',
'已关闭': 'Closed',
'待回复': 'Pending Reply',
'已回复': 'Replied',
'查看': 'View',
'关闭': 'Cancel',
'新的工单': 'My Tickets',
'新的工单': 'New Ticket',
'确认': 'Confirm',
'主题': 'Subject',
'请输入工单主题': 'Please enter a subject',
'工单等级': 'Ticket Priority',
'请选择工单等级': 'Please select the ticket priority',
'消息': 'Message',
'请描述你遇到的问题': 'Please describe the problem you encountered',
'记录时间': 'Record Time',
'实际上行': 'Actual Upload',
'实际下行': 'Actual Download',
'合计': 'Total',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': 'Formula: (Actual Upload + Actual Download) x Deduction Rate = Deduct Transfer Data',
'复制成功': 'Copied successfully',
'复制订阅地址': 'Copy Subscription URL',
'导入到': 'Export to',
'一键订阅': 'Quick Subscription',
'复制订阅': 'Copy Subscription URL',
'推广佣金划转至余额': 'Transfer Invitation Commission to Account Balance',
'确认': 'Confirm',
'划转后的余额仅用于{title}消费使用': 'The transferred balance will be used for {title} payments only',
'当前推广佣金余额': 'Current invitation balance',
'划转金额': 'Transfer amount',
'请输入需要划转到余额的金额': 'Please enter the amount to be transferred to the balance',
'输入内容回复工单...': 'Please enter to reply to the ticket...',
'申请提现': 'Apply For Withdrawal',
'确认': 'Confirm',
'取消': 'Cancel',
'提现方式': 'Withdrawal Method',
'请选择提现方式': 'Please select a withdrawal method',
'提现账号': 'Withdrawal Account',
'请输入提现账号': 'Please enter the withdrawal account',
'我知道了': 'I got it',
'绑定Telegram': 'Link to Telegram',
'第一步': 'First Step',
'第二步': 'Second Step',
'打开Telegram搜索': 'Open Telegram and Search ',
'向机器人发送你的': 'Send the following command to bot',
'使用文档': 'Knowledge Base',
'最后更新: {date}': 'Last Updated: {date}',
'复制成功': 'Copied successfully',
'我的订阅': 'My Subscription',
'还有没支付的订单': 'There are still unpaid orders',
'立即支付': 'Pay Now',
'条工单正在处理中': 'tickets are in process',
'立即查看': 'View Now',
'购买订阅': 'Purchase Subscription',
'使用文档': 'Knowledge Base',
'我的订单': 'My Orders',
'流量明细': 'Transfer Data Details',
'配置订阅': 'Configure Subscription',
'我的邀请': 'My Invitation',
'节点状态': 'Access Point Status',
'复制成功': 'Copied successfully',
'商品信息': 'Product Information',
'产品名称': 'Product Name',
'类型/周期': 'Type / Cycle',
'产品流量': 'Product Transfer Data',
'订单信息': 'Order Details',
'关闭订单': 'Close order',
'订单号': 'Order Number',
'优惠金额': 'Discount amount',
'旧订阅折抵金额': 'Old subscription converted amount',
'退款金额': 'Refunded amount',
'余额支付': 'Balance payment',
'我的工单': 'My Tickets',
'工单历史': 'Ticket History',
'{reset_day} 日后重置流量': 'after {reset_day} days reset usage',
'节点名称': 'Access Point Name',
'于 {date} 到期,距离到期还有 {day} 天。': 'Will expire on {date}, {day} days before expiration, ',
'Telegram 讨论组': 'Telegram Discussion Group',
'立即加入': 'Join Now',
'续费': 'Renewal',
'购买': 'Purchase',
'该订阅无法续费,仅允许新用户购买': 'This subscription cannot be renewed and is only available to new users.',
'重置当月流量': 'Reset current month usage',
'流量明细仅保留近月数据以供查询。': 'Only keep the most recent month\'s usage for checking the transfer data details.',
'扣费倍率': 'Fee deduction rate'
};

View File

@ -1,316 +1,248 @@
window.settings.i18n['ja-JP'] = {
'request.error': 'リクエストエラー',
// period_text
'period.month_price': '月払い',
'period.quarter_price': '四半期払い',
'period.half_year_price': '半年払い',
'period.year_price': '年払い',
'period.two_year_price': '2年払い',
'period.three_year_price': '3年払い',
'period.onetime_price': '一括払い',
'period.reset_price': 'データ通信量のカウントをリセット',
// order_status
'order_status.no_paid': 'お支払い待ち',
'order_status.opening': '処理中',
'order_status.cancel': '取り消し済み',
'order_status.done': '済み',
'order_status.surplus': '控除済み',
// commission_status
'commission_status.pending_confirm': '承認待ち',
'commission_status.confirm': '承認済み',
'commission_status.done': '済み',
'commission_status.reject': '無効',
// header
'header.user_center': '会員メニュー',
'header.logout': 'ログアウト',
'header.search': '検索',
// nav
'nav.dashboard': 'ダッシュボード',
'nav.subscribe': '定期購入',
'nav.my_subscribe': 'ご利用中の定期購入',
'nav.buy_subscribe': '定期購入のご注文',
'nav.bill': 'マイファイナンス',
'nav.my_order': '注文履歴',
'nav.my_invite': '招待メニュー',
'nav.user': 'ユーザー',
'nav.user_center': '会員メニュー',
'nav.my_ticket': 'お問い合わせ',
'nav.traffic_detail': 'データ通信明細',
'nav.knowledge': '知識ベース',
// dashboard
'dashboard.not_bind_telegram': 'Telegramは関連付けられていません',
'dashboard.click_here_bind': '関連付けを開始するにはこちらをクリックしてください',
'dashboard.announcement': 'お知らせ',
'dashboard.override': '概要',
'dashboard.pending_order': 'お支払い待ち',
'dashboard.pending_ticket': '対応中のお問い合わせ',
'dashboard.my_invite': '招待済み',
'dashboard.my_subscribe': 'ご利用中の定期購入',
'dashboard.add_subscribe': '定期購入の追加購入',
'dashboard.not_expire': '無期限',
'dashboard.expired': '期限切れ',
'dashboard.subscribe_info': 'ご利用期限 {date} まで,期限まであと {day} 日。',
'dashboard.traffic_info': '使用済み {used} / 合計 {total}',
'dashboard.view_subscribe': 'サーバーステータス',
'dashboard.renew': '継続料金のお支払い',
// forgetPassword
'forgetPassword.email': 'Eメールアドレス',
'forgetPassword.email_verify_code': '承認コード',
'forgetPassword.send': '送信',
'forgetPassword.password': 'パスワード',
'forgetPassword.reset_password': 'パスワードの変更',
'forgetPassword.back_login': 'ログインメニューへ',
// invite
'invite.table_code': '招待コード',
'invite.table_create_time': '作成日付',
'invite.table_action': 'アクション',
'invite.copy_link': 'リンクをコピー',
'invite.table_complete_time': '完了日付',
'invite.table_commission_balance': 'コミッション',
'invite.table_commission_status': 'コミッションステータス',
'invite.override': '招待の概要',
'invite.register_count': '登録済みユーザー数',
'invite.commission_rate': 'コミッションレート',
'invite.confirm_commission': '承認待ちのコミッション',
'invite.confirm_commission_tip': '承認済み後にコミッションアカウントに入金されます',
'invite.invite_code_manage': '招待コードの管理',
'invite.generate_code': '招待コードの作成',
'invite.detail': '招待済み明細',
'invite.copy_success': 'クリップボードにコピーされました',
// login
'login.email': 'Eメールアドレス',
'login.password': 'パスワード',
'login': 'ログイン',
'login.register': '新規登録',
'login.forget_password': 'パスワードをお忘れの方は[こちら]',
// order
'order.table_no': '# オーダー番号',
'order.table_period': 'タイプ/お支払い周期',
'order.table_amount': 'ご注文の金額',
'order.table_status': 'ステータス',
'order.table_create_time': '作成日付',
'order.table_action': 'アクション',
'order.view_detail': '詳細',
'order.cancel': 'キャンセル',
'order.order_manage': 'ご注文の一覧',
'order.please_select_pay_method': 'お支払い方法をお選びください',
'order.please_check_credit_card': 'クレジットカードのお支払い情報をご確認ください',
'order.order_detail': '最終明細のご確認',
'order.product': 'お選びの定期購入',
'order.type_period': 'タイプ/お支払い周期',
'order.amount': '金額',
'order.traffic': 'データ通信量',
'order.discount': '割引き',
'order.surplus': '控除額',
'order.refund': 'お払戻し',
'order.balance_pay': '残高支払い',
'order.pay_method': 'お支払い方法',
'order.enter_credit_info': 'クレジットカード情報をご入力ください',
'order.credit_alert': 'お客様のカード情報はお支払いにのみ使用され、サーバーに保存されることはございません',
'order.total_amount': '合計金額',
'order.total': '合計',
'order.checkout': 'お会計',
'order.waiting_payment': 'お支払い待ち',
'order.result_opening_title': 'オープニング',
'order.result_opening_sub_title': '注文システムは処理中です。1〜3分お待ちください。',
'order.result_cancel_title': 'キャンセル',
'order.result_cancel_sub_title': '残業のため、注文はキャンセルされました。',
'order.result_success_title': '完了',
'order.result_success_sub_title': '注文は支払われ、開かれました。',
// plan
'plan.select_subscribe': '定期購入を選択',
'plan.buy': '今すぐ購入',
'plan.setting_subscribe': '定期購入の設定',
'plan.discount': '割引き',
'plan.period': 'お支払い周期',
'plan.have_coupon': 'キャンペーンコード',
'plan.coupon_verify': '確定',
'plan.order_total_amount': 'ご注文の合計金額',
'plan.place_order': '注文&最終確認',
'plan.total': '合計',
'plan.subscribe_change_title': '定期購入プランの変更に伴うご注意',
'plan.subscribe_change_content': '定期購入プランを変更されますと、既存のプランが新規プランによって上書きされます、ご注意下さい',
'plan.subscribe_not_renew': 'この商品は契約の更新ができません',
'plan.choose_another_subscribe': '他の商品を選ぶ',
// profile
'profile.user_center': '会員メニュー',
'profile.my_wallet': 'マイウォレット',
'profile.balance_tip': '残高(サービスの購入のみ)',
'profile.commission_balance_tip': '招待によるコミッション(出金可)',
'profile.wallet_component': 'ウォレットの内訳',
'profile.transfer': 'お振替',
'profile.commission_withdraw': 'コミッションのお引き出し',
'profile.change_password': 'パスワードの変更',
'profile.save': '変更を保存',
'profile.old_password': '現在のパスワード',
'profile.new_password': '新しいパスワード',
'profile.re_new_password': '新しいパスワードの確認',
'profile.please_enter_old_password': '現在のパスワードをご入力ください',
'profile.please_enter_new_password': '新しいパスワードをご入力ください',
'profile.please_enter_re_new_password': '新しいパスワードをご入力ください',
'profile.notice': '通知',
'profile.expire_remind': '期限切れ通知',
'profile.traffic_remind': 'データ通信量通知',
'profile.bind_telegram': 'Telegramの関連付け',
'profile.start': '今すぐ開始',
'profile.reset_subscribe': '定期購入URLの変更',
'profile.reset': '変更',
'profile.reset_info_notify_title': 'URLをご変更なされますか',
'profile.reset_info_notify_content': '定期購入のURL及び情報が外部に漏れた場合にご操作ください。操作後はUUIDや定期購入のURLが変更され、再度定期購入の更新を必要になります。',
'profile.reset_info_notify_success': '変更完了',
'profile.two_password_error': 'ご入力されました新しいパスワードが一致しません',
// reg
'register.two_password_error': 'ご入力されました新しいパスワードが一致しません',
'register.email': 'Eメールアドレス',
'register.verify_code': '承認コード',
'register.password': 'パスワード',
'register.re_password': 'パスワード',
'register.send': '送信',
'register.invite_code_require': '招待コード',
'register.invite_code': '招待コード(オプション)',
'register': '新規登録',
'register.back_login': 'ログイン画面へ',
'register.tos_url': '<a target="_blank" href="{url}">サービス条項</a>を読んで同意しました',
'register.please_agree_tos': 'サービス条項に同意してください',
// subscribe
'subscribe.table_name': '名称',
'subscribe.table_tag': '説明',
'subscribe.table_action': 'アクション',
'subscribe.table_status': 'ステータス',
'subscribe.table_status_tip': '5分間のサーバーオンラインステータス',
'subscribe.table_rate': 'レート',
'subscribe.table_rate_tip': '通信量は該当レートに基き計算されます',
'subscribe.more_action': 'その他のアクション',
'subscribe.copy_success': 'クリップボードにコピーされました',
'subscribe.copy_link': 'リンクをコピー',
'subscribe.my_subscribe': 'ご利用中の定期購入',
'subscribe.add_subscribe': '定期購入の追加',
'subscribe.not_expire': '無期限',
'subscribe.expired': '期限切れ',
'subscribe.subscribe_info': 'ご利用期限 {date} まで,期限まであと {day} 日',
'subscribe.traffic_info': '使用済み {used} / 合計 {total}',
'subscribe.renew': '継続料金のお支払い',
'subscribe.action': 'アクション',
'subscribe.reset_traffic': 'データ通信量のカウントをリセット',
'subscribe.reset_info': '定期購入URLの変更',
'subscribe.node_status': 'サーバーステータス',
'subscribe.no_node': 'ご利用可能なサーバーがありません,定期購入期限切れまたは購入なされていない場合は',
'subscribe.no_node_renew': '継続料金のお支払い',
'subscribe.no_node_go': '購入',
'subscribe.reset_traffic_notify_title': '当月分のデータ通信量のカウントをリセットしますか?',
'subscribe.reset_traffic_notify_content': '「確定」をクリックしお会計画面へ,お支払い後に当月分のデータ通信量は即時リセットされます',
'subscribe.reset_traffic_notify_confirm': '確定',
'subscribe.reset_traffic_notify_cancel': 'キャンセル',
'subscribe.reset_info_notify_title': 'URLやUUIDをご変更なされますか',
'subscribe.reset_info_notify_content': '定期購入のURL及び情報が外部に漏れた場合にご操作ください。操作後はUUIDや定期購入のURLが変更され、再度定期購入の更新を必要になります。',
'subscribe.reset_info_notify_success': 'リセットは完了しました',
// ticket
'ticket.low': 'Low',
'ticket.middle': 'Middle',
'ticket.high': 'High',
'ticket.table_subject': 'タイトル',
'ticket.table_level': 'お問い合わせ優先度',
'ticket.table_status': 'お問い合わせ状況',
'ticket.table_create_time': '作成日付',
'ticket.table_last_reply': '最終回答日付',
'ticket.table_action': 'アクション',
'ticket.status_closed': '終了',
'ticket.status_pending': '対応待ち',
'ticket.status_reply': '回答済み',
'ticket.action_view': '閲覧',
'ticket.action_close': '終了',
'ticket.my_ticket': 'お問い合わせの一覧',
'ticket.new_ticket': '新規お問い合わせ',
'ticket.new_ticket_modal_title': '新規お問い合わせ',
'ticket.new_ticket_modal_confirm': '確定',
'ticket.new_ticket.modal_cancel': 'キャンセル',
'ticket.subject': 'タイトル',
'ticket.please_enter_subject': 'お問い合わせタイトルをご入力ください',
'ticket.level': 'ご希望の優先度',
'ticket.please_select_level': 'ご希望の優先度をお選びください',
'ticket.message': 'メッセージ',
'ticket.please_enter_issue': 'お問い合わせ内容をご入力ください',
// traffic
'traffic.table_record_time': '記録日付',
'traffic.table_tx': 'アップロード',
'traffic.table_rx': 'ダウンロード',
'traffic.table_rate': '適応レート',
'traffic.table_total': '合計',
'traffic.table_total_tip': '計算公式:(アップロード + ダウンロード) x レート = 使用済みデータ通信量',
'traffic.traffic_detail': 'データ通信量明細',
'traffic.today': '本日',
'traffic.month': '今月',
'traffic.week': '今週',
// one_click_subscribe
'one_click_subscribe.copy_success': 'クリップボードにコピーされました',
'one_click_subscribe.copy_subscribe_url': '定期購入のURLをコピー',
'one_click_subscribe.import': 'インポート',
'one_click_subscribe': 'ワンクリックインポート',
'one_click_subscribe.copy_subscribe': '定期購入のURLをコピー',
// transfer_modal
'transfer_modal.title': 'コミッションを残高へ振替',
'transfer_modal.confirm': '確定',
'transfer_modal.cancel': 'キャンセル',
'transfer_modal.alert': '振替済みの残高は{title}でのみご利用可能です',
'transfer_modal.commission_balance': '現在のコミッション額',
'transfer_modal.transfer_balance': '振替金額',
'transfer_modal.please_enter_transfer_balance': '残高への振替金額をご入力ください',
// chat
'chat.please_enter_message': 'お問い合わせ内容をご入力ください...',
// withdraw_modal
'withdraw_modal.title': '出金申請',
'withdraw_modal.confirm': '確定',
'withdraw_modal.cancel': 'キャンセル',
'withdraw_modal.withdraw_method': 'お振込み先',
'withdraw_modal.please_select_method': 'お振込み先をお選びください',
'withdraw_modal.withdraw_account': 'お振り込み先口座',
'withdraw_modal.please_enter_account': 'お振込み先口座をご入力ください',
// bind_telegram
'bind_telegram_modal.confirm': '了解',
'bind_telegram_modal.title': 'Telegramの関連付けについて',
'bind_telegram_modal.step_1': 'ステップその1',
'bind_telegram_modal.step_2': 'ステップその2',
'bind_telegram_modal.open_telegram_search': 'Telegramを起動後に右記内容を入力し検索',
'bind_telegram_modal.send_bot_message': 'テレグラムボットへ下記内容を送信',
// knowledge
'knowledge': '知識ベース',
'knowledge.last_release_date': '最終更新日: {date}',
'knowledge.copy_success' : 'コピーしました',
// new language
'Dashboard': 'ダッシュボード',
'My subscription': 'ご利用中の定期購入',
'There are still unpaid orders': 'まだお支払いしていない注文があります',
'Pay now': '即時払い',
'Ticket is being processed': '工票は処理中です。',
'View now': 'すぐに確認します',
'Buy subscription': '定期購入のご注文',
'Knowledge': '知識ベース',
'My order': '注文履歴',
'Personal Center': '会員メニュー',
'Flow details': 'データ通信明細',
'Configure subscription': '購読の設定',
'My invitation': '招待メニュー',
'Node status': 'サーバーステータス',
'Copied': 'コピーしました',
'Product information': '商品情報',
'Product name': '商品コード',
'Type/Period': 'タイプ/周期',
'Product flow': '商品の流量',
'Order information': 'オーダー情報',
'Close order': '注文を閉じる',
'Order number': '注文番号',
'Discount amount': '割引金額',
'Deduction amount': '旧換算金額',
'Refund amount': '返金金額',
'Balance payment': '残額払い',
'Creation time': '作成時間',
'My tickets': 'マイチケット',
'Tickets history': 'チケット履歴',
'Reset flow after {reset_day} day': '{reset_day} 日後にリセットされます',
'Due on {date}, {day} days before expiration': '{date} の期限が切れるまで、あと {day} 日あります。',
'Telegram discuss': 'Telegram グループ',
'Join now': '参加する',
'Renew': '継続料金のお支払い',
'Buy': '買う',
'This subscription cannot be renewed. Only new users are allowed to purchase it': 'このサブスクリプションは更新できません。新しいユーザだけがそれを購入できます。'
}
'请求失败': 'リクエストエラー',
'月付': '月間プラン',
'季付': '3か月プラン',
'半年付': '半年プラン',
'年付': '年間プラン',
'两年付': '2年プラン',
'三年付': '3年プラン',
'一次性': '使い切りプラン',
'重置流量包': '使用済みデータをリセット',
'待支付': 'お支払い待ち',
'开通中': '処理中',
'已取消': 'キャンセル済み',
'已完成': '済み',
'已折抵': '控除済み',
'待确认': '承認待ち',
'发放中': '処理中',
'已发放': '処理済み',
'无效': '無効',
'个人中心': '会員メニュー',
'登出': 'ログアウト',
'搜索': '検索',
'仪表盘': 'ダッシュボード',
'订阅': 'サブスクリプションプラン',
'我的订阅': 'マイプラン',
'购买订阅': 'プランの購入',
'财务': 'ファイナンス',
'我的订单': '注文履歴',
'我的邀请': '招待リスト',
'用户': 'ユーザー',
'我的工单': 'お問い合わせ',
'流量明细': 'データ通信明細',
'使用文档': 'ナレッジベース',
'绑定Telegram获取更多服务': 'Telegramと連携し各種便利な通知を受け取ろう',
'点击这里进行绑定': 'こちらをクリックして連携開始',
'公告': 'お知らせ',
'总览': '概要',
'该订阅长期有效': '時間制限なし',
'已过期': '期限切れ',
'已用 {used} / 总计 {total}': '使用済み {used} / 合計 {total}',
'查看订阅': 'プランを表示',
'邮箱': 'E-mail アドレス',
'邮箱验证码': '確認コード',
'发送': '送信',
'重置密码': 'パスワードを変更',
'返回登入': 'ログインページへ戻る',
'邀请码': '招待コード',
'复制链接': 'URLをコピー',
'完成时间': '完了日時',
'佣金': 'コミッション金額',
'已注册用户数': '登録済みユーザー数',
'佣金比例': 'コミッションレート',
'确认中的佣金': '承認待ちのコミッション',
'佣金将会在确认后会到达你的佣金账户。': 'コミッションは承認処理完了後にカウントされます',
'邀请码管理': '招待コードの管理',
'生成邀请码': '招待コードを生成',
'邀请明细': '招待済みリスト',
'复制成功': 'クリップボードにコピーされました',
'密码': 'パスワード',
'登入': 'ログイン',
'注册': '新規登録',
'忘记密码': 'パスワードをお忘れの方は[こちら]',
'# 订单号': '受注番号',
'周期': 'サイクル',
'订单金额': 'ご注文金額',
'订单状态': 'ご注文状況',
'创建时间': '作成日時',
'操作': 'アクション',
'查看详情': '詳細を表示',
'请选择支付方式': '支払い方法をお選びください',
'请检查信用卡支付信息': 'クレジットカード決済情報をご確認ください',
'订单详情': 'ご注文詳細',
'折扣': '割引',
'折抵': '控除',
'退款': '払い戻し',
'支付方式': 'お支払い方法',
'填写信用卡支付信息': 'クレジットカード決済情報をご入力ください。',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': 'お客様のカード情報は今回限りリクエストされ、記録に残ることはございません',
'订单总额': 'ご注文の合計金額',
'总计': '合計金額',
'结账': 'チェックアウト',
'等待支付中': 'お支払い待ち',
'开通中': 'プラン処理中',
'订单系统正在进行处理请稍等1-3分钟。': 'システム処理中です、しばらくお待ちください',
'已取消': 'キャンセル',
'订单由于超时支付已被取消。': 'ご注文はキャンセルされました',
'已完成': '完了しました',
'订单已支付并开通。': 'お支払いが完了しました、プランはご利用可能です',
'选择订阅': 'プランをお選びください',
'立即订阅': '今すぐ購入',
'配置订阅': 'プランの内訳',
'折扣': '割引',
'付款周期': 'お支払いサイクル',
'有优惠券?': 'キャンペーンコード',
'验证': '確定',
'订单总额': 'ご注文の合計金額',
'下单': 'チェックアウト',
'总计': '合計',
'订阅变更须知': 'プラン変更のご注意',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': 'プランを変更なされます場合は、既存のプランが新規プランによって上書きされます、ご注意下さい',
'该订阅无法续费': '該当プランは継続利用できません',
'选择其他订阅': 'その他のプランを選択',
'我的钱包': 'マイウォレット',
'账户余额(仅消费)': '残高(サービスの購入のみ)',
'推广佣金(可提现)': '招待によるコミッション(出金可)',
'钱包组成部分': 'ウォレットの内訳',
'划转': 'お振替',
'推广佣金提现': 'コミッションのお引き出し',
'修改密码': 'パスワードの変更',
'保存': '変更を保存',
'旧密码': '現在のパスワード',
'新密码': '新しいパスワード',
'请输入旧密码': '現在のパスワードをご入力ください',
'请输入新密码': '新しいパスワードをご入力ください',
'通知': 'お知らせ',
'到期邮件提醒': '期限切れ前にメールで通知',
'流量邮件提醒': 'データ量不足時にメールで通知',
'绑定Telegram': 'Telegramと連携',
'立即开始': '今すぐ連携開始',
'重置订阅信息': 'サブスクリプションURLの変更',
'重置': '変更',
'确定要重置订阅信息?': 'サブスクリプションURLやUUIDをご変更なされますか',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'サブスクリプションのURL及び情報が外部に漏れた場合にご操作ください。操作後はUUIDやURLが変更され、再度サブスクリプションのインポートが必要になります',
'重置成功': '変更完了',
'两次新密码输入不同': 'ご入力されました新しいパスワードが一致しません',
'两次密码输入不同': 'ご入力されましたパスワードが一致しません',
'邮箱': 'E-mail アドレス',
'邮箱验证码': '確認コード',
'发送': '送信',
'邀请码': '招待コード',
'邀请码(选填)': '招待コード (オプション)',
'注册': '新規登録',
'返回登入': 'ログインページへ戻る',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': '<a target="_blank" href="{url}">ご利用規約</a>に同意します',
'请同意服务条款': 'ご利用規約に同意してください',
'名称': '名称',
'标签': 'ラベル',
'状态': 'ステータス',
'节点五分钟内节点在线情况': '5分間のオンラインステータス',
'倍率': '適応レート',
'使用的流量将乘以倍率进行扣除': '通信量は該当レートに基き計算されます',
'更多操作': 'アクション',
'复制成功': 'クリップボードにコピーされました',
'复制链接': 'リンクをコピー',
'该订阅长期有效': '時間制限なし',
'已过期': '期限切れ',
'已用 {used} / 总计 {total}': '使用済み {used} / 合計 {total}',
'重置订阅信息': 'サブスクリプションURLの変更',
'没有可用节点,如果您未订阅或已过期请': 'ご利用可能なサーバーがありません,プランの期限切れまたは購入なされていない場合は',
'订阅': '購入',
'确定要重置当月流量?': '当月分の使用済みデータ通信量をリセットしますか?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': '「確定」をクリックし次のページへ移動,お支払い後に当月分のデータ通信量は即時リセットされます',
'确定': '確定',
'确定要重置订阅信息?': 'サブスクリプションURLやUUIDをご変更なされますか',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'サブスクリプションのURL及び情報が外部に漏れた場合にご操作ください。操作後はUUIDやURLが変更され、再度サブスクリプションのインポートが必要になります',
'重置成功': '変更が完了しました',
'低': '低',
'中': '中',
'高': '高',
'主题': 'タイトル',
'工单级别': 'プライオリティ',
'工单状态': '進捗状況',
'最后回复': '最終回答日時',
'已关闭': '終了',
'待回复': '対応待ち',
'已回复': '回答済み',
'查看': '閲覧',
'关闭': '終了',
'新的工单': '新規お問い合わせ',
'新的工单': '新規お問い合わせ',
'确认': '送信',
'主题': 'タイトル',
'请输入工单主题': 'お問い合わせタイトルをご入力ください',
'工单等级': 'ご希望のプライオリティ',
'请选择工单等级': 'ご希望のプライオリティをお選びください',
'消息': 'メッセージ',
'请描述你遇到的问题': 'お問い合わせ内容をご入力ください',
'记录时间': '記録日時',
'实际上行': 'アップロード',
'实际下行': 'ダウンロード',
'合计': '合計',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': '計算式:(アップロード + ダウンロード) x 適応レート = 使用済みデータ通信量',
'复制成功': 'クリップボードにコピーされました',
'复制订阅地址': 'サブスクリプションのURLをコピー',
'导入到': 'インポート先:',
'一键订阅': 'ワンクリックインポート',
'复制订阅': 'サブスクリプションのURLをコピー',
'推广佣金划转至余额': 'コミッションを残高へ振替',
'确认': '確定',
'划转后的余额仅用于{title}消费使用': '振替済みの残高は{title}でのみご利用可能です',
'当前推广佣金余额': '現在のコミッション金額',
'划转金额': '振替金額',
'请输入需要划转到余额的金额': '振替金額をご入力ください',
'输入内容回复工单...': 'お問い合わせ内容をご入力ください...',
'申请提现': '出金申請',
'确认': '確定',
'取消': 'キャンセル',
'提现方式': 'お振込み先',
'请选择提现方式': 'お振込み先をお選びください',
'提现账号': 'お振り込み先口座',
'请输入提现账号': 'お振込み先口座をご入力ください',
'我知道了': '了解',
'绑定Telegram': 'Telegramと連携',
'第一步': 'ステップその1',
'第二步': 'ステップその2',
'打开Telegram搜索': 'Telegramを起動後に右記内容を入力し検索',
'向机器人发送你的': 'テレグラムボットへ下記内容を送信',
'使用文档': 'ナレッジベース',
'最后更新: {date}': '最終更新日: {date}',
'复制成功': 'クリップボードにコピーされました',
'我的订阅': 'ご利用中のプラン',
'还有没支付的订单': '未払いのご注文があります',
'立即支付': 'チェックアウト',
'条工单正在处理中': '件のお問い合わせ',
'立即查看': '閲覧',
'购买订阅': 'プランの購入',
'使用文档': 'ナレッジベース',
'我的订单': '注文履歴',
'流量明细': 'データ通信明細',
'配置订阅': 'プランの内訳',
'我的邀请': '招待メニュー',
'节点状态': 'サーバーステータス',
'复制成功': 'クリップボードにコピーされました',
'商品信息': 'プラン詳細',
'产品名称': 'プラン名',
'类型/周期': 'サイクル',
'产品流量': 'ご利用可能データ量',
'订单信息': 'オーダー情報',
'关闭订单': '注文をキャンセル',
'订单号': '受注番号',
'优惠金额': '\'割引額',
'旧订阅折抵金额': '既存プラン控除額',
'退款金额': '返金額',
'余额支付': '残高ご利用分',
'我的工单': 'お問い合わせ',
'工单历史': 'お問い合わせ履歴',
'{reset_day} 日后重置流量': '{reset_day} 日後にカウントリセット',
'节点名称': 'サーバー名',
'于 {date} 到期,距离到期还有 {day} 天。': 'ご利用期限は {date} まで,期限まであと {day} 日',
'Telegram 讨论组': 'Telegramグループ',
'立即加入': '今すぐ参加',
'续费': '継続料金のお支払い',
'购买': '購入',
'该订阅无法续费,仅允许新用户购买': '該当プランは継続利用できません、新規ユーザーのみが購入可能です',
'重置当月流量': '使用済みデータ量のカウントリセット',
'流量明细仅保留近月数据以供查询。': 'データ通信明細は当月分のみ表示されます',
'扣费倍率': '適応レート'
};

View File

@ -0,0 +1,248 @@
window.settings.i18n['ko-KR'] = {
'请求失败': '요청실패',
'月付': '월간',
'季付': '3개월간',
'半年付': '반년간',
'年付': '1년간',
'两年付': '2년마다',
'三年付': '3년마다',
'一次性': '한 번',
'重置流量包': '데이터 재설정 패키지',
'待支付': '지불 보류중',
'开通中': '보류 활성화',
'已取消': '취소 됨',
'已完成': '완료',
'已折抵': '변환',
'待确认': '보류중',
'发放中': '확인중',
'已发放': '완료',
'无效': '유효하지 않음',
'个人中心': '사용자 센터',
'登出': '로그아웃',
'搜索': '검색',
'仪表盘': '대시보드',
'订阅': '구독',
'我的订阅': '나의 구독',
'购买订阅': '구독 구매 내역',
'财务': '청구',
'我的订单': '나의 주문',
'我的邀请': '나의 초청',
'用户': '사용자 센터',
'我的工单': '나의 티켓',
'流量明细': '데이터 세부 정보 전송',
'使用文档': '사용 설명서',
'绑定Telegram获取更多服务': '텔레그램에 아직 연결되지 않았습니다',
'点击这里进行绑定': '텔레그램에 연결되도록 여기를 눌러주세요',
'公告': '공고',
'总览': '개요',
'该订阅长期有效': '구독은 무제한으로 유효합니다',
'已过期': '만료',
'已用 {used} / 总计 {total}': '{date}에 만료됩니다, 만료 {day}이 전, {reset_day}후 데이터 전송 재설정',
'查看订阅': '구독 보기',
'邮箱': '이메일',
'邮箱验证码': '이메일 확인 코드',
'发送': '보내기',
'重置密码': '비밀번호 재설정',
'返回登入': '로그인 다시하기',
'邀请码': '초청 코드',
'复制链接': '링크 복사',
'完成时间': '완료 시간',
'佣金': '수수료',
'已注册用户数': '등록 된 사용자들',
'佣金比例': '수수료율',
'确认中的佣金': '수수료 상태',
'佣金将会在确认后会到达你的佣金账户。': '수수료는 검토 후 수수료 계정에서 확인할 수 있습니다',
'邀请码管理': '초청 코드 관리',
'生成邀请码': '초청 코드 생성하기',
'邀请明细': '초청 세부사항',
'复制成功': '성공적으로 복사 됨',
'密码': '비밀번호',
'登入': '로그인',
'注册': '등록하기',
'忘记密码': '비밀번호를 잊으셨나요',
'# 订单号': '주문 번호 #',
'周期': '유형/기간',
'订单金额': '주문량',
'订单状态': '주문 상태',
'创建时间': '생성 시간',
'操作': '설정',
'查看详情': '세부사항 보기',
'请选择支付方式': '지불 방식을 선택 해주세요',
'请检查信用卡支付信息': '신용카드 지불 정보를 확인 해주세요',
'订单详情': '주문 세부사항',
'折扣': '할인',
'折抵': '변환',
'退款': '환불',
'支付方式': '지불 방식',
'填写信用卡支付信息': '신용카드 지불 정보를 적으세요',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': '현재 거래를 확인하는 데 사용하는 귀하의 신용 카드 정보, 신용 카드 번호 및 기타 세부 정보를 수집하지 않습니다.',
'订单总额': '전체 주문',
'总计': '전체',
'结账': '점검',
'等待支付中': '결제 대기 중',
'开通中': '개통 중',
'订单系统正在进行处理请稍等1-3分钟。': '주문 시스템이 처리 중입니다. 1-3분 정도 기다려 주십시오.',
'已取消': '취소 됨',
'订单由于超时支付已被取消。': '결제 시간 초과로 인해 주문이 취소되었습니다.',
'已完成': '성공',
'订单已支付并开通。': '주문이 결제되고 개통되었습니다.',
'选择订阅': '구독 선택하기',
'立即订阅': '지금 구독하기',
'配置订阅': '구독 환경 설정하기',
'折扣': '할인',
'付款周期': '지불 기간',
'有优惠券?': '쿠폰을 가지고 있나요?',
'验证': '확인',
'订单总额': '전체 주문',
'下单': '주문',
'总计': '전체',
'订阅变更须知': '구독 변경 사항 주의',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': '주의하십시오. 구독을 변경하면 현재 구독을 덮어씁니다',
'该订阅无法续费': '이 구독은 갱신할 수 없습니다.',
'选择其他订阅': '다른 구독 선택',
'我的钱包': '나의 지갑',
'账户余额(仅消费)': '계정 잔액(결제 전용)',
'推广佣金(可提现)': '초청수수료(인출하는 데 사용할 수 있습니다)',
'钱包组成部分': '지갑 세부사항',
'划转': '이체하기',
'推广佣金提现': '초청 수수료 인출',
'修改密码': '비밀번호 변경',
'保存': '저장하기',
'旧密码': '이전 비밀번호',
'新密码': '새로운 비밀번호',
'请输入旧密码': '이전 비밀번호를 입력해주세요',
'请输入新密码': '새로운 비밀번호를 입력해주세요',
'通知': '공고',
'到期邮件提醒': '구독 만료 이메일 알림',
'流量邮件提醒': '불충분한 데이터 이메일 전송 알림',
'绑定Telegram': '탤레그램으로 연결',
'立即开始': '지금 시작하기',
'重置订阅信息': '구독 재설정하기',
'重置': '재설정',
'确定要重置订阅信息?': '구독을 재설정하시겠습니까?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '계정 정보나 구독이 누출된 경우 이 옵션은 UUID를 재설정하는 데 사용되며 재설정 후에 구독이 변경되므로 다시 구독해야 합니다.',
'重置成功': '재설정 성공',
'两次新密码输入不同': '입력한 두 개의 새 비밀번호가 일치하지 않습니다.',
'两次密码输入不同': '입력한 비밀번호가 일치하지 않습니다.',
'邮箱': '이메일',
'邮箱验证码': '이메일 확인 코드',
'发送': '보내기',
'邀请码': '초청 코드',
'邀请码(选填)': '초청 코드(선택 사항)',
'注册': '등록하기',
'返回登入': '로그인으로 돌아가기',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': '을 읽었으며 이에 동의합니다 <a target="_blank" href="{url}">서비스 약관</a>',
'请同意服务条款': '서비스 약관에 동의해주세요',
'名称': '이름',
'标签': '태그',
'状态': '설정',
'节点五分钟内节点在线情况': '지난 5분 동안의 액세스 포인트 온라인 상태',
'倍率': '요금',
'使用的流量将乘以倍率进行扣除': '사용된 전송 데이터에 전송 데이터 요금을 뺀 값을 곱합니다.',
'更多操作': '설정',
'复制成功': '성공적으로 복사함',
'复制链接': '링크 복사',
'该订阅长期有效': '나의 구독',
'已过期': '구독 주문하기',
'已用 {used} / 总计 {total}': '{used} 이용량/{total} 전체',
'重置订阅信息': '구독 재설정',
'没有可用节点,如果您未订阅或已过期请': '사용 가능한 액세스 포인트가 없습니다. 구독을 신청하지 않았거나 구독이 만료된 경우',
'订阅': '구독',
'确定要重置当月流量?': '이번 달의 이체 데이터를 재설정하시겠습니까?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': '확인"을 클릭하면 결제 페이지로 이동됩니다. 주문이 완료되면 시스템에서 해당 월의 사용 데이터를 삭제합니다.',
'确定': '확인',
'确定要重置订阅信息?': '구독을 재설정하시겠습니까?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '계정 정보 또는 구독 정보가 누출된 경우 이 옵션을 사용하여 UUID를 재설정하며 재설정 후 구독이 변경되므로 다시 구독해야 합니다.',
'重置成功': '재설정 성공',
'低': '낮음',
'中': '중간',
'高': '높음',
'主题': '주제',
'工单级别': '티켓 우선 순위',
'工单状态': '티켓 상태',
'最后回复': '생성 시간',
'已关闭': '마지막 답장',
'待回复': '설정',
'已回复': '닫힘',
'查看': '보기',
'关闭': '닫기',
'新的工单': '새로운 티켓',
'新的工单': '새로운 티켓',
'确认': '확인',
'主题': '주제',
'请输入工单主题': '제목을 입력하세요',
'工单等级': '티켓 우선순위',
'请选择工单等级': '티켓 우선순위를 선택해주세요',
'消息': '메세지',
'请描述你遇到的问题': '문제를 설명하십시오 발생한',
'记录时间': '기록 시간',
'实际上行': '실제 업로드',
'实际下行': '실제 다운로드',
'合计': '전체',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': '공식: (실제 업로드 + 실제 다운로드) x 공제율 = 전송 데이터 공제',
'复制成功': '성공적으로 복사',
'复制订阅地址': '구독 URL 복사',
'导入到': '내보내기',
'一键订阅': '빠른 구독',
'复制订阅': '구독 URL 복사',
'推广佣金划转至余额': '초청 수수료를 계좌 잔액으로 이체',
'确认': '확인',
'划转后的余额仅用于{title}消费使用': '이체된 잔액은 {title} 결제에만 사용됩니다.',
'当前推广佣金余额': '현재 초청 잔액',
'划转金额': '이체 금액',
'请输入需要划转到余额的金额': '잔액으로 이체할 금액을 입력하세요',
'输入内容回复工单...': '티켓에 답장하려면 입력하세요...',
'申请提现': '인출 신청',
'确认': '확인',
'取消': '취소',
'提现方式': '인출 방법',
'请选择提现方式': '인출 방법을 선택해주세요',
'提现账号': '인출 계좌',
'请输入提现账号': '인출 계좌를 입력해주세요',
'我知道了': '알겠습니다.',
'绑定Telegram': '텔레그램 열기 및 탐색',
'第一步': '첫번째 단계',
'第二步': '두번째 단계',
'打开Telegram搜索': '텔레그램 열기 및 탐색',
'向机器人发送你的': '봇에 다음 명령을 보냅니다',
'使用文档': '사용 설명서',
'最后更新: {date}': '마지막 업데이트{date}',
'复制成功': '복사 성공',
'我的订阅': '나의 구독',
'还有没支付的订单': '미결제 주문이 있습니다',
'立即支付': '즉시 지불',
'条工单正在处理中': '티켓이 처리 중입니다',
'立即查看': '제목을 입력하세요',
'购买订阅': '구독 구매 내역',
'使用文档': '사용 설명서',
'我的订单': '나의 주문',
'流量明细': '데이터 세부 정보 전송',
'配置订阅': '구독 환경 설정하기',
'我的邀请': '나의 초청',
'节点状态': '노드 상태',
'复制成功': '복사 성공',
'商品信息': '제품 정보',
'产品名称': '제품 명칭',
'类型/周期': '종류/기간',
'产品流量': '제품 데이터 용량',
'订单信息': '주문 정보',
'关闭订单': '주문 취소',
'订单号': '주문 번호',
'优惠金额': '할인 가격',
'旧订阅折抵金额': '기존 패키지 공제 금액',
'退款金额': '환불 금액',
'余额支付': '잔액 지불',
'我的工单': '나의 티켓',
'工单历史': '티켓 기록',
'{reset_day} 日后重置流量': '{reset_day} 일 후 플로우 재설정',
'节点名称': '환불 금액',
'于 {date} 到期,距离到期还有 {day} 天。': '{day}까지, 만료 {day}일 전.',
'Telegram 讨论组': '텔레그램으로 문의하세요',
'立即加入': '지금 가입하세요',
'续费': '고쳐쓰기',
'购买': '구매',
'该订阅无法续费,仅允许新用户购买': '이 구독은 갱신할 수 없습니다. 신규 사용자만 구매할 수 있습니다.',
'重置当月流量': '이번 달 트래픽 초기화',
'流量明细仅保留近月数据以供查询。': '귀하의 트래픽 세부 정보는 최근 몇 달 동안만 유지됩니다',
'扣费倍率': '수수료 공제율'
};

View File

@ -1,316 +1,248 @@
window.settings.i18n['vi-VN'] = {
'request.error': 'Yêu Cầu Thất Bại',
// period_text
'period.month_price': 'Tháng',
'period.quarter_price': 'Hàng quý',
'period.half_year_price': 'Nửa năm',
'period.year_price': 'Năm',
'period.two_year_price': 'Hai năm',
'period.three_year_price': 'Ba năm',
'period.onetime_price': 'Dài hạn',
'period.reset_price': 'Cập nhật dung lượng',
// order_status
'order_status.no_paid': 'Đợi Thanh toán',
'order_status.opening': 'Khai mạc',
'order_status.cancel': 'Đã hủy',
'order_status.done': 'Thực hiện',
'order_status.surplus': 'Quy đổi',
// commission_status
'commission_status.pending_confirm': 'được xác nhận',
'commission_status.confirm': 'đã xác nhận',
'commission_status.done': 'Hoàn thành',
'commission_status.reject': 'không hợp',
// header
'header.user_center': 'Trung tâm kiểm soát',
'header.logout': 'đăng xuất',
'header.search': 'Tìm kiếm',
// nav
'nav.dashboard': 'Trang Chủ',
'nav.subscribe': 'Gói Dịch Vụ',
'nav.my_subscribe': 'Gói Dịch Vụ Của Tôi',
'nav.buy_subscribe': 'Mua Gói Dịch Vụ',
'nav.bill': 'Tài Chính',
'nav.my_order': 'Đơn Hàng Của Tôi',
'nav.my_invite': 'Lời Mời Của Tôi',
'nav.user': 'Người Dùng',
'nav.user_center': 'Trung Tâm Kiểm Soát',
'nav.my_ticket': 'Liên Hệ Với Chúng Tôi',
'nav.traffic_detail': 'Chi Tiết Dung Lượng',
'nav.knowledge': 'Kiến thức cơ bản',
// dashboard
'dashboard.not_bind_telegram': 'Chưa thêm liên kếtTelegram',
'dashboard.click_here_bind': 'Nhấp vào đây để thêm liên kết',
'dashboard.announcement': 'Thông báo',
'dashboard.override': 'Tổng quát',
'dashboard.pending_order': 'Đang đợi thanh toán',
'dashboard.pending_ticket': 'Chờ trả lời',
'dashboard.my_invite': 'Lời mời của tôi',
'dashboard.my_subscribe': 'Gói dịch vụ của tôi',
'dashboard.add_subscribe': 'Mua gói dịch vụ',
'dashboard.not_expire': 'Gói dịch vụ có giá trị trong thời gian dài',
'dashboard.expired': 'Tài khoản hết hạn',
'dashboard.subscribe_info': 'Năm {date} đến hạnThời gian đến hạn còn {day} ngày.',
'dashboard.traffic_info': 'Đã sử dụng {used} / Tổng dung lượng {total}',
'dashboard.view_subscribe': 'Xem gói dịch vụ',
'dashboard.renew': 'Gia hạn',
// forgetPassword
'forgetPassword.email': 'Email',
'forgetPassword.email_verify_code': 'Mã xác minh Email',
'forgetPassword.send': 'Gửi đi',
'forgetPassword.password': 'Mật khẩu',
'forgetPassword.reset_password': 'Đặt lại mật khẩu',
'forgetPassword.back_login': 'Quay lại đăng nhập',
// invite
'invite.table_code': 'Mã người mời',
'invite.table_create_time': 'Thời gian thực hiện',
'invite.table_action': 'Thao tác',
'invite.copy_link': 'Sao chép liên kết',
'invite.table_complete_time': 'Thời gian hoàn thành',
'invite.table_commission_balance': 'Tiền Hoa hồng',
'invite.table_commission_status': 'Trạng thái tiền hoa hồng',
'invite.override': 'Bảng chi tiết người được mời',
'invite.register_count': 'Số lượng người dùng đã đăng ký',
'invite.commission_rate': 'Phần trăm hoa hồng được hưởng',
'invite.confirm_commission': 'Tiền hoa hồng đang được xác nhận',
'invite.confirm_commission_tip': 'Tiền hoa hồng sau khi được xác nhận sẽ được gửi vào tài khoản tiền hoa hồng của bạn.',
'invite.invite_code_manage': 'Quản lý mã người mời',
'invite.generate_code': 'Tạo mã người mời',
'invite.detail': 'Chi tiết thiệp mời',
'invite.copy_success': 'Sao chép thành công',
'register.tos_url': 'Tôi đã đọc và đồng ý <a target="_blank" href="{url}">điều khoản dịch vụ</a>',
'register.please_agree_tos': 'Hãy đồng ý điều kiện dịch vụ',
// login
'login.email': 'Email',
'login.password': 'Mật khẩu',
'login': 'Đăng nhập',
'login.register': 'Đăng kí',
'login.forget_password': 'Quên mật khẩu',
// order
'order.table_no': '# Mã đơn hàng',
'order.table_period': 'Chu kì',
'order.table_amount': 'Tổng tiền đơn hàng',
'order.table_status': 'Trạng thái đơn hàng',
'order.table_create_time': 'Tạo thời gian',
'order.table_action': 'Thao tác',
'order.view_detail': 'Xem chi tiết',
'order.cancel': 'Hủy',
'order.order_manage': 'Quản lí đơn hàng',
'order.please_select_pay_method': 'Chọn hình thức thanh toán',
'order.please_check_credit_card': 'Vui lòng kiểm tra thông tin thanh toán thẻ',
'order.order_detail': 'Chi tiết đơn hàng',
'order.product': 'Sản phẩm',
'order.type_period': 'Loại hình/Chu kì',
'order.amount': 'Tổng cộng',
'order.traffic': 'Dung lượng',
'order.discount': 'Triết khấu',
'order.surplus': 'Quy đổi',
'order.refund': 'Hoàn tiền',
'order.balance_pay': 'Thanh toán qua số dư tài khoản',
'order.pay_method': 'Phương thức thanh toán',
'order.enter_credit_info': 'Điền thông tin thanh toán thẻ tín dụng',
'order.credit_alert': 'Thông tin thẻ tín dụng của bạn sẽ chỉ sử dụng cho khoản khấu trừ hiện tại, hệ thống sẽ không lưu thông tin đó, đây là cách an toàn nhất',
'order.total_amount': 'Tổng cộng',
'order.total': 'Tổng cộng',
'order.checkout': 'Thanh toán',
'order.waiting_payment': 'Đợi Thanh toán',
'order.result_opening_title': 'Khai mạc',
'order.result_opening_sub_title': 'Hệ thống đặt hàng đang xử lý, vui lòng đợi 1-3 phút.',
'order.result_cancel_title': 'Đã hủy',
'order.result_cancel_sub_title': 'Đơn hàng đã bị hủy do thanh toán ngoài giờ.',
'order.result_success_title': 'hoàn thành',
'order.result_success_sub_title': 'Đơn hàng đã được thanh toán và mở.',
// plan
'plan.select_subscribe': 'Chọn gói dịch vụ',
'plan.buy': 'Mua ngay',
'plan.setting_subscribe': 'Cài đặt',
'plan.discount': 'Triết khấu',
'plan.period': 'Chu kì thanh toán ',
'plan.have_coupon': 'Có phiếu giảm giá',
'plan.coupon_verify': 'Xác minh',
'plan.order_total_amount': 'Tổng cộng',
'plan.place_order': 'Đặt hàng',
'plan.total': 'Tổng cộng',
'plan.subscribe_change_title': 'Thông báo về gói dịch vụ thay đổi',
'plan.subscribe_change_content': 'Việc thay đổi gói dịch vụ mới sẽ khiến gói hiện tại bị ghi đè bởi đăng ký mới, xin lưu ý',
'plan.subscribe_not_renew': 'Không thể thay đổi bản đăng ký',
'plan.choose_another_subscribe': 'Chọn một sản phẩm khác',
// profile
'profile.user_center': 'Trung tâm kiểm soát',
'profile.my_wallet': 'Ví tiền của tôi',
'profile.balance_tip': 'Số dư tài khoản',
'profile.commission_balance_tip': 'Tiền hoa hồng (có thể rút)',
'profile.wallet_component': 'Thành phần cấu tạo ví tiền',
'profile.transfer': 'Chuyển đổi',
'profile.commission_withdraw': 'Rút tiền hoa hồng',
'profile.change_password': 'Đổi mật khẩu',
'profile.save': 'Lưu',
'profile.old_password': 'Mật khẩu cũ',
'profile.new_password': 'Mật khẩu mới',
'profile.re_new_password': 'Mật khẩu mới',
'profile.please_enter_old_password': 'Vui lòng nhập mật khẩu cũ',
'profile.please_enter_new_password': 'Vui lòng nhập mật khẩu mới',
'profile.please_enter_re_new_password': 'Vui lòng nhập mật khẩu mới',
'profile.notice': 'Thông báo',
'profile.expire_remind': 'Email nhắc nhở đến hạn',
'profile.traffic_remind': 'Email nhắc nhở dung lượng',
'profile.bind_telegram': 'Thêm Telegram',
'profile.start': 'Bắt đầu ngay',
'profile.reset_subscribe': 'Làm mới liên kết',
'profile.reset': 'Làm mới',
'profile.reset_info_notify_title': 'Bạn có chắc chắn muốn đặt lại liên kết không?',
'profile.reset_info_notify_content': 'Nếu địa chỉ đăng ký hoặc thông tin của bạn bị rò rỉ có thể thao tác ở đây.Sau khi đặt lại UUID và liên kết của bạn sẽ được thay đổi , bạn cần thêm lại liên kết mới.',
'profile.reset_info_notify_success': 'Làm mới thành công',
'profile.two_password_error': '2 lần đăng nhập mật khẩu mới không đúng',
// reg
'register.two_password_error': '2 lần đăng nhập mật khẩu không đúng',
'register.email': 'Email',
'register.verify_code': 'Mã xác minh Email',
'register.password': 'Mật khẩu',
'register.re_password': 'Mật khẩu',
'register.send': 'Gửi đi',
'register.invite_code_require': 'Mã người mời',
'register.invite_code': 'Mã người mời(chọn điền)',
'register': 'Đăng kí',
'register.back_login': 'Quay lại đăng nhập',
// subscribe
'subscribe.table_name': 'Tên',
'subscribe.table_tag': 'Kí hiệu',
'subscribe.table_action': 'Thao tác',
'subscribe.table_status': 'Trạng thái',
'subscribe.table_status_tip': 'Người dùng trực tuyến trong 5 phút',
'subscribe.table_rate': 'Bội suất',
'subscribe.table_rate_tip': 'Lưu lượng sử dụng sẽ được nhân với bội suất',
'subscribe.more_action': 'Nhiều hơn',
'subscribe.copy_success': 'Sao chép thành công',
'subscribe.copy_link': 'Sao chép liên kết',
'subscribe.my_subscribe': 'Gói dịch vụ của tôi',
'subscribe.add_subscribe': 'Mua gói dịch vụ',
'subscribe.not_expire': 'Gói dịch vụ có giá trị trong thời gian dài',
'subscribe.expired': 'Tài khoản hết hạn',
'subscribe.subscribe_info': 'Năm {date} đến hạnThời gian đến hạn còn {day} ngày.',
'subscribe.traffic_info': 'Đã sử dụng {used} / Tổng dung lượng {total}',
'subscribe.renew': 'Gia hạn',
'subscribe.action': 'Thao tác',
'subscribe.reset_traffic': 'Mua thêm dung lượng',
'subscribe.reset_info': 'Thay đổi liên kết',
'subscribe.node_status': 'Trạng thái của máy chủ',
'subscribe.no_node': 'Không có máy chủ, nếu bạn chưa mua gói dịch vụ hoặc gói dịch vụ đã hết hạn, vui lòng',
'subscribe.no_node_renew': 'Gia hạn',
'subscribe.no_node_go': 'Mua gói dịch vụ',
'subscribe.reset_traffic_notify_title': 'Bạn có chắc muốn làm mới lại dung lượng trong tháng ?',
'subscribe.reset_traffic_notify_content': 'Ấn [Xác nhận] để chuyển sang trang thanh toán, sau khi thanh toán dung lượng tháng của bạn sẽ trở về số 0 và cộng thêm dung lượng vừa mua.',
'subscribe.reset_traffic_notify_confirm': 'Xác nhận',
'subscribe.reset_traffic_notify_cancel': 'Hủy',
'subscribe.reset_info_notify_title': 'Bạn có chắc chắn muốn đặt lại liên kết không?',
'subscribe.reset_info_notify_content': 'Nếu liên kết và thông tin của bạn bị lộ ra ngoài có thể làm thao tác này. Sau khi làm mới UUID và liên kết của bạn sẽ được thay đổi. Bạn cần bắt đầu thêm lại liên kết vào phần mềm. ',
'subscribe.reset_info_notify_success': 'Làm mới thành công',
// ticket
'ticket.low': 'Thấp',
'ticket.middle': 'Trung',
'ticket.high': 'Cao',
'ticket.table_subject': 'Chủ đề',
'ticket.table_level': 'Mức độ',
'ticket.table_status': 'Trạng thái',
'ticket.table_create_time': 'Tạo thời gian',
'ticket.table_last_reply': 'Lần trả lời cuối',
'ticket.table_action': 'Thao tác',
'ticket.status_closed': 'Đã đóng',
'ticket.status_pending': 'Đợi trả lời',
'ticket.status_reply': 'Đã trả lời',
'ticket.action_view': 'Xem',
'ticket.action_close': 'Đóng',
'ticket.my_ticket': 'Liên hệ với chúng tôi',
'ticket.new_ticket': 'Thông tin mới',
'ticket.new_ticket_modal_title': 'Tin nhắn mới',
'ticket.new_ticket_modal_confirm': 'Xác nhận',
'ticket.new_ticket.modal_cancel': 'Hủy',
'ticket.subject': 'Chủ đề',
'ticket.please_enter_subject': 'Vui lòng nhập tiêu đề tin nhắn',
'ticket.level': 'Mức độ',
'ticket.please_select_level': 'Vui lòng chọn cấp độ tin nhắn',
'ticket.message': 'Nội dung tin nhắn',
'ticket.please_enter_issue': 'Mô tả vấn đề bạn gặp phải',
// traffic
'traffic.table_record_time': 'Ghi nhớ thời gian',
'traffic.table_tx': 'Thực tế tải lên',
'traffic.table_rx': 'Thực tế tải xuống',
'traffic.table_rate': 'Bội suất',
'traffic.table_total': 'Tổng dung lượng',
'traffic.table_total_tip': 'Công thức : ( Thực tế tải lên + Thực tế tải xuống ) x Bội suất = Dung lượng đã sử dụng',
'traffic.traffic_detail': 'Chi tiết dung lượng',
'traffic.today': 'Hôm nay',
'traffic.month': 'Tháng này',
'traffic.week': 'Tuần này',
// one_click_subscribe
'one_click_subscribe.copy_success': 'Sao chép thành công',
'one_click_subscribe.copy_subscribe_url': 'Sao chép liên kết',
'one_click_subscribe.import': 'Chuyển đến',
'one_click_subscribe': 'Nhấp chuột để đồng bộ máy chủ',
'one_click_subscribe.copy_subscribe': 'Sao chép liên kết',
// transfer_modal
'transfer_modal.title': 'Tiền hoa hồng chuyển sang tài khoản của bạn',
'transfer_modal.confirm': 'Xác nhận',
'transfer_modal.cancel': 'Hủy',
'transfer_modal.alert': 'Sau khi chuyển đổi sang tài khoản, tiền này chỉ dùng để mua {title} gói dịch vụ của chúng tôi',
'transfer_modal.commission_balance': 'Số dư tài khoản hoa hồng',
'transfer_modal.transfer_balance': 'Số tiền muốn đổi',
'transfer_modal.please_enter_transfer_balance': 'Vui lòng nhập số tiền bạn muốn đổi sang tài khoản',
// chat
'chat.please_enter_message': 'Điền thông tin nội dung trả lờ...',
// withdraw_modal
'withdraw_modal.title': 'Đăng kí rút tiền',
'withdraw_modal.confirm': 'Xác nhận',
'withdraw_modal.cancel': 'Hủy',
'withdraw_modal.withdraw_method': 'Phương thức rút tiền',
'withdraw_modal.please_select_method': 'Chọn hình thức rút tiền ',
'withdraw_modal.withdraw_account': 'Tài khoản rút tiền',
'withdraw_modal.please_enter_account': 'Vui lòng điền tài khoản rút tiền',
// bind_telegram
'bind_telegram_modal.confirm': 'Tôi biết',
'bind_telegram_modal.title': 'Thêm Telegram',
'bind_telegram_modal.step_1': 'Bước đầu tiên',
'bind_telegram_modal.step_2': 'Bước thứ hai',
'bind_telegram_modal.open_telegram_search': 'Bật Telegram tìm kiếm',
'bind_telegram_modal.send_bot_message': 'Gửi cho bot',
// knowledge
'knowledge': 'Kiến thức cơ bản',
'knowledge.last_release_date': 'Lần cập nhật cuối cùng vào: {date}',
'knowledge.copy_success' : 'Chép thành công',
// new language
'Dashboard': 'Bảng',
'My subscription': 'Ảnh của tôi',
'There are still unpaid orders': 'Vẫn còn những mệnh lệnh không lương',
'Pay now': 'Trả ngay đi.',
'Ticket is being processed': 'Đang xử lý lệnh làm việc.',
'View now': 'Xem ngay',
'Buy subscription': 'Mua ấn bản',
'Knowledge': 'Sử dụng tài liệu',
'My order': 'Lệnh của tôi',
'Personal Center': 'Trung tâm cá nhân',
'Flow details': 'Hoa chi tiết',
'Configure subscription': 'Cấu hình ảnh',
'My invitation': 'Giấy mời của tôi',
'Node status': 'Không biết',
'Copied': 'Nghe',
'Product information': 'Thông tin sản xuất',
'Product name': 'Tên sản phẩm',
'Type/Period': 'Kiểu/Period',
'Product flow': 'Truyền sản phẩm',
'Order information': 'Thông tin trật tự',
'Close order': 'Theo lệnh',
'Order number': 'Số mệnh lệnh',
'Discount amount': 'Giá trị',
'Deduction amount': 'Giá trị giảm giá',
'Refund amount': 'bù đắp',
'Balance payment': 'Khoản thanh toán',
'Creation time': 'Đã tạo',
'My tickets': 'Nhiệm vụ của tôi',
'Tickets history': 'Lịch sử trật tự công việc',
'Reset flow after {reset_day} day': 'Sau ngày {reset_day} dung lượng sẽ được làm mới.',
'Due on {date}, {day} days before expiration': 'Năm {date} đến hạnThời gian đến hạn còn {day} ngày.',
'Telegram discuss': 'Telegram thảo luận',
'Join now': 'Tham gia ngay',
'Renew': 'Gia hạn',
'Buy': 'Mua',
'This subscription cannot be renewed. Only new users are allowed to purchase it': 'Không thể thay đổi bản đăng ký này. Chỉ có người dùng mới được phép mua nó.'
}
'请求失败': 'Yêu Cầu Thất Bại',
'月付': 'Tháng',
'季付': 'Hàng Quý',
'半年付': '6 Tháng',
'年付': 'Năm',
'两年付': 'Hai Năm',
'三年付': 'Ba Năm',
'一次性': 'Dài Hạn',
'重置流量包': 'Cập Nhật Dung Lượng',
'待支付': 'Đợi Thanh Toán',
'开通中': 'Khai Mạc',
'已取消': 'Đã Hủy',
'已完成': 'Thực Hiện',
'已折抵': 'Quy Đổi',
'待确认': 'Đợi Xác Nhận',
'发放中': 'Đang Xác Nhận',
'已发放': 'Hoàn Thành',
'无效': 'Không Hợp Lệ',
'个人中心': 'Trung Tâm Kiểm Soát',
'登出': 'Đăng Xuất',
'搜索': 'Tìm Kiếm',
'仪表盘': 'Trang Chủ',
'订阅': 'Gói Dịch Vụ',
'我的订阅': 'Gói Dịch Vụ Của Tôi',
'购买订阅': 'Mua Gói Dịch Vụ',
'财务': 'Tài Chính',
'我的订单': 'Đơn Hàng Của Tôi',
'我的邀请': 'Lời Mời Của Tôi',
'用户': 'Người Dùng',
'我的工单': 'Liên Hệ Với Chúng Tôi',
'流量明细': 'Chi Tiết Dung Lượng',
'使用文档': 'Kiến Thức Cơ Bản',
'绑定Telegram获取更多服务': 'Liên kết Telegram thêm dịch vụ',
'点击这里进行绑定': 'Ấn vào để liên kết',
'公告': 'Thông Báo',
'总览': 'Tổng Quat',
'该订阅长期有效': 'Gói này có thời hạn dài',
'已过期': 'Tài khoản hết hạn',
'已用 {used} / 总计 {total}': 'Đã sử dụng {used} / Tổng dung lượng {total}',
'查看订阅': 'Xem Dịch Vụ',
'邮箱': 'E-mail',
'邮箱验证码': 'Mã xác minh mail',
'发送': 'Gửi',
'重置密码': 'Đặt Lại Mật Khẩu',
'返回登入': 'Về đăng nhập',
'邀请码': 'Mã mời',
'复制链接': 'Sao chép đường dẫn',
'完成时间': 'Thời gian hoàn thành',
'佣金': 'Tiền hoa hồng',
'已注册用户数': 'Số người dùng đã đăng ký',
'佣金比例': 'Tỷ lệ hoa hồng',
'确认中的佣金': 'Hoa hồng đang xác nhận',
'佣金将会在确认后会到达你的佣金账户。': 'Sau khi xác nhận tiền hoa hồng sẽ gửi đến tài khoản hoa hồng của bạn.',
'邀请码管理': 'Quản lý mã mời',
'生成邀请码': 'Tạo mã mời',
'邀请明细': 'Chi tiết mời',
'复制成功': 'Sao chép thành công',
'密码': 'Mật khẩu',
'登入': 'Đăng nhập',
'注册': 'Đăng ký',
'忘记密码': 'Quên mật khẩu',
'# 订单号': '# Mã đơn hàng',
'周期': 'Chu Kỳ',
'订单金额': 'Tiền đơn hàng',
'订单状态': 'Trạng thái đơn',
'创建时间': 'Thời gian tạo',
'操作': 'Thao tác',
'查看详情': 'Xem chi tiết',
'请选择支付方式': 'Chọn phương thức thanh toán',
'请检查信用卡支付信息': 'Hãy kiểm tra thông tin thẻ thanh toán',
'订单详情': 'Chi tiết đơn hàng',
'折扣': 'Chiết khấu',
'折抵': 'Giảm giá',
'退款': 'Hoàn lại',
'支付方式': 'Phương thức thanh toán',
'填写信用卡支付信息': 'Điền thông tin Thẻ Tín Dụng',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': 'Thông tin thẻ tín dụng của bạn sẽ chỉ được sử dụng cho lần thanh toán này, hệ thống sẽ không lưu thông tin đó, chúng tôi nghĩ đây à cách an toàn nhất.',
'订单总额': 'Tổng tiền đơn hàng',
'总计': 'Tổng',
'结账': 'Kết toán',
'等待支付中': 'Đang chờ thanh toán',
'开通中': 'Đang mở',
'订单系统正在进行处理请稍等1-3分钟。': 'Hệ thống đang xử lý đơn hàng, vui lòng đợi 1-3p.',
'已取消': 'Đã hủy',
'订单由于超时支付已被取消。': 'Do quá giờ nên đã hủy đơn hàng.',
'已完成': 'Đã hoàn thành',
'订单已支付并开通。': 'Đơn hàng đã thanh toán và mở.',
'选择订阅': 'Chọn gói',
'立即订阅': 'Mua gói ngay',
'配置订阅': 'Thiết lập gói',
'折扣': 'Chiết khấu',
'付款周期': 'Chu kỳ thanh toán',
'有优惠券?': 'Có phiếu giảm giá?',
'验证': 'Xác minh',
'订单总额': 'Tổng tiền đơn hàng',
'下单': 'Đặt hàng',
'总计': 'Tổng',
'订阅变更须知': 'Thông báo thay đổi gói dịch vụ',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': 'Việc thay đổi gói dịch vụ sẽ thay thế gói hiện tại bằng gói mới, xin lưu ý.',
'该订阅无法续费': 'Gói này không thể gia hạn',
'选择其他订阅': 'Chọn gói dịch vụ khác',
'我的钱包': 'Ví tiền của tôi',
'账户余额(仅消费)': 'Số dư tài khoản (Chỉ tiêu dùng)',
'推广佣金(可提现)': 'Tiền hoa hồng giới thiệu (Được rút)',
'钱包组成部分': 'Thành phần ví tiền',
'划转': 'Chuyển khoản',
'推广佣金提现': 'Rút tiền hoa hồng giới thiệu',
'修改密码': 'Đổi mật khẩu',
'保存': 'Lưu',
'旧密码': 'Mật khẩu cũ',
'新密码': 'Mật khẩu mới',
'请输入旧密码': 'Hãy nhập mật khẩu cũ',
'请输入新密码': 'Hãy nhập mật khẩu mới',
'通知': 'Thông Báo',
'到期邮件提醒': 'Mail nhắc đến hạn',
'流量邮件提醒': 'Mail nhắc dung lượng',
'绑定Telegram': 'Liên kết Telegram',
'立即开始': 'Bắt Đầu',
'重置订阅信息': 'Reset thông tin gói',
'重置': 'Reset',
'确定要重置订阅信息?': 'Xác nhận reset thông tin gói?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'Nếu địa chỉ hoặc thông tin gói dịch vụ của bạn bị tiết lộ có thể tiến hành thao tác này. Sau khi reset UUID sẽ thay đổi.',
'重置成功': 'Reset thành công',
'两次新密码输入不同': 'Mật khẩu mới xác nhận không khớp',
'两次密码输入不同': 'Mật khẩu xác nhận không khớp',
'邮箱': 'E-mail',
'邮箱验证码': 'Mã xác minh mail',
'发送': 'Gửi',
'邀请码': 'Mã mời',
'邀请码(选填)': 'Mã mời(Điền)',
'注册': 'Đăng Ký',
'返回登入': 'Về đăng nhập',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': 'Tôi đã đọc và đồng ý <a target="_blank" href="{url}">điều khoản dịch vụ</a>',
'请同意服务条款': 'Hãy đồng ý điều khoản dịch vụ',
'名称': 'Tên',
'标签': 'Nhãn',
'状态': 'Trạng thái',
'节点五分钟内节点在线情况': 'Node trạng thái online trong vòng 5 phút',
'倍率': 'Bội số',
'使用的流量将乘以倍率进行扣除': 'Dung lượng sử dụng nhân với bội số rồi khấu trừ',
'更多操作': 'Thêm thao tác',
'复制成功': 'Sao chép thành công',
'复制链接': 'Sao chép đường dẫn',
'该订阅长期有效': 'Gói này có thời hạn dài',
'已过期': 'Đã hết hạn',
'已用 {used} / 总计 {total}': 'Đã dùng {used} / Tổng {total}',
'重置订阅信息': 'Reset thông tin gói',
'没有可用节点,如果您未订阅或已过期请': 'Chưa có node khả dụng, nếu bạn chưa mua gói hoặc đã hết hạn hãy',
'订阅': 'Mua Gói',
'确定要重置当月流量?': 'Xác nhận muốn reset dung lượng tháng này?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': 'Ấn 「OK」 sẽ chuyển đến trang thanh toán, sau khi thanh toán đơn hàng hệ thống sẽ xóa dung lượng đã dùng tháng này của bạn.',
'确定': 'OK',
'确定要重置订阅信息?': 'Xác nhận reset thông tin gói dịch vụ?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': 'Nếu địa chỉ hoặc thông tin gói dịch vụ của bạn bị tiết lộ có thể tiến hành thao tác này. Sau khi reset Uuid sẽ thay đổi.',
'重置成功': 'Reset thành công',
'低': 'Thấp',
'中': 'Vừa',
'高': 'Cao',
'主题': 'Chủ Đề',
'工单级别': 'Cấp độ',
'工单状态': 'Trạng thái',
'最后回复': 'Trả lời gần đây',
'已关闭': 'Đã đóng',
'待回复': 'Chờ trả lời',
'已回复': 'Đã trả lời',
'查看': 'Xem',
'关闭': 'Đóng',
'新的工单': 'Việc mới',
'新的工单': 'Việc mới',
'确认': 'OK',
'主题': 'Chủ Đề',
'请输入工单主题': 'Hãy nhập chủ đề công việc',
'工单等级': 'Cấp độ công việc',
'请选择工单等级': 'Hãy chọn cấp độ công việc',
'消息': 'Thông tin',
'请描述你遇到的问题': 'Hãy mô tả vấn đề gặp phải',
'记录时间': 'Thời gian ghi',
'实际上行': 'Upload thực tế',
'实际下行': 'Download thực tế',
'合计': 'Cộng',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': 'Công thức: (upload thực tế + download thực tế) x bội số trừ phí = Dung lượng khấu trừ',
'复制成功': 'Sao chép thành công',
'复制订阅地址': 'Sao chép liên kết',
'导入到': 'Nhập vào',
'一键订阅': 'Nhấp chuột để đồng bộ máy chủ',
'复制订阅': 'Sao chép liên kết',
'推广佣金划转至余额': 'Chuyển khoản hoa hồng giới thiệu đến số dư',
'确认': 'OK',
'划转后的余额仅用于{title}消费使用': 'Số dư sau khi chuyển khoản chỉ dùng để tiêu dùng {title}',
'当前推广佣金余额': 'Số dư hoa hồng giới thiệu hiện tại',
'划转金额': 'Chuyển tiền',
'请输入需要划转到余额的金额': 'Hãy nhậo số tiền muốn chuyển đến số dư',
'输入内容回复工单...': 'Nhập nội dung trả lời công việc...',
'申请提现': 'Yêu cầu rút tiền',
'确认': 'OK',
'取消': 'Hủy',
'提现方式': 'Phương thức rút tiền',
'请选择提现方式': 'Hãy chọn phương thức rút tiền',
'提现账号': 'Rút về tào khoản',
'请输入提现账号': 'Hãy chọn tài khoản rút tiền',
'我知道了': 'OK',
'绑定Telegram': 'Liên kết Telegram',
'第一步': 'Bước 1',
'第二步': 'Bước 2',
'打开Telegram搜索': 'Mở Telegram tìm kiếm',
'向机器人发送你的': 'Gửi cho bot',
'使用文档': 'Tài liệu sử dụng',
'最后更新: {date}': 'Cập nhật gần đây: {date}',
'复制成功': 'Sao chép thành công',
'我的订阅': 'Gói Dịch Vụ Của Tôi',
'还有没支付的订单': 'Có đơn hàng chưa thanh toán',
'立即支付': 'Thanh toán ngay',
'条工单正在处理中': ' công việc đang xử lý',
'立即查看': 'Xem Ngay',
'购买订阅': 'Mua Gói Dịch Vụ',
'使用文档': 'Tài liệu sử dụng',
'我的订单': 'Đơn Hàng Của Tôi',
'流量明细': 'Chi Tiết Dung Lượng',
'配置订阅': 'Thiết lập gói',
'我的邀请': 'Lời Mời Của Tôi',
'节点状态': 'Trạng thái node',
'复制成功': 'Sao chép thành công',
'商品信息': 'Thông tin',
'产品名称': 'Tên sản phẩm',
'类型/周期': 'Loại/Chu kỳ',
'产品流量': 'Dung Lượng',
'订单信息': 'Thông tin đơn hàng',
'关闭订单': 'Đóng đơn hàng',
'订单号': 'Mã đơn hàng',
'优惠金额': 'Tiền ưu đãi',
'旧订阅折抵金额': 'Tiền giảm giá gói cũ',
'退款金额': 'Số tiền hoàn lại',
'余额支付': 'Thanh toán số dư',
'我的工单': 'Liên Hệ Với Chúng Tôi',
'工单历史': 'Lịch sử đơn hàng',
'{reset_day} 日后重置流量': '{reset_day} ngày sau reset dung lượng',
'节点名称': 'Tên node',
'于 {date} 到期,距离到期还有 {day} 天。': 'Hết hạn vào {date}, còn {day} ngày.',
'Telegram 讨论组': 'Nhóm Telegram',
'立即加入': 'Vào ngay',
'续费': 'Gia hạn',
'购买': 'Mua',
'该订阅无法续费,仅允许新用户购买': 'Đăng ký này không thể gia hạn, chỉ người dùng mới được phép mua',
'重置当月流量': 'Đặt lại dung lượng tháng hiện tại',
'流量明细仅保留近月数据以供查询。': 'Chi tiết dung lượng chỉ lưu dữ liệu của những tháng gần đây để truy vấn.',
'扣费倍率': 'Tỷ lệ khấu trừ'
};

View File

@ -1,318 +1,248 @@
window.settings.i18n['zh-CN'] = {
'request.error': '请求失败',
// period_text
'period.month_price': '月付',
'period.quarter_price': '季付',
'period.half_year_price': '半年付',
'period.year_price': '年付',
'period.two_year_price': '两年付',
'period.three_year_price': '三年付',
'period.onetime_price': '一次性',
'period.reset_price': '重置流量包',
// order_status
'order_status.no_paid': '待支付',
'order_status.opening': '开通中',
'order_status.cancel': '已取消',
'order_status.done': '已完成',
'order_status.surplus': '已折抵',
// commission_status
'commission_status.pending_confirm': '待确认',
'commission_status.confirm': '发放中',
'commission_status.done': '已发放',
'commission_status.reject': '无效',
// header
'header.user_center': '个人中心',
'header.logout': '登出',
'header.search': '搜索',
// nav
'nav.dashboard': '仪表盘',
'nav.subscribe': '订阅',
'nav.my_subscribe': '我的订阅',
'nav.buy_subscribe': '购买订阅',
'nav.bill': '财务',
'nav.my_order': '我的订单',
'nav.my_invite': '我的邀请',
'nav.user': '用户',
'nav.user_center': '个人中心',
'nav.my_ticket': '我的工单',
'nav.traffic_detail': '流量明细',
'nav.knowledge': '使用文档',
// dashboard
'dashboard.not_bind_telegram': '绑定Telegram获取更多服务',
'dashboard.click_here_bind': '点击这里进行绑定',
'dashboard.announcement': '公告',
'dashboard.override': '总览',
'dashboard.pending_order': '待付订单',
'dashboard.pending_ticket': '待办工单',
'dashboard.my_invite': '我的邀请',
'dashboard.my_subscribe': '我的订阅',
'dashboard.add_subscribe': '添加订阅',
'dashboard.not_expire': '该订阅长期有效',
'dashboard.expired': '已过期',
'dashboard.subscribe_info': '于 {date} 到期,距离到期还有 {day} 天',
'dashboard.traffic_info': '已用 {used} / 总计 {total}',
'dashboard.view_subscribe': '查看订阅',
'dashboard.renew': '续费',
// forgetPassword
'forgetPassword.email': '邮箱',
'forgetPassword.email_verify_code': '邮箱验证码',
'forgetPassword.send': '发送',
'forgetPassword.password': '密码',
'forgetPassword.reset_password': '重置密码',
'forgetPassword.back_login': '返回登入',
// invite
'invite.table_code': '邀请码',
'invite.table_create_time': '创建时间',
'invite.table_action': '操作',
'invite.copy_link': '复制链接',
'invite.table_complete_time': '完成时间',
'invite.table_commission_balance': '佣金',
'invite.table_commission_status': '佣金状态',
'invite.override': '邀请总览',
'invite.register_count': '已注册用户数',
'invite.commission_rate': '佣金比例',
'invite.confirm_commission': '确认中的佣金',
'invite.confirm_commission_tip': '佣金将会在确认后会到达你的佣金账户。',
'invite.invite_code_manage': '邀请码管理',
'invite.generate_code': '生成邀请码',
'invite.detail': '邀请明细',
'invite.copy_success': '复制成功',
// login
'login.email': '邮箱',
'login.password': '密码',
'login': '登入',
'login.register': '注册',
'login.forget_password': '忘记密码',
// order
'order.table_no': '# 订单号',
'order.table_period': '周期',
'order.table_amount': '订单金额',
'order.table_status': '订单状态',
'order.table_create_time': '创建时间',
'order.table_action': '操作',
'order.view_detail': '查看详情',
'order.cancel': '取消',
'order.order_manage': '订单管理',
'order.please_select_pay_method': '请选择支付方式',
'order.please_check_credit_card': '请检查信用卡支付信息',
'order.order_detail': '订单详情',
'order.product': '产品',
'order.type_period': '类型/周期',
'order.amount': '金额',
'order.traffic': '流量',
'order.discount': '折扣',
'order.surplus': '折抵',
'order.refund': '退款',
'order.balance_pay': '余额支付',
'order.pay_method': '支付方式',
'order.enter_credit_info': '填写信用卡支付信息',
'order.credit_alert': '您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。',
'order.total_amount': '订单总额',
'order.total': '总计',
'order.checkout': '结账',
'order.waiting_payment': '等待支付中',
'order.result_opening_title': '开通中',
'order.result_opening_sub_title': '订单系统正在进行处理请稍等1-3分钟。',
'order.result_cancel_title': '已取消',
'order.result_cancel_sub_title': '订单由于超时支付已被取消。',
'order.result_success_title': '已完成',
'order.result_success_sub_title': '订单已支付并开通。',
// plan
'plan.select_subscribe': '选择订阅',
'plan.buy': '立即订阅',
'plan.setting_subscribe': '配置订阅',
'plan.discount': '折扣',
'plan.period': '付款周期',
'plan.have_coupon': '有优惠券?',
'plan.coupon_verify': '验证',
'plan.order_total_amount': '订单总额',
'plan.place_order': '下单',
'plan.total': '总计',
'plan.subscribe_change_title': '订阅变更须知',
'plan.subscribe_change_content': '变更订阅会导致当前订阅被新订阅覆盖,请注意。',
'plan.subscribe_not_renew': '该订阅无法续费',
'plan.choose_another_subscribe': '选择其他订阅',
// profile
'profile.user_center': '个人中心',
'profile.my_wallet': '我的钱包',
'profile.balance_tip': '账户余额(仅消费)',
'profile.commission_balance_tip': '推广佣金(可提现)',
'profile.wallet_component': '钱包组成部分',
'profile.transfer': '划转',
'profile.commission_withdraw': '推广佣金提现',
'profile.change_password': '修改密码',
'profile.save': '保存',
'profile.old_password': '旧密码',
'profile.new_password': '新密码',
'profile.re_new_password': '新密码',
'profile.please_enter_old_password': '请输入旧密码',
'profile.please_enter_new_password': '请输入新密码',
'profile.please_enter_re_new_password': '请输入新密码',
'profile.notice': '通知',
'profile.expire_remind': '到期邮件提醒',
'profile.traffic_remind': '流量邮件提醒',
'profile.bind_telegram': '绑定Telegram',
'profile.start': '立即开始',
'profile.reset_subscribe': '重置订阅信息',
'profile.reset': '重置',
'profile.reset_info_notify_title': '确定要重置订阅信息?',
'profile.reset_info_notify_content': '如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。',
'profile.reset_info_notify_success': '重置成功',
'profile.two_password_error': '两次新密码输入不同',
// reg
'register.two_password_error': '两次密码输入不同',
'register.email': '邮箱',
'register.verify_code': '邮箱验证码',
'register.password': '密码',
'register.re_password': '密码',
'register.send': '发送',
'register.invite_code_require': '邀请码',
'register.invite_code': '邀请码(选填)',
'register': '注册',
'register.back_login': '返回登入',
'register.tos_url': '我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>',
'register.please_agree_tos': '请同意服务条款',
// subscribe
'subscribe.table_name': '名称',
'subscribe.table_tag': '标签',
'subscribe.table_action': '操作',
'subscribe.table_status': '状态',
'subscribe.table_status_tip': '节点五分钟内节点在线情况',
'subscribe.table_rate': '倍率',
'subscribe.table_rate_tip': '使用的流量将乘以倍率进行扣除',
'subscribe.more_action': '更多操作',
'subscribe.copy_success': '复制成功',
'subscribe.copy_link': '复制链接',
'subscribe.my_subscribe': '我的订阅',
'subscribe.add_subscribe': '添加订阅',
'subscribe.not_expire': '该订阅长期有效',
'subscribe.expired': '已过期',
'subscribe.subscribe_info': '于 {date} 到期,距离到期还有 {day} 天',
'subscribe.traffic_info': '已用 {used} / 总计 {total}',
'subscribe.renew': '续费',
'subscribe.action': '操作',
'subscribe.reset_traffic': '重置当月流量',
'subscribe.reset_info': '重置订阅信息',
'subscribe.node_status': '节点状态',
'subscribe.no_node': '没有可用节点,如果您未订阅或已过期请',
'subscribe.no_node_renew': '续费',
'subscribe.no_node_go': '订阅',
'subscribe.reset_traffic_notify_title': '确定要重置当月流量?',
'subscribe.reset_traffic_notify_content': '点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。',
'subscribe.reset_traffic_notify_confirm': '确定',
'subscribe.reset_traffic_notify_cancel': '取消',
'subscribe.reset_info_notify_title': '确定要重置订阅信息?',
'subscribe.reset_info_notify_content': '如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。',
'subscribe.reset_info_notify_success': '重置成功',
// ticket
'ticket.low': '低',
'ticket.middle': '中',
'ticket.high': '高',
'ticket.table_subject': '主题',
'ticket.table_level': '工单级别',
'ticket.table_status': '工单状态',
'ticket.table_create_time': '创建时间',
'ticket.table_last_reply': '最后回复',
'ticket.table_action': '操作',
'ticket.status_closed': '已关闭',
'ticket.status_pending': '待回复',
'ticket.status_reply': '已回复',
'ticket.action_view': '查看',
'ticket.action_close': '关闭',
'ticket.my_ticket': '我的工单',
'ticket.new_ticket': '新的工单',
'ticket.new_ticket_modal_title': '新的工单',
'ticket.new_ticket_modal_confirm': '确认',
'ticket.new_ticket.modal_cancel': '取消',
'ticket.subject': '主题',
'ticket.please_enter_subject': '请输入工单主题',
'ticket.level': '工单等级',
'ticket.please_select_level': '请选择工单等级',
'ticket.message': '消息',
'ticket.please_enter_issue': '请描述你遇到的问题',
// traffic
'traffic.table_record_time': '记录时间',
'traffic.table_tx': '实际上行',
'traffic.table_rx': '实际下行',
'traffic.table_rate': '扣费倍率',
'traffic.table_total': '合计',
'traffic.table_total_tip': '公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量',
'traffic.traffic_detail': '流量明细',
'traffic.today': '今日',
'traffic.month': '本月',
'traffic.week': '本周',
// one_click_subscribe
'one_click_subscribe.copy_success': '复制成功',
'one_click_subscribe.copy_subscribe_url': '复制订阅地址',
'one_click_subscribe.import': '导入到',
'one_click_subscribe': '一键订阅',
'one_click_subscribe.copy_subscribe': '复制订阅',
// transfer_modal
'transfer_modal.title': '推广佣金划转至余额',
'transfer_modal.confirm': '确认',
'transfer_modal.cancel': '取消',
'transfer_modal.alert': '划转后的余额仅用于{title}消费使用',
'transfer_modal.commission_balance': '当前推广佣金余额',
'transfer_modal.transfer_balance': '划转金额',
'transfer_modal.please_enter_transfer_balance': '请输入需要划转到余额的金额',
// chat
'chat.please_enter_message': '输入内容回复工单...',
// withdraw_modal
'withdraw_modal.title': '申请提现',
'withdraw_modal.confirm': '确认',
'withdraw_modal.cancel': '取消',
'withdraw_modal.withdraw_method': '提现方式',
'withdraw_modal.please_select_method': '请选择提现方式',
'withdraw_modal.withdraw_account': '提现账号',
'withdraw_modal.please_enter_account': '请输入提现账号',
// bind_telegram
'bind_telegram_modal.confirm': '我知道了',
'bind_telegram_modal.title': '绑定Telegram',
'bind_telegram_modal.step_1': '第一步',
'bind_telegram_modal.step_2': '第二步',
'bind_telegram_modal.open_telegram_search': '打开Telegram搜索',
'bind_telegram_modal.send_bot_message': '向机器人发送你的',
// knowledge
'knowledge': '使用文档',
'knowledge.last_release_date': '最后更新: {date}',
'knowledge.copy_success' : '复制成功',
// new language
'Dashboard': '仪表盘',
'My subscription': '我的订阅',
'There are still unpaid orders': '还有没支付的订单',
'Pay now': '立即支付',
'Ticket is being processed': '条工单正在处理中',
'View now': '立即查看',
'Buy subscription': '购买订阅',
'Knowledge': '使用文档',
'My order': '我的订单',
'Personal Center': '个人中心',
'Flow details': '流量明细',
'Configure subscription': '配置订阅',
'My invitation': '我的邀请',
'Node status': '节点状态',
'Copied': '复制成功',
'Product information': '商品信息',
'Product name': '产品名称',
'Type/Period': '类型/周期',
'Product flow': '产品流量',
'Order information': '订单信息',
'Close order': '关闭订单',
'Order number': '订单号',
'Discount amount': '优惠金额',
'Deduction amount': '旧订阅折抵金额',
'Refund amount': '退款金额',
'Balance payment': '余额支付',
'Creation time': '创建时间',
'My tickets': '我的工单',
'Tickets history': '工单历史',
'Reset flow after {reset_day} day': '{reset_day} 日后重置流量',
'Node name': '节点名称',
'Due on {date}, {day} days before expiration': '于 {date} 到期,距离到期还有 {day} 天。',
'Telegram discuss': 'Telegram 讨论组',
'Join now': '立即加入',
'Renew': '续费',
'Buy': '购买',
'This subscription cannot be renewed. Only new users are allowed to purchase it': '该订阅无法续费,仅允许新用户购买'
}
'请求失败': '请求失败',
'月付': '月付',
'季付': '季付',
'半年付': '半年付',
'年付': '年付',
'两年付': '两年付',
'三年付': '三年付',
'一次性': '一次性',
'重置流量包': '重置流量包',
'待支付': '待支付',
'开通中': '开通中',
'已取消': '已取消',
'已完成': '已完成',
'已折抵': '已折抵',
'待确认': '待确认',
'发放中': '发放中',
'已发放': '已发放',
'无效': '无效',
'个人中心': '个人中心',
'登出': '登出',
'搜索': '搜索',
'仪表盘': '仪表盘',
'订阅': '订阅',
'我的订阅': '我的订阅',
'购买订阅': '购买订阅',
'财务': '财务',
'我的订单': '我的订单',
'我的邀请': '我的邀请',
'用户': '用户',
'我的工单': '我的工单',
'流量明细': '流量明细',
'使用文档': '使用文档',
'绑定Telegram获取更多服务': '绑定 Telegram 获取更多服务',
'点击这里进行绑定': '点击这里进行绑定',
'公告': '公告',
'总览': '总览',
'该订阅长期有效': '该订阅长期有效',
'已过期': '已过期',
'已用 {used} / 总计 {total}': '已用 {used} / 总计 {total}',
'查看订阅': '查看订阅',
'邮箱': '邮箱',
'邮箱验证码': '邮箱验证码',
'发送': '发送',
'重置密码': '重置密码',
'返回登入': '返回登入',
'邀请码': '邀请码',
'复制链接': '复制链接',
'完成时间': '完成时间',
'佣金': '佣金',
'已注册用户数': '已注册用户数',
'佣金比例': '佣金比例',
'确认中的佣金': '确认中的佣金',
'佣金将会在确认后会到达你的佣金账户。': '佣金将会在确认后会到达您的佣金账户。',
'邀请码管理': '邀请码管理',
'生成邀请码': '生成邀请码',
'邀请明细': '邀请明细',
'复制成功': '复制成功',
'密码': '密码',
'登入': '登入',
'注册': '注册',
'忘记密码': '忘记密码',
'# 订单号': '# 订单号',
'周期': '周期',
'订单金额': '订单金额',
'订单状态': '订单状态',
'创建时间': '创建时间',
'操作': '操作',
'查看详情': '查看详情',
'请选择支付方式': '请选择支付方式',
'请检查信用卡支付信息': '请检查信用卡支付信息',
'订单详情': '订单详情',
'折扣': '折扣',
'折抵': '折抵',
'退款': '退款',
'支付方式': '支付方式',
'填写信用卡支付信息': '填写信用卡支付信息',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': '您的信用卡信息只会用于当次扣款,系统并不会保存,我们认为这是最安全的。',
'订单总额': '订单总额',
'总计': '总计',
'结账': '结账',
'等待支付中': '等待支付中',
'开通中': '开通中',
'订单系统正在进行处理请稍等1-3分钟。': '订单系统正在进行处理,请等候 1-3 分钟。',
'已取消': '已取消',
'订单由于超时支付已被取消。': '订单由于超时支付已被取消。',
'已完成': '已完成',
'订单已支付并开通。': '订单已支付并开通。',
'选择订阅': '选择订阅',
'立即订阅': '立即订阅',
'配置订阅': '配置订阅',
'折扣': '折扣',
'付款周期': '付款周期',
'有优惠券?': '有优惠券?',
'验证': '验证',
'订单总额': '订单总额',
'下单': '下单',
'总计': '总计',
'订阅变更须知': '订阅变更须知',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': '变更订阅会导致当前订阅被新订阅覆盖,请注意。',
'该订阅无法续费': '该订阅无法续费',
'选择其他订阅': '选择其它订阅',
'我的钱包': '我的钱包',
'账户余额(仅消费)': '账户余额(仅消费)',
'推广佣金(可提现)': '推广佣金(可提现)',
'钱包组成部分': '钱包组成部分',
'划转': '划转',
'推广佣金提现': '推广佣金提现',
'修改密码': '修改密码',
'保存': '保存',
'旧密码': '旧密码',
'新密码': '新密码',
'请输入旧密码': '请输入旧密码',
'请输入新密码': '请输入新密码',
'通知': '通知',
'到期邮件提醒': '到期邮件提醒',
'流量邮件提醒': '流量邮件提醒',
'绑定Telegram': '绑定 Telegram',
'立即开始': '立即开始',
'重置订阅信息': '重置订阅信息',
'重置': '重置',
'确定要重置订阅信息?': '确定要重置订阅信息?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '如果您的订阅地址或信息发生泄露可以执行此操作。重置后您的 UUID 及订阅将会变更,需要重新导入订阅。',
'重置成功': '重置成功',
'两次新密码输入不同': '两次新密码输入不同',
'两次密码输入不同': '两次密码输入不同',
'邮箱': '邮箱',
'邮箱验证码': '邮箱验证码',
'发送': '发送',
'邀请码': '邀请码',
'邀请码(选填)': '邀请码(选填)',
'注册': '注册',
'返回登入': '返回登入',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': '我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>',
'请同意服务条款': '请同意服务条款',
'名称': '名称',
'标签': '标签',
'状态': '状态',
'节点五分钟内节点在线情况': '五分钟内节点在线情况',
'倍率': '倍率',
'使用的流量将乘以倍率进行扣除': '使用的流量将乘以倍率进行扣除',
'更多操作': '更多操作',
'复制成功': '复制成功',
'复制链接': '复制链接',
'该订阅长期有效': '该订阅长期有效',
'已过期': '已过期',
'已用 {used} / 总计 {total}': '已用 {used} / 总计 {total}',
'重置订阅信息': '重置订阅信息',
'没有可用节点,如果您未订阅或已过期请': '没有可用节点,如果您未订阅或已过期请',
'订阅': '订阅',
'确定要重置当月流量?': '确定要重置当月流量?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': '点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。',
'确定': '确定',
'确定要重置订阅信息?': '确定要重置订阅信息?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '如果您的订阅地址或信息发生泄露可以执行此操作。重置后您的 UUID 及订阅将会变更,需要重新导入订阅。',
'重置成功': '重置成功',
'低': '低',
'中': '中',
'高': '高',
'主题': '主题',
'工单级别': '工单级别',
'工单状态': '工单状态',
'最后回复': '最后回复',
'已关闭': '已关闭',
'待回复': '待回复',
'已回复': '已回复',
'查看': '查看',
'关闭': '关闭',
'新的工单': '新的工单',
'新的工单': '新的工单',
'确认': '确认',
'主题': '主题',
'请输入工单主题': '请输入工单主题',
'工单等级': '工单等级',
'请选择工单等级': '请选择工单等级',
'消息': '消息',
'请描述你遇到的问题': '请描述您遇到的问题',
'记录时间': '记录时间',
'实际上行': '实际上行',
'实际下行': '实际下行',
'合计': '合计',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': '公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量',
'复制成功': '复制成功',
'复制订阅地址': '复制订阅地址',
'导入到': '导入到',
'一键订阅': '一键订阅',
'复制订阅': '复制订阅',
'推广佣金划转至余额': '推广佣金划转至余额',
'确认': '确认',
'划转后的余额仅用于{title}消费使用': '划转后的余额仅用于{title}消费使用',
'当前推广佣金余额': '当前推广佣金余额',
'划转金额': '划转金额',
'请输入需要划转到余额的金额': '请输入需要划转到余额的金额',
'输入内容回复工单...': '输入内容回复工单...',
'申请提现': '申请提现',
'确认': '确认',
'取消': '取消',
'提现方式': '提现方式',
'请选择提现方式': '请选择提现方式',
'提现账号': '提现账号',
'请输入提现账号': '请输入提现账号',
'我知道了': '我知道了',
'绑定Telegram': '绑定 Telegram',
'第一步': '第一步',
'第二步': '第二步',
'打开Telegram搜索': '打开 Telegram 搜索',
'向机器人发送你的': '向机器人发送您的',
'使用文档': '使用文档',
'最后更新: {date}': '最后更新: {date}',
'复制成功': '复制成功',
'我的订阅': '我的订阅',
'还有没支付的订单': '还有没支付的订单',
'立即支付': '立即支付',
'条工单正在处理中': '条工单正在处理中',
'立即查看': '立即查看',
'购买订阅': '购买订阅',
'使用文档': '使用文档',
'我的订单': '我的订单',
'流量明细': '流量明细',
'配置订阅': '配置订阅',
'我的邀请': '我的邀请',
'节点状态': '节点状态',
'复制成功': '复制成功',
'商品信息': '商品信息',
'产品名称': '产品名称',
'类型/周期': '类型/周期',
'产品流量': '产品流量',
'订单信息': '订单信息',
'关闭订单': '关闭订单',
'订单号': '订单号',
'优惠金额': '优惠金额',
'旧订阅折抵金额': '旧订阅折抵金额',
'退款金额': '退款金额',
'余额支付': '余额支付',
'我的工单': '我的工单',
'工单历史': '工单历史',
'{reset_day} 日后重置流量': '{reset_day} 日后重置流量',
'节点名称': '节点名称',
'于 {date} 到期,距离到期还有 {day} 天。': '于 {date} 到期,距离到期还有 {day} 天。',
'Telegram 讨论组': 'Telegram 讨论组',
'立即加入': '立即加入',
'续费': '续费',
'购买': '购买',
'该订阅无法续费,仅允许新用户购买': '该订阅无法续费,仅允许新用户购买',
'重置当月流量': '重置当月流量',
'流量明细仅保留近月数据以供查询。': '流量明细仅保留近一个月数据以供查询。',
'扣费倍率': '扣费倍率'
};

View File

@ -0,0 +1,248 @@
window.settings.i18n['zh-TW'] = {
'请求失败': '請求失敗',
'月付': '月繳制',
'季付': '季付',
'半年付': '半年付',
'年付': '年繳',
'两年付': '两年付',
'三年付': '三年付',
'一次性': '一次性',
'重置流量包': '重置流量包',
'待支付': '待支付',
'开通中': '开通中',
'已取消': '已取消',
'已完成': '已完成',
'已折抵': '已折抵',
'待确认': '待確認',
'发放中': '發放中',
'已发放': '已發放',
'无效': '無效',
'个人中心': '您的帳戸',
'登出': '登出',
'搜索': '搜尋',
'仪表盘': '儀表板',
'订阅': '訂閱',
'我的订阅': '我的訂閱',
'购买订阅': '購買訂閱',
'财务': '財務',
'我的订单': '我的訂單',
'我的邀请': '我的邀請',
'用户': '使用者',
'我的工单': '我的工单',
'流量明细': '流量明细',
'使用文档': '說明文件',
'绑定Telegram获取更多服务': '绑定Telegram获取更多服务',
'点击这里进行绑定': '点击这里进行绑定',
'公告': '公告',
'总览': '總覽',
'该订阅长期有效': '该订阅长期有效',
'已过期': '已過期',
'已用 {used} / 总计 {total}': '已用 {used} / 总计 {total}',
'查看订阅': '查看訂閱',
'邮箱': '郵箱',
'邮箱验证码': '邮箱验证码',
'发送': '傳送',
'重置密码': '重設密碼',
'返回登入': '返回登入',
'邀请码': '邀請碼',
'复制链接': '複製鏈接',
'完成时间': '完成時間',
'佣金': '佣金',
'已注册用户数': '已注册用户数',
'佣金比例': '佣金比例',
'确认中的佣金': '确认中的佣金',
'佣金将会在确认后会到达你的佣金账户。': '佣金将会在确认后会到达你的佣金账户。',
'邀请码管理': '邀请码管理',
'生成邀请码': '生成邀请码',
'邀请明细': '邀请明细',
'复制成功': '複製成功',
'密码': '密碼',
'登入': '登入',
'注册': '註冊',
'忘记密码': '忘記密碼',
'# 订单号': '# 订单号',
'周期': '週期',
'订单金额': '訂單金額',
'订单状态': '訂單狀態',
'创建时间': '创建时间',
'操作': '操作',
'查看详情': '查看详情',
'请选择支付方式': '请选择支付方式',
'请检查信用卡支付信息': '请检查信用卡支付信息',
'订单详情': '订单详情',
'折扣': '折扣',
'折抵': '折抵',
'退款': '退款',
'支付方式': '支付方式',
'填写信用卡支付信息': '填写信用卡支付信息',
'您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。': '您的信用卡信息只会被用作当次扣款,系统并不会保存,这是我们认为最安全的。',
'订单总额': '订单总额',
'总计': '总计',
'结账': '结账',
'等待支付中': '等待支付中',
'开通中': '开通中',
'订单系统正在进行处理请稍等1-3分钟。': '订单系统正在进行处理请稍等1-3分钟。',
'已取消': '已取消',
'订单由于超时支付已被取消。': '订单由于超时支付已被取消。',
'已完成': '已完成',
'订单已支付并开通。': '订单已支付并开通。',
'选择订阅': '选择订阅',
'立即订阅': '立即订阅',
'配置订阅': '配置订阅',
'折扣': '折扣',
'付款周期': '付款周期',
'有优惠券?': '有优惠券?',
'验证': '验证',
'订单总额': '订单总额',
'下单': '下单',
'总计': '总计',
'订阅变更须知': '订阅变更须知',
'变更订阅会导致当前订阅被新订阅覆盖,请注意。': '变更订阅会导致当前订阅被新订阅覆盖,请注意。',
'该订阅无法续费': '该订阅无法续费',
'选择其他订阅': '选择其他订阅',
'我的钱包': '我的钱包',
'账户余额(仅消费)': '账户余额(仅消费)',
'推广佣金(可提现)': '推广佣金(可提现)',
'钱包组成部分': '钱包组成部分',
'划转': '划转',
'推广佣金提现': '推广佣金提现',
'修改密码': '修改密码',
'保存': '保存',
'旧密码': '旧密码',
'新密码': '新密码',
'请输入旧密码': '请输入旧密码',
'请输入新密码': '请输入新密码',
'通知': '通知',
'到期邮件提醒': '到期邮件提醒',
'流量邮件提醒': '流量邮件提醒',
'绑定Telegram': '绑定Telegram',
'立即开始': '立即开始',
'重置订阅信息': '重置订阅信息',
'重置': '重置',
'确定要重置订阅信息?': '确定要重置订阅信息?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。',
'重置成功': '重置成功',
'两次新密码输入不同': '两次新密码输入不同',
'两次密码输入不同': '两次密码输入不同',
'邮箱': '郵箱',
'邮箱验证码': '邮箱验证码',
'发送': '发送',
'邀请码': '邀请码',
'邀请码(选填)': '邀请码(选填)',
'注册': '注册',
'返回登入': '返回登入',
'我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>': '我已阅读并同意 <a target="_blank" href="{url}">服务条款</a>',
'请同意服务条款': '请同意服务条款',
'名称': '名称',
'标签': '标签',
'状态': '状态',
'节点五分钟内节点在线情况': '节点五分钟内节点在线情况',
'倍率': '倍率',
'使用的流量将乘以倍率进行扣除': '使用的流量将乘以倍率进行扣除',
'更多操作': '更多操作',
'复制成功': '复制成功',
'复制链接': '复制链接',
'该订阅长期有效': '该订阅长期有效',
'已过期': '已过期',
'已用 {used} / 总计 {total}': '已用 {used} / 总计 {total}',
'重置订阅信息': '重置订阅信息',
'没有可用节点,如果您未订阅或已过期请': '没有可用节点,如果您未订阅或已过期请',
'订阅': '订阅',
'确定要重置当月流量?': '确定要重置当月流量?',
'点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。': '点击「确定」将会跳转到收银台,支付订单后系统将会清空您当月已使用流量。',
'确定': '确定',
'确定要重置订阅信息?': '确定要重置订阅信息?',
'如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。': '如果你的订阅地址或信息泄露可以进行此操作。重置后你的UUID及订阅将会变更需要重新进行订阅。',
'重置成功': '重置成功',
'低': '低',
'中': '中',
'高': '高',
'主题': '主题',
'工单级别': '工单级别',
'工单状态': '工单状态',
'最后回复': '最后回复',
'已关闭': '已关闭',
'待回复': '待回复',
'已回复': '已回复',
'查看': '查看',
'关闭': '关闭',
'新的工单': '新的工单',
'新的工单': '新的工单',
'确认': '确认',
'主题': '主题',
'请输入工单主题': '请输入工单主题',
'工单等级': '工单等级',
'请选择工单等级': '请选择工单等级',
'消息': '消息',
'请描述你遇到的问题': '请描述你遇到的问题',
'记录时间': '记录时间',
'实际上行': '实际上行',
'实际下行': '实际下行',
'合计': '合计',
'公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量': '公式:(实际上行 + 实际下行) x 扣费倍率 = 扣除流量',
'复制成功': '复制成功',
'复制订阅地址': '复制订阅地址',
'导入到': '导入到',
'一键订阅': '一键订阅',
'复制订阅': '复制订阅',
'推广佣金划转至余额': '推广佣金划转至余额',
'确认': '确认',
'划转后的余额仅用于{title}消费使用': '划转后的余额仅用于{title}消费使用',
'当前推广佣金余额': '当前推广佣金余额',
'划转金额': '划转金额',
'请输入需要划转到余额的金额': '请输入需要划转到余额的金额',
'输入内容回复工单...': '输入内容回复工单...',
'申请提现': '申请提现',
'确认': '确认',
'取消': '取消',
'提现方式': '提现方式',
'请选择提现方式': '请选择提现方式',
'提现账号': '提现账号',
'请输入提现账号': '请输入提现账号',
'我知道了': '我知道了',
'绑定Telegram': '绑定Telegram',
'第一步': '第一步',
'第二步': '第二步',
'打开Telegram搜索': '打开Telegram搜索',
'向机器人发送你的': '向机器人发送你的',
'使用文档': '使用文档',
'最后更新: {date}': '最后更新: {date}',
'复制成功': '复制成功',
'我的订阅': '我的订阅',
'还有没支付的订单': '还有没支付的订单',
'立即支付': '立即支付',
'条工单正在处理中': '条工单正在处理中',
'立即查看': '立即查看',
'购买订阅': '购买订阅',
'使用文档': '使用文档',
'我的订单': '我的订单',
'流量明细': '流量明细',
'配置订阅': '配置订阅',
'我的邀请': '我的邀请',
'节点状态': '节点状态',
'复制成功': '复制成功',
'商品信息': '商品信息',
'产品名称': '产品名称',
'类型/周期': '类型/周期',
'产品流量': '产品流量',
'订单信息': '订单信息',
'关闭订单': '关闭订单',
'订单号': '订单号',
'优惠金额': '优惠金额',
'旧订阅折抵金额': '旧订阅折抵金额',
'退款金额': '退款金额',
'余额支付': '余额支付',
'我的工单': '我的工单',
'工单历史': '工单历史',
'{reset_day} 日后重置流量': '{reset_day} 日后重置流量',
'节点名称': '节点名称',
'于 {date} 到期,距离到期还有 {day} 天。': '于 {date} 到期,距离到期还有 {day} 天。',
'Telegram 讨论组': 'Telegram 讨论组',
'立即加入': '立即加入',
'续费': '续费',
'购买': '购买',
'该订阅无法续费,仅允许新用户购买': '该订阅无法续费,仅允许新用户购买',
'重置当月流量': '重置当月流量',
'流量明细仅保留近月数据以供查询。': '流量明细仅保留近月数据以供查询。',
'扣费倍率': '扣费倍率'
};

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

@ -2,9 +2,11 @@
<html>
<head>
<link rel="stylesheet" href="/theme/{{$theme}}/assets/components.chunk.css?v={{$verison}}">
<link rel="stylesheet" href="/theme/{{$theme}}/assets/umi.css?v={{$verison}}">
<link rel="stylesheet" href="/theme/{{$theme}}/assets/custom.css?v={{$verison}}">
<link rel="stylesheet" href="/theme/{{$theme}}/assets/components.chunk.css?v={{$version}}">
<link rel="stylesheet" href="/theme/{{$theme}}/assets/umi.css?v={{$version}}">
@if (file_exists(public_path("/theme/{$theme}/assets/custom.css")))
<link rel="stylesheet" href="/theme/{{$theme}}/assets/custom.css?v={{$version}}">
@endif
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no">
@php ($colors = [
@ -26,24 +28,33 @@
header: '{{$theme_header}}',
color: '{{$theme_color}}',
},
verison: '{{$verison}}',
background_url: '{{$backgroun_url}}',
version: '{{$version}}',
background_url: '{{$background_url}}',
description: '{{$description}}',
crisp_id: '{{$crisp_id}}'
crisp_id: '{{$crisp_id}}',
i18n: [
'zh-CN',
'en-US',
'ja-JP',
'vi-VN',
'ko-KR',
'zh-TW'
]
}
</script>
<script src="/theme/{{$theme}}/assets/i18n.js"></script>
<script src="/theme/{{$theme}}/assets/i18n/zh-CN.js"></script>
<script src="/theme/{{$theme}}/assets/i18n/en-US.js"></script>
<script src="/theme/{{$theme}}/assets/i18n/ja-JP.js"></script>
<script src="/theme/{{$theme}}/assets/i18n/vi-VN.js"></script>
<script src="/theme/{{$theme}}/assets/i18n/zh-CN.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/i18n/zh-TW.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/i18n/en-US.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/i18n/ja-JP.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/i18n/vi-VN.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/i18n/ko-KR.js?v={{$version}}"></script>
</head>
<body>
<div id="root"></div>
<script src="/theme/{{$theme}}/assets/vendors.async.js?v={{$verison}}"></script>
<script src="/theme/{{$theme}}/assets/components.async.js?v={{$verison}}"></script>
<script src="/theme/{{$theme}}/assets/umi.js?v={{$verison}}"></script>
<script src="/theme/{{$theme}}/assets/vendors.async.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/components.async.js?v={{$version}}"></script>
<script src="/theme/{{$theme}}/assets/umi.js?v={{$version}}"></script>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-P1E9Z5LRRK"></script>
<script>
@ -57,6 +68,9 @@
gtag('config', 'G-P1E9Z5LRRK');
</script>
@if (file_exists(public_path("/theme/{$theme}/assets/custom.js")))
<script src="/theme/{{$theme}}/assets/custom.js?v={{$version}}"></script>
@endif
</body>
</html>

View File

@ -37,11 +37,11 @@
"Cancel failed": "取消失败",
"Currency conversion has timed out, please try again later": "货币转换超时,请稍后再试",
"Payment gateway request failed": "支付网关请求失败",
"Oops, there's a problem... Please refresh the page and try again later": "了点问题,请刷新页面稍后再试",
"Oops, there's a problem... Please refresh the page and try again later": "出现了点问题,请刷新页面稍后再试",
"Payment failed. Please check your credit card information": "扣款失败,请检查信用卡信息",
"Article does not exist": "文章不存在",
"No active subscription. Unable to use our provided Apple ID": "无有效订阅,无法使用本站提供的 AppleID",
"You must have a valid subscription to view content in this area": "必须拥有有效的订阅才可以查看该区域的内容",
"You must have a valid subscription to view content in this area": "必须拥有有效的订阅才可以查看该区域的内容",
"The maximum number of creations has been reached": "已达到创建数量上限",
"Coupon cannot be empty": "优惠券不能为空",
"This coupon is no longer available": "优惠券已无可用次数",
@ -56,16 +56,16 @@
"You must use the invitation code to register": "必须使用邀请码才可以注册",
"Email verification code cannot be empty": "邮箱验证码不能为空",
"Incorrect email verification code": "邮箱验证码有误",
"Email already exists": "邮箱已在系统中",
"Email already exists": "邮箱已在系统中存在",
"Invalid invitation code": "邀请码无效",
"Register failed": "注册失败",
"Incorrect email or password": "邮箱或密码错误",
"Your account has been suspended": "该账户已被停止使用",
"Token error": "令牌有误",
"This email is not registered in the system": "该邮箱不存在系统中",
"Email verification code has been sent, please request again later": "验证码已发送,请过一会再请求",
"Email verification code has been sent, please request again later": "验证码已发送,请过一会再请求",
"Email verification code": "邮箱验证码",
"Plan ID cannot be empty": "套餐ID不能为空",
"Plan ID cannot be empty": "套餐 ID 不能为空",
"Plan period cannot be empty": "套餐周期不能为空",
"Wrong plan period": "套餐周期参数有误",
"Ticket subject cannot be empty": "工单主题不能为空",
@ -75,7 +75,7 @@
"The withdrawal account cannot be empty": "提现账号不能为空",
"Old password cannot be empty": "旧密码不能为空",
"New password cannot be empty": "新密码不能为空",
"Password must be greater than 8 digits": "密码必须大于8个字符",
"Password must be greater than 8 digits": "密码必须大于 8 个字符",
"The transfer amount cannot be empty": "划转金额不能为空",
"The transfer amount parameter is wrong": "划转金额参数有误",
"Incorrect format of expiration reminder": "过期提醒参数有误",
@ -83,8 +83,8 @@
"Email can not be empty": "邮箱不能为空",
"Email format is incorrect": "邮箱格式不正确",
"Password can not be empty": "密码不能为空",
"The traffic usage in :app_name has reached 80%": "在:app_name的流量使用已达到80%",
"The service in :app_name is about to expire": "在:app_name的服务即将到期",
"The coupon can only be used :limit_use_with_user per person": "该优惠券每人只能用:limit_use_with_user次",
"The coupon code cannot be used for this period": "该优惠券无法用于这个付款周期"
"The traffic usage in :app_name has reached 80%": "在 :app_name 已用流量已达到 80%",
"The service in :app_name is about to expire": "在 :app_name 的服务即将到期",
"The coupon can only be used :limit_use_with_user per person": "该优惠券每人只能用 :limit_use_with_user 次",
"The coupon code cannot be used for this period": "此优惠券无法用于该付款周期"
}

View File

@ -1 +1 @@
custom.clash.yaml
custom.*

View File

@ -25,8 +25,8 @@ Route::get('/', function (Request $request) {
'theme_sidebar' => config('v2board.frontend_theme_sidebar', 'light'),
'theme_header' => config('v2board.frontend_theme_header', 'dark'),
'theme_color' => config('v2board.frontend_theme_color', 'default'),
'backgroun_url' => config('v2board.frontend_background_url'),
'verison' => config('app.version'),
'background_url' => config('v2board.frontend_background_url'),
'version' => config('app.version'),
'description' => config('v2board.app_description', 'V2Board is best'),
'crisp_id' => config('v2board.frontend_customer_service_method') === 'crisp' ? config('v2board.frontend_customer_service_id') : ''
];

View File

@ -4,4 +4,3 @@ wget https://github.com/composer/composer/releases/latest/download/composer.phar
php composer.phar update -vvv
php artisan v2board:update
php artisan config:cache
pm2 restart pm2.yaml

View File

@ -5,4 +5,3 @@ wget https://github.com/composer/composer/releases/latest/download/composer.phar
php composer.phar update -vvv
php artisan v2board:update
php artisan config:cache
pm2 restart pm2.yaml