feat: add plugin migrations and fix plan management bugs
- Plugin database migration support - Fix empty prices error in plan management - Plugin update functionality - Custom shadowsocks encryption algorithms
This commit is contained in:
@@ -53,9 +53,9 @@ class PluginController extends Controller
|
|||||||
{
|
{
|
||||||
$type = $request->query('type');
|
$type = $request->query('type');
|
||||||
|
|
||||||
$installedPlugins = Plugin::when($type, function($query) use ($type) {
|
$installedPlugins = Plugin::when($type, function ($query) use ($type) {
|
||||||
return $query->byType($type);
|
return $query->byType($type);
|
||||||
})
|
})
|
||||||
->get()
|
->get()
|
||||||
->keyBy('code')
|
->keyBy('code')
|
||||||
->toArray();
|
->toArray();
|
||||||
@@ -84,7 +84,14 @@ class PluginController extends Controller
|
|||||||
->map(fn($f) => $directory . '/' . $f)
|
->map(fn($f) => $directory . '/' . $f)
|
||||||
->first(fn($path) => File::exists($path));
|
->first(fn($path) => File::exists($path));
|
||||||
$readmeContent = $readmeFile ? File::get($readmeFile) : '';
|
$readmeContent = $readmeFile ? File::get($readmeFile) : '';
|
||||||
|
$needUpgrade = false;
|
||||||
|
if ($installed) {
|
||||||
|
$installedVersion = $installedPlugins[$code]['version'] ?? null;
|
||||||
|
$localVersion = $config['version'] ?? null;
|
||||||
|
if ($installedVersion && $localVersion && version_compare($localVersion, $installedVersion, '>')) {
|
||||||
|
$needUpgrade = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
$plugins[] = [
|
$plugins[] = [
|
||||||
'code' => $config['code'],
|
'code' => $config['code'],
|
||||||
'name' => $config['name'],
|
'name' => $config['name'],
|
||||||
@@ -98,6 +105,7 @@ class PluginController extends Controller
|
|||||||
'can_be_deleted' => !in_array($code, Plugin::PROTECTED_PLUGINS),
|
'can_be_deleted' => !in_array($code, Plugin::PROTECTED_PLUGINS),
|
||||||
'config' => $pluginConfig,
|
'config' => $pluginConfig,
|
||||||
'readme' => $readmeContent,
|
'readme' => $readmeContent,
|
||||||
|
'need_upgrade' => $needUpgrade,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -138,8 +146,16 @@ class PluginController extends Controller
|
|||||||
'code' => 'required|string'
|
'code' => 'required|string'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$code = $request->input('code');
|
||||||
|
$plugin = Plugin::where('code', $code)->first();
|
||||||
|
if ($plugin && $plugin->is_enabled) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => '请先禁用插件后再卸载'
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->pluginManager->uninstall($request->input('code'));
|
$this->pluginManager->uninstall($code);
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => '插件卸载成功'
|
'message' => '插件卸载成功'
|
||||||
]);
|
]);
|
||||||
@@ -150,6 +166,26 @@ class PluginController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升级插件
|
||||||
|
*/
|
||||||
|
public function upgrade(Request $request)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'code' => 'required|string',
|
||||||
|
]);
|
||||||
|
try {
|
||||||
|
$this->pluginManager->update($request->input('code'));
|
||||||
|
return response()->json([
|
||||||
|
'message' => '插件升级成功'
|
||||||
|
]);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
return response()->json([
|
||||||
|
'message' => '插件升级失败:' . $e->getMessage()
|
||||||
|
], 400);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 启用插件
|
* 启用插件
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -257,6 +257,7 @@ class AdminRoute
|
|||||||
$router->post('disable', [\App\Http\Controllers\V2\Admin\PluginController::class, 'disable']);
|
$router->post('disable', [\App\Http\Controllers\V2\Admin\PluginController::class, 'disable']);
|
||||||
$router->get('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'getConfig']);
|
$router->get('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'getConfig']);
|
||||||
$router->post('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'updateConfig']);
|
$router->post('config', [\App\Http\Controllers\V2\Admin\PluginController::class, 'updateConfig']);
|
||||||
|
$router->post('upgrade', [\App\Http\Controllers\V2\Admin\PluginController::class, 'upgrade']);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 流量重置管理
|
// 流量重置管理
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ use Illuminate\Support\Facades\Log;
|
|||||||
* @property string $requires
|
* @property string $requires
|
||||||
* @property string $config
|
* @property string $config
|
||||||
* @property string $type
|
* @property string $type
|
||||||
|
* @property boolean $is_enabled
|
||||||
*/
|
*/
|
||||||
class Plugin extends Model
|
class Plugin extends Model
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -137,32 +137,32 @@ class PluginManager
|
|||||||
*/
|
*/
|
||||||
public function install(string $pluginCode): bool
|
public function install(string $pluginCode): bool
|
||||||
{
|
{
|
||||||
|
$configFile = $this->getPluginPath($pluginCode) . '/config.json';
|
||||||
|
|
||||||
|
if (!File::exists($configFile)) {
|
||||||
|
throw new \Exception('Plugin config file not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = json_decode(File::get($configFile), true);
|
||||||
|
if (!$this->validateConfig($config)) {
|
||||||
|
throw new \Exception('Invalid plugin config');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查插件是否已安装
|
||||||
|
if (Plugin::where('code', $pluginCode)->exists()) {
|
||||||
|
throw new \Exception('Plugin already installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查依赖
|
||||||
|
if (!$this->checkDependencies($config['require'] ?? [])) {
|
||||||
|
throw new \Exception('Dependencies not satisfied');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 运行数据库迁移
|
||||||
|
$this->runMigrations(pluginCode: $pluginCode);
|
||||||
|
|
||||||
DB::beginTransaction();
|
DB::beginTransaction();
|
||||||
try {
|
try {
|
||||||
$configFile = $this->getPluginPath($pluginCode) . '/config.json';
|
|
||||||
|
|
||||||
if (!File::exists($configFile)) {
|
|
||||||
throw new \Exception('Plugin config file not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
$config = json_decode(File::get($configFile), true);
|
|
||||||
if (!$this->validateConfig($config)) {
|
|
||||||
throw new \Exception('Invalid plugin config');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查插件是否已安装
|
|
||||||
if (Plugin::where('code', $pluginCode)->exists()) {
|
|
||||||
throw new \Exception('Plugin already installed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查依赖
|
|
||||||
if (!$this->checkDependencies($config['require'] ?? [])) {
|
|
||||||
throw new \Exception('Dependencies not satisfied');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 运行数据库迁移
|
|
||||||
$this->runMigrations($pluginCode);
|
|
||||||
|
|
||||||
// 提取配置默认值
|
// 提取配置默认值
|
||||||
$defaultValues = $this->extractDefaultConfig($config);
|
$defaultValues = $this->extractDefaultConfig($config);
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ class PluginManager
|
|||||||
$plugin = $this->loadPlugin($pluginCode);
|
$plugin = $this->loadPlugin($pluginCode);
|
||||||
|
|
||||||
// 注册到数据库
|
// 注册到数据库
|
||||||
$dbPlugin = Plugin::create([
|
Plugin::create([
|
||||||
'code' => $pluginCode,
|
'code' => $pluginCode,
|
||||||
'name' => $config['name'],
|
'name' => $config['name'],
|
||||||
'version' => $config['version'],
|
'version' => $config['version'],
|
||||||
@@ -191,7 +191,9 @@ class PluginManager
|
|||||||
DB::commit();
|
DB::commit();
|
||||||
return true;
|
return true;
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
DB::rollBack();
|
if (DB::transactionLevel() > 0) {
|
||||||
|
DB::rollBack();
|
||||||
|
}
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +225,22 @@ class PluginManager
|
|||||||
|
|
||||||
if (File::exists($migrationsPath)) {
|
if (File::exists($migrationsPath)) {
|
||||||
Artisan::call('migrate', [
|
Artisan::call('migrate', [
|
||||||
'--path' => "plugins/{$pluginCode}/database/migrations",
|
'--path' => "plugins/" . Str::studly($pluginCode) . "/database/migrations",
|
||||||
|
'--force' => true
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 回滚插件数据库迁移
|
||||||
|
*/
|
||||||
|
protected function runMigrationsRollback(string $pluginCode): void
|
||||||
|
{
|
||||||
|
$migrationsPath = $this->getPluginPath($pluginCode) . '/database/migrations';
|
||||||
|
|
||||||
|
if (File::exists($migrationsPath)) {
|
||||||
|
Artisan::call('migrate:rollback', [
|
||||||
|
'--path' => "plugins/" . Str::studly($pluginCode) . "/database/migrations",
|
||||||
'--force' => true
|
'--force' => true
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -352,10 +369,8 @@ class PluginManager
|
|||||||
*/
|
*/
|
||||||
public function uninstall(string $pluginCode): bool
|
public function uninstall(string $pluginCode): bool
|
||||||
{
|
{
|
||||||
// 先禁用插件
|
|
||||||
$this->disable($pluginCode);
|
$this->disable($pluginCode);
|
||||||
|
$this->runMigrationsRollback($pluginCode);
|
||||||
// 删除数据库记录
|
|
||||||
Plugin::query()->where('code', $pluginCode)->delete();
|
Plugin::query()->where('code', $pluginCode)->delete();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -400,6 +415,62 @@ class PluginManager
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 升级插件
|
||||||
|
*
|
||||||
|
* @param string $pluginCode
|
||||||
|
* @return bool
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function update(string $pluginCode): bool
|
||||||
|
{
|
||||||
|
$dbPlugin = Plugin::where('code', $pluginCode)->first();
|
||||||
|
if (!$dbPlugin) {
|
||||||
|
throw new \Exception('Plugin not installed: ' . $pluginCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取插件配置文件中的最新版本
|
||||||
|
$configFile = $this->getPluginPath($pluginCode) . '/config.json';
|
||||||
|
if (!File::exists($configFile)) {
|
||||||
|
throw new \Exception('Plugin config file not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = json_decode(File::get($configFile), true);
|
||||||
|
if (!$config || !isset($config['version'])) {
|
||||||
|
throw new \Exception('Invalid plugin config or missing version');
|
||||||
|
}
|
||||||
|
|
||||||
|
$newVersion = $config['version'];
|
||||||
|
$oldVersion = $dbPlugin->version;
|
||||||
|
|
||||||
|
if (version_compare($newVersion, $oldVersion, '<=')) {
|
||||||
|
throw new \Exception('Plugin is already up to date');
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->disable($pluginCode);
|
||||||
|
$this->runMigrations($pluginCode);
|
||||||
|
|
||||||
|
$plugin = $this->loadPlugin($pluginCode);
|
||||||
|
if ($plugin) {
|
||||||
|
if (!empty($dbPlugin->config)) {
|
||||||
|
$plugin->setConfig(json_decode($dbPlugin->config, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method_exists($plugin, 'update')) {
|
||||||
|
$plugin->update($oldVersion, $newVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$dbPlugin->update([
|
||||||
|
'version' => $newVersion,
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->enable($pluginCode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 上传插件
|
* 上传插件
|
||||||
*
|
*
|
||||||
@@ -466,6 +537,10 @@ class PluginManager
|
|||||||
File::deleteDirectory($pluginPath);
|
File::deleteDirectory($pluginPath);
|
||||||
File::deleteDirectory($extractPath);
|
File::deleteDirectory($extractPath);
|
||||||
|
|
||||||
|
if (Plugin::where('code', $config['code'])->exists()) {
|
||||||
|
return $this->update($config['code']);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Vendored
+16
-16
File diff suppressed because one or more lines are too long
Vendored
+193
-183
File diff suppressed because one or more lines are too long
@@ -16,7 +16,7 @@
|
|||||||
title: 'Xboard',
|
title: 'Xboard',
|
||||||
version: '1.0.0',
|
version: '1.0.0',
|
||||||
logo: 'https://xboard.io/i6mages/logo.png',
|
logo: 'https://xboard.io/i6mages/logo.png',
|
||||||
secure_path: '/afbced4e',
|
secure_path: '/ea25d015',
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script src="./locales/en-US.js"></script>
|
<script src="./locales/en-US.js"></script>
|
||||||
|
|||||||
Vendored
+33
-1
@@ -196,6 +196,7 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
|
|||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"install": "Install",
|
"install": "Install",
|
||||||
|
"upgrade": "Upgrade",
|
||||||
"config": "Configure",
|
"config": "Configure",
|
||||||
"enable": "Enable",
|
"enable": "Enable",
|
||||||
"disable": "Disable",
|
"disable": "Disable",
|
||||||
@@ -224,6 +225,11 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
|
|||||||
"description": "Are you sure you want to uninstall this plugin? Plugin data will be cleared after uninstallation.",
|
"description": "Are you sure you want to uninstall this plugin? Plugin data will be cleared after uninstallation.",
|
||||||
"button": "Uninstall"
|
"button": "Uninstall"
|
||||||
},
|
},
|
||||||
|
"upgrade": {
|
||||||
|
"title": "Upgrade Plugin",
|
||||||
|
"description": "Are you sure you want to upgrade this plugin? The plugin will be temporarily unavailable during the upgrade process.",
|
||||||
|
"button": "Upgrade"
|
||||||
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"title": "Configuration",
|
"title": "Configuration",
|
||||||
"description": "Modify plugin configuration",
|
"description": "Modify plugin configuration",
|
||||||
@@ -237,6 +243,8 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
|
|||||||
"messages": {
|
"messages": {
|
||||||
"installSuccess": "Plugin installed successfully",
|
"installSuccess": "Plugin installed successfully",
|
||||||
"installError": "Failed to install plugin",
|
"installError": "Failed to install plugin",
|
||||||
|
"upgradeSuccess": "Plugin upgraded successfully",
|
||||||
|
"upgradeError": "Failed to upgrade plugin",
|
||||||
"uninstallSuccess": "Plugin uninstalled successfully",
|
"uninstallSuccess": "Plugin uninstalled successfully",
|
||||||
"uninstallError": "Failed to uninstall plugin",
|
"uninstallError": "Failed to uninstall plugin",
|
||||||
"enableSuccess": "Plugin enabled successfully",
|
"enableSuccess": "Plugin enabled successfully",
|
||||||
@@ -2033,8 +2041,31 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
|
|||||||
"shadowsocks": {
|
"shadowsocks": {
|
||||||
"cipher": {
|
"cipher": {
|
||||||
"label": "Encryption Method",
|
"label": "Encryption Method",
|
||||||
"placeholder": "Select encryption method"
|
"placeholder": "Select encryption method",
|
||||||
|
"search_placeholder": "Search or enter custom encryption method...",
|
||||||
|
"description": "Select preset encryption method or enter custom encryption method",
|
||||||
|
"preset_group": "Preset Encryption Methods",
|
||||||
|
"custom_group": "Custom Encryption Method",
|
||||||
|
"current_value": "Current Value",
|
||||||
|
"use_custom": "Use",
|
||||||
|
"no_results": "No matching encryption method found",
|
||||||
|
"custom_hint": "You can directly enter a custom encryption method, such as: aes-256-cfb",
|
||||||
|
"custom_label": "Custom"
|
||||||
},
|
},
|
||||||
|
"plugin": {
|
||||||
|
"label": "Plugin",
|
||||||
|
"placeholder": "Select plugin",
|
||||||
|
"obfs_hint": "Hint: Configuration format like obfs=http;obfs-host=www.bing.com;path=/",
|
||||||
|
"v2ray_hint": "Hint: WebSocket mode format is mode=websocket;host=mydomain.me;path=/;tls=true, QUIC mode format is mode=quic;host=mydomain.me"
|
||||||
|
},
|
||||||
|
"plugin_opts": {
|
||||||
|
"label": "Plugin Options",
|
||||||
|
"description": "Enter plugin options in key=value;key2=value2 format",
|
||||||
|
"placeholder": "Example: mode=tls;host=bing.com"
|
||||||
|
},
|
||||||
|
"client_fingerprint": "Client Fingerprint",
|
||||||
|
"client_fingerprint_placeholder": "Select client fingerprint",
|
||||||
|
"client_fingerprint_description": "Client spoofing fingerprint to reduce detection risk",
|
||||||
"obfs": {
|
"obfs": {
|
||||||
"label": "Obfuscation",
|
"label": "Obfuscation",
|
||||||
"placeholder": "Select obfuscation method",
|
"placeholder": "Select obfuscation method",
|
||||||
@@ -2676,6 +2707,7 @@ window.XBOARD_TRANSLATIONS['en-US'] = {
|
|||||||
"three_yearly": "Three Years",
|
"three_yearly": "Three Years",
|
||||||
"onetime": "One Time",
|
"onetime": "One Time",
|
||||||
"reset_traffic": "Reset Traffic",
|
"reset_traffic": "Reset Traffic",
|
||||||
|
"no_price": "No Price",
|
||||||
"unit": {
|
"unit": {
|
||||||
"month": "/month",
|
"month": "/month",
|
||||||
"quarter": "/quarter",
|
"quarter": "/quarter",
|
||||||
|
|||||||
Vendored
+24
-370
@@ -162,7 +162,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"orderManagement": "주문 관리",
|
"orderManagement": "주문 관리",
|
||||||
"couponManagement": "쿠폰 관리",
|
"couponManagement": "쿠폰 관리",
|
||||||
"userManagement": "사용자 관리",
|
"userManagement": "사용자 관리",
|
||||||
"trafficResetLogs": "트래픽 재설정 로그",
|
|
||||||
"ticketManagement": "티켓 관리"
|
"ticketManagement": "티켓 관리"
|
||||||
},
|
},
|
||||||
"plugin": {
|
"plugin": {
|
||||||
@@ -414,42 +413,20 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"description": "허용된 이메일 접미사를 한 줄에 하나씩 입력하세요"
|
"description": "허용된 이메일 접미사를 한 줄에 하나씩 입력하세요"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"captcha": {
|
"recaptcha": {
|
||||||
"enable": {
|
"enable": {
|
||||||
"label": "캡차 활성화",
|
"label": "reCAPTCHA 활성화",
|
||||||
"description": "활성화하면 사용자는 등록 시 캡차 인증을 통과해야 합니다."
|
"description": "활성화하면 사용자는 등록 시 reCAPTCHA 인증을 통과해야 합니다."
|
||||||
},
|
},
|
||||||
"type": {
|
"key": {
|
||||||
"label": "캡차 유형",
|
"label": "reCAPTCHA 키",
|
||||||
"description": "사용할 캡차 서비스 유형을 선택하세요",
|
"placeholder": "reCAPTCHA 키 입력",
|
||||||
"options": {
|
"description": "reCAPTCHA 키를 입력하세요"
|
||||||
"recaptcha": "Google reCAPTCHA v2",
|
|
||||||
"turnstile": "Cloudflare Turnstile"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"recaptcha": {
|
"siteKey": {
|
||||||
"key": {
|
"label": "reCAPTCHA 사이트 키",
|
||||||
"label": "reCAPTCHA 키",
|
"placeholder": "reCAPTCHA 사이트 키 입력",
|
||||||
"placeholder": "reCAPTCHA 키 입력",
|
"description": "reCAPTCHA 사이트 키를 입력하세요"
|
||||||
"description": "reCAPTCHA 키를 입력하세요"
|
|
||||||
},
|
|
||||||
"siteKey": {
|
|
||||||
"label": "reCAPTCHA 사이트 키",
|
|
||||||
"placeholder": "reCAPTCHA 사이트 키 입력",
|
|
||||||
"description": "reCAPTCHA 사이트 키를 입력하세요"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"turnstile": {
|
|
||||||
"secretKey": {
|
|
||||||
"label": "Turnstile 키",
|
|
||||||
"placeholder": "Turnstile 키 입력",
|
|
||||||
"description": "Cloudflare Turnstile 키를 입력하세요"
|
|
||||||
},
|
|
||||||
"siteKey": {
|
|
||||||
"label": "Turnstile 사이트 키",
|
|
||||||
"placeholder": "Turnstile 사이트 키 입력",
|
|
||||||
"description": "Cloudflare Turnstile 사이트 키를 입력하세요"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"registerLimit": {
|
"registerLimit": {
|
||||||
@@ -464,8 +441,8 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"expire": {
|
"expire": {
|
||||||
"label": "제한 기간",
|
"label": "제한 기간",
|
||||||
"placeholder": "제한 기간을 분 단위로 입력",
|
"placeholder": "제한 기간을 시간 단위로 입력",
|
||||||
"description": "등록 제한 기간(분)"
|
"description": "등록 제한 기간(시간)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"passwordLimit": {
|
"passwordLimit": {
|
||||||
@@ -480,8 +457,8 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"expire": {
|
"expire": {
|
||||||
"label": "잠금 기간",
|
"label": "잠금 기간",
|
||||||
"placeholder": "잠금 기간을 분 단위로 입력",
|
"placeholder": "잠금 기간을 시간 단위로 입력",
|
||||||
"description": "계정 잠금 기간(분)"
|
"description": "계정 잠금 기간(시간)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -819,10 +796,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"surge": {
|
"surge": {
|
||||||
"title": "Surge 템플릿",
|
"title": "Surge 템플릿",
|
||||||
"description": "Surge의 구독 템플릿 형식 설정"
|
"description": "Surge의 구독 템플릿 형식 설정"
|
||||||
},
|
|
||||||
"surfboard": {
|
|
||||||
"title": "Surfboard 템플릿",
|
|
||||||
"description": "Surfboard의 구독 템플릿 형식 설정"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -887,7 +860,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"save": "저장",
|
"save": "저장",
|
||||||
"cancel": "취소",
|
"cancel": "취소",
|
||||||
"confirm": "확인",
|
"confirm": "확인",
|
||||||
"close": "닫기",
|
|
||||||
"delete": {
|
"delete": {
|
||||||
"success": "삭제되었습니다",
|
"success": "삭제되었습니다",
|
||||||
"failed": "삭제에 실패했습니다"
|
"failed": "삭제에 실패했습니다"
|
||||||
@@ -1050,21 +1022,7 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"result": "결과",
|
"result": "결과",
|
||||||
"duration": "소요 시간",
|
"duration": "소요 시간",
|
||||||
"attempts": "시도 횟수",
|
"attempts": "시도 횟수",
|
||||||
"nextRetry": "다음 재시도",
|
"nextRetry": "다음 재시도"
|
||||||
"failedJobsDetailTitle": "실패한 작업 세부 정보",
|
|
||||||
"viewFailedJobs": "실패한 작업 보기",
|
|
||||||
"jobDetailTitle": "작업 세부 정보",
|
|
||||||
"time": "시간",
|
|
||||||
"queue": "대기열",
|
|
||||||
"name": "작업 이름",
|
|
||||||
"exception": "예외",
|
|
||||||
"noFailedJobs": "실패한 작업 없음",
|
|
||||||
"connection": "연결",
|
|
||||||
"payload": "작업 페이로드",
|
|
||||||
"viewDetail": "세부 정보 보기",
|
|
||||||
"action": "작업",
|
|
||||||
"noRecentOrder": "최근 주문 없음",
|
|
||||||
"viewAll": "모두 보기"
|
|
||||||
},
|
},
|
||||||
"actions": {
|
"actions": {
|
||||||
"retry": "재시도",
|
"retry": "재시도",
|
||||||
@@ -1074,86 +1032,7 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"empty": "대기열에 작업 없음",
|
"empty": "대기열에 작업 없음",
|
||||||
"loading": "대기열 상태 로딩 중...",
|
"loading": "대기열 상태 로딩 중...",
|
||||||
"error": "대기열 상태 로드 실패",
|
"error": "대기열 상태 로드 실패"
|
||||||
"recentOrders": {
|
|
||||||
"title": "최근 주문"
|
|
||||||
},
|
|
||||||
"jobs": {
|
|
||||||
"title": "작업 현황",
|
|
||||||
"failedJobsDetailTitle": "실패한 작업 세부 정보",
|
|
||||||
"viewFailedJobs": "실패한 작업 보기",
|
|
||||||
"jobDetailTitle": "작업 세부 정보",
|
|
||||||
"time": "시간",
|
|
||||||
"queue": "대기열",
|
|
||||||
"name": "작업 이름",
|
|
||||||
"exception": "예외",
|
|
||||||
"noFailedJobs": "실패한 작업 없음",
|
|
||||||
"connection": "연결",
|
|
||||||
"payload": "작업 페이로드",
|
|
||||||
"viewDetail": "세부 정보 보기",
|
|
||||||
"action": "작업"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"systemLog": {
|
|
||||||
"title": "시스템 로그",
|
|
||||||
"description": "시스템 운영 로그 조회",
|
|
||||||
"viewAll": "모두 보기",
|
|
||||||
"level": "레벨",
|
|
||||||
"time": "시간",
|
|
||||||
"message": "메시지",
|
|
||||||
"logTitle": "제목",
|
|
||||||
"method": "요청 방법",
|
|
||||||
"action": "작업",
|
|
||||||
"context": "컨텍스트",
|
|
||||||
"search": "로그 검색...",
|
|
||||||
"noLogs": "로그 없음",
|
|
||||||
"noInfoLogs": "정보 로그 없음",
|
|
||||||
"noWarningLogs": "경고 로그 없음",
|
|
||||||
"noErrorLogs": "오류 로그 없음",
|
|
||||||
"noSearchResults": "일치하는 로그가 없습니다",
|
|
||||||
"detailTitle": "로그 세부 정보",
|
|
||||||
"viewDetail": "세부 정보 보기",
|
|
||||||
"host": "호스트",
|
|
||||||
"ip": "IP 주소",
|
|
||||||
"uri": "URI",
|
|
||||||
"requestData": "요청 데이터",
|
|
||||||
"exception": "예외",
|
|
||||||
"totalLogs": "총 로그 수",
|
|
||||||
"tabs": {
|
|
||||||
"all": "전체",
|
|
||||||
"info": "정보",
|
|
||||||
"warning": "경고",
|
|
||||||
"error": "오류"
|
|
||||||
},
|
|
||||||
"filter": {
|
|
||||||
"searchAndLevel": "필터 결과: \\\"{{keyword}}\\\"를 포함하고 레벨이 \\\"{{level}}\\\"인 로그 {{count}}개",
|
|
||||||
"searchOnly": "검색 결과: \\\"{{keyword}}\\\"를 포함하는 로그 {{count}}개",
|
|
||||||
"levelOnly": "필터 결과: 레벨이 \\\"{{level}}\\\"인 로그 {{count}}개",
|
|
||||||
"reset": "필터 초기화"
|
|
||||||
},
|
|
||||||
"clearLogs": "로그 삭제",
|
|
||||||
"clearDays": "삭제 일수",
|
|
||||||
"clearDaysDesc": "며칠 전 로그를 삭제할지 (0-365일, 0은 오늘)",
|
|
||||||
"clearLevel": "로그 레벨",
|
|
||||||
"clearLimit": "배치 제한",
|
|
||||||
"clearLimitDesc": "배치 삭제 수량 제한 (100-10000건)",
|
|
||||||
"clearPreview": "삭제 미리보기",
|
|
||||||
"getStats": "통계 가져오기",
|
|
||||||
"cutoffDate": "마감일",
|
|
||||||
"willClear": "삭제 예정",
|
|
||||||
"logsUnit": "개 로그",
|
|
||||||
"clearWarning": "이 작업은 되돌릴 수 없습니다. 신중하게 진행해 주세요!",
|
|
||||||
"clearing": "삭제 중...",
|
|
||||||
"confirmClear": "삭제 확인",
|
|
||||||
"clearSuccess": "삭제 완료! {{count}}개 로그 삭제됨",
|
|
||||||
"clearFailed": "삭제 실패",
|
|
||||||
"getStatsFailed": "삭제 통계 가져오기 실패",
|
|
||||||
"clearLogsFailed": "로그 삭제 실패"
|
|
||||||
},
|
|
||||||
"common": {
|
|
||||||
"refresh": "새로고침",
|
|
||||||
"close": "닫기",
|
|
||||||
"pagination": "{{current}}/{{total}} 페이지, 총 {{count}}개 항목"
|
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"placeholder": "메뉴 및 기능 검색...",
|
"placeholder": "메뉴 및 기능 검색...",
|
||||||
@@ -1273,8 +1152,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"basicInfo": "기본 정보",
|
"basicInfo": "기본 정보",
|
||||||
"amountInfo": "금액 정보",
|
"amountInfo": "금액 정보",
|
||||||
"timeInfo": "시간 정보",
|
"timeInfo": "시간 정보",
|
||||||
"commissionInfo": "수수료 정보",
|
|
||||||
"commissionStatusActive": "활성",
|
|
||||||
"addOrder": "주문 추가",
|
"addOrder": "주문 추가",
|
||||||
"assignOrder": "주문 할당",
|
"assignOrder": "주문 할당",
|
||||||
"fields": {
|
"fields": {
|
||||||
@@ -1288,12 +1165,7 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"refundAmount": "환불 금액",
|
"refundAmount": "환불 금액",
|
||||||
"deductionAmount": "차감 금액",
|
"deductionAmount": "차감 금액",
|
||||||
"createdAt": "생성 시간",
|
"createdAt": "생성 시간",
|
||||||
"updatedAt": "업데이트 시간",
|
"updatedAt": "업데이트 시간"
|
||||||
"commissionStatus": "수수료 상태",
|
|
||||||
"commissionAmount": "수수료 금액",
|
|
||||||
"actualCommissionAmount": "실제 수수료",
|
|
||||||
"inviteUser": "초대자",
|
|
||||||
"inviteUserId": "초대자 ID"
|
|
||||||
},
|
},
|
||||||
"placeholders": {
|
"placeholders": {
|
||||||
"email": "사용자 이메일을 입력해주세요",
|
"email": "사용자 이메일을 입력해주세요",
|
||||||
@@ -1410,17 +1282,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"saveFailed": "쿠폰 저장 실패"
|
"saveFailed": "쿠폰 저장 실패"
|
||||||
},
|
|
||||||
"timeRange": {
|
|
||||||
"quickSet": "빠른 설정",
|
|
||||||
"presets": {
|
|
||||||
"1week": "1주",
|
|
||||||
"2weeks": "2주",
|
|
||||||
"1month": "1개월",
|
|
||||||
"3months": "3개월",
|
|
||||||
"6months": "6개월",
|
|
||||||
"1year": "1년"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"period": {
|
"period": {
|
||||||
@@ -1645,17 +1506,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"tooltip": "이 노드를 구독할 수 있는 그룹",
|
"tooltip": "이 노드를 구독할 수 있는 그룹",
|
||||||
"empty": "--"
|
"empty": "--"
|
||||||
},
|
},
|
||||||
"loadStatus": {
|
|
||||||
"title": "부하 상태",
|
|
||||||
"tooltip": "서버 리소스 사용량",
|
|
||||||
"noData": "데이터 없음",
|
|
||||||
"details": "시스템 부하 세부정보",
|
|
||||||
"cpu": "CPU 사용률",
|
|
||||||
"memory": "메모리 사용량",
|
|
||||||
"swap": "스왑 사용량",
|
|
||||||
"disk": "디스크 사용량",
|
|
||||||
"lastUpdate": "마지막 업데이트"
|
|
||||||
},
|
|
||||||
"type": "유형",
|
"type": "유형",
|
||||||
"actions": "작업",
|
"actions": "작업",
|
||||||
"copyAddress": "연결 주소 복사",
|
"copyAddress": "연결 주소 복사",
|
||||||
@@ -1698,9 +1548,7 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"rate": {
|
"rate": {
|
||||||
"label": "요금",
|
"label": "요금",
|
||||||
"error": "요금은 필수입니다",
|
"error": "올바른 요금을 입력해주세요"
|
||||||
"error_numeric": "요금은 숫자여야 합니다",
|
|
||||||
"error_gte_zero": "요금은 0보다 크거나 같아야 합니다"
|
|
||||||
},
|
},
|
||||||
"code": {
|
"code": {
|
||||||
"label": "사용자 지정 노드 ID",
|
"label": "사용자 지정 노드 ID",
|
||||||
@@ -1719,21 +1567,18 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
},
|
},
|
||||||
"host": {
|
"host": {
|
||||||
"label": "노드 주소",
|
"label": "노드 주소",
|
||||||
"placeholder": "도메인 또는 IP를 입력해주세요",
|
"placeholder": "도메인 또는 IP를 입력해주세요"
|
||||||
"error": "노드 주소는 필수입니다"
|
|
||||||
},
|
},
|
||||||
"port": {
|
"port": {
|
||||||
"label": "연결 포트",
|
"label": "연결 포트",
|
||||||
"placeholder": "사용자 연결 포트",
|
"placeholder": "사용자 연결 포트",
|
||||||
"tooltip": "사용자가 실제로 연결하는 포트로, 클라이언트 설정에 입력해야 하는 포트 번호입니다. 중계 또는 터널을 사용하는 경우 서버가 실제로 수신하는 포트와 다를 수 있습니다.",
|
"tooltip": "사용자가 실제로 연결하는 포트로, 클라이언트 설정에 입력해야 하는 포트 번호입니다. 중계 또는 터널을 사용하는 경우 서버가 실제로 수신하는 포트와 다를 수 있습니다.",
|
||||||
"sync": "서버 포트와 동기화",
|
"sync": "서버 포트와 동기화"
|
||||||
"error": "연결 포트는 필수입니다"
|
|
||||||
},
|
},
|
||||||
"server_port": {
|
"server_port": {
|
||||||
"label": "서버 포트",
|
"label": "서버 포트",
|
||||||
"placeholder": "서버 포트 입력",
|
"placeholder": "서버 수신 포트",
|
||||||
"error": "서버 포트는 필수입니다",
|
"tooltip": "서버가 실제로 수신하는 포트로, 서버에서 실제로 열린 포트입니다. 중계 또는 터널을 사용하는 경우 사용자 연결 포트와 다를 수 있습니다."
|
||||||
"tooltip": "서버의 실제 수신 포트입니다."
|
|
||||||
},
|
},
|
||||||
"parent": {
|
"parent": {
|
||||||
"label": "상위 노드",
|
"label": "상위 노드",
|
||||||
@@ -1895,83 +1740,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"empty": "사용 가능한 ALPN 프로토콜이 없습니다"
|
"empty": "사용 가능한 ALPN 프로토콜이 없습니다"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"socks": {
|
|
||||||
"version": {
|
|
||||||
"label": "프로토콜 버전",
|
|
||||||
"placeholder": "SOCKS 버전 선택"
|
|
||||||
},
|
|
||||||
"tls": {
|
|
||||||
"label": "TLS",
|
|
||||||
"placeholder": "보안을 선택해주세요",
|
|
||||||
"disabled": "비활성화",
|
|
||||||
"enabled": "활성화"
|
|
||||||
},
|
|
||||||
"tls_settings": {
|
|
||||||
"server_name": {
|
|
||||||
"label": "서버 이름 표시(SNI)",
|
|
||||||
"placeholder": "사용하지 않는 경우 비워두세요"
|
|
||||||
},
|
|
||||||
"allow_insecure": "안전하지 않은 연결 허용?"
|
|
||||||
},
|
|
||||||
"network": {
|
|
||||||
"label": "전송 프로토콜",
|
|
||||||
"placeholder": "전송 프로토콜 선택"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"naive": {
|
|
||||||
"tls_settings": {
|
|
||||||
"server_name": {
|
|
||||||
"label": "서버 이름 표시(SNI)",
|
|
||||||
"placeholder": "사용하지 않는 경우 비워두세요"
|
|
||||||
},
|
|
||||||
"allow_insecure": "안전하지 않은 연결 허용?"
|
|
||||||
},
|
|
||||||
"tls": {
|
|
||||||
"label": "TLS",
|
|
||||||
"placeholder": "보안을 선택해주세요",
|
|
||||||
"disabled": "비활성화",
|
|
||||||
"enabled": "활성화",
|
|
||||||
"server_name": {
|
|
||||||
"label": "서버 이름 표시(SNI)",
|
|
||||||
"placeholder": "노드 주소와 인증서가 다를 때 인증서 확인에 사용"
|
|
||||||
},
|
|
||||||
"allow_insecure": "안전하지 않은 연결 허용"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"http": {
|
|
||||||
"tls_settings": {
|
|
||||||
"server_name": {
|
|
||||||
"label": "서버 이름 표시(SNI)",
|
|
||||||
"placeholder": "사용하지 않는 경우 비워두세요"
|
|
||||||
},
|
|
||||||
"allow_insecure": "안전하지 않은 연결 허용?"
|
|
||||||
},
|
|
||||||
"tls": {
|
|
||||||
"label": "TLS",
|
|
||||||
"placeholder": "보안을 선택해주세요",
|
|
||||||
"disabled": "비활성화",
|
|
||||||
"enabled": "활성화",
|
|
||||||
"server_name": {
|
|
||||||
"label": "서버 이름 표시(SNI)",
|
|
||||||
"placeholder": "노드 주소와 인증서가 다를 때 인증서 확인에 사용"
|
|
||||||
},
|
|
||||||
"allow_insecure": "안전하지 않은 연결 허용"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"mieru": {
|
|
||||||
"transport": {
|
|
||||||
"label": "전송 프로토콜",
|
|
||||||
"placeholder": "전송 프로토콜 선택"
|
|
||||||
},
|
|
||||||
"multiplexing": {
|
|
||||||
"label": "다중화",
|
|
||||||
"placeholder": "다중화 수준 선택",
|
|
||||||
"MULTIPLEXING_OFF": "비활성화",
|
|
||||||
"MULTIPLEXING_LOW": "낮음",
|
|
||||||
"MULTIPLEXING_MIDDLE": "중간",
|
|
||||||
"MULTIPLEXING_HIGH": "높음"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -2028,7 +1796,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"orders": "주문 내역",
|
"orders": "주문 내역",
|
||||||
"invites": "초대 내역",
|
"invites": "초대 내역",
|
||||||
"traffic_records": "트래픽 기록",
|
"traffic_records": "트래픽 기록",
|
||||||
"reset_traffic": "트래픽 재설정",
|
|
||||||
"delete": "삭제",
|
"delete": "삭제",
|
||||||
"delete_confirm_title": "사용자 삭제 확인",
|
"delete_confirm_title": "사용자 삭제 확인",
|
||||||
"delete_confirm_description": "이 작업은 사용자 {{email}}와 관련된 모든 데이터(주문, 쿠폰, 트래픽 기록, 지원 티켓 등)를 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다. 계속하시겠습니까?"
|
"delete_confirm_description": "이 작업은 사용자 {{email}}와 관련된 모든 데이터(주문, 쿠폰, 트래픽 기록, 지원 티켓 등)를 영구적으로 삭제합니다. 이 작업은 취소할 수 없습니다. 계속하시겠습니까?"
|
||||||
@@ -2105,8 +1872,7 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"generate_count_placeholder": "일괄 생성할 수량 입력",
|
"generate_count_placeholder": "일괄 생성할 수량 입력",
|
||||||
"cancel": "취소",
|
"cancel": "취소",
|
||||||
"submit": "생성",
|
"submit": "생성",
|
||||||
"success": "생성 완료",
|
"success": "생성 완료"
|
||||||
"download_csv": "CSV 파일로 내보내기"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"edit": {
|
"edit": {
|
||||||
@@ -2165,7 +1931,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"title": "작업",
|
"title": "작업",
|
||||||
"send_email": "이메일 보내기",
|
"send_email": "이메일 보내기",
|
||||||
"export_csv": "CSV 내보내기",
|
"export_csv": "CSV 내보내기",
|
||||||
"traffic_reset_stats": "트래픽 재설정 통계",
|
|
||||||
"batch_ban": "일괄 차단",
|
"batch_ban": "일괄 차단",
|
||||||
"confirm_ban": {
|
"confirm_ban": {
|
||||||
"title": "일괄 차단 확인",
|
"title": "일괄 차단 확인",
|
||||||
@@ -2193,117 +1958,6 @@ window.XBOARD_TRANSLATIONS['ko-KR'] = {
|
|||||||
"required_fields": "모든 필수 항목을 입력해주세요"
|
"required_fields": "모든 필수 항목을 입력해주세요"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"traffic_reset": {
|
|
||||||
"title": "트래픽 재설정",
|
|
||||||
"description": "사용자 {{email}}의 트래픽 사용량 재설정",
|
|
||||||
"tabs": {
|
|
||||||
"reset": "트래픽 재설정",
|
|
||||||
"history": "재설정 기록"
|
|
||||||
},
|
|
||||||
"user_info": "사용자 정보",
|
|
||||||
"warning": {
|
|
||||||
"title": "중요 안내",
|
|
||||||
"irreversible": "트래픽 재설정 작업은 되돌릴 수 없습니다. 신중하게 진행해주세요",
|
|
||||||
"reset_to_zero": "재설정 후 사용자의 업로드 및 다운로드 트래픽이 0으로 초기화됩니다",
|
|
||||||
"logged": "모든 재설정 작업은 시스템 로그에 기록됩니다"
|
|
||||||
},
|
|
||||||
"reason": {
|
|
||||||
"label": "재설정 사유",
|
|
||||||
"placeholder": "트래픽 재설정 사유를 입력해주세요 (선택사항)",
|
|
||||||
"optional": "이 필드는 선택사항이며 재설정 사유를 기록하는 데 사용됩니다"
|
|
||||||
},
|
|
||||||
"confirm_reset": "재설정 확인",
|
|
||||||
"resetting": "재설정 중...",
|
|
||||||
"reset_success": "트래픽 재설정 성공",
|
|
||||||
"reset_failed": "트래픽 재설정 실패",
|
|
||||||
"history": {
|
|
||||||
"summary": "재설정 개요",
|
|
||||||
"reset_count": "재설정 횟수",
|
|
||||||
"last_reset": "마지막 재설정",
|
|
||||||
"next_reset": "다음 재설정",
|
|
||||||
"never": "재설정된 적 없음",
|
|
||||||
"no_schedule": "예약된 재설정 없음",
|
|
||||||
"records": "재설정 기록",
|
|
||||||
"recent_records": "최근 10번의 재설정 기록",
|
|
||||||
"no_records": "재설정 기록이 없습니다",
|
|
||||||
"reset_time": "재설정 시간",
|
|
||||||
"traffic_cleared": "삭제된 트래픽"
|
|
||||||
},
|
|
||||||
"stats": {
|
|
||||||
"title": "트래픽 재설정 통계",
|
|
||||||
"description": "시스템 트래픽 재설정 통계 정보 보기",
|
|
||||||
"time_range": "통계 시간 범위",
|
|
||||||
"total_resets": "총 재설정 횟수",
|
|
||||||
"auto_resets": "자동 재설정",
|
|
||||||
"manual_resets": "수동 재설정",
|
|
||||||
"cron_resets": "예약 재설정",
|
|
||||||
"in_period": "최근 {{days}}일",
|
|
||||||
"breakdown": "재설정 유형별 분석",
|
|
||||||
"breakdown_description": "다양한 재설정 작업 유형의 백분율 분석",
|
|
||||||
"auto_percentage": "자동 재설정 비율",
|
|
||||||
"manual_percentage": "수동 재설정 비율",
|
|
||||||
"cron_percentage": "예약 재설정 비율",
|
|
||||||
"days_options": {
|
|
||||||
"week": "지난 주",
|
|
||||||
"month": "지난 달",
|
|
||||||
"quarter": "지난 분기",
|
|
||||||
"year": "지난 해"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"traffic_reset_logs": {
|
|
||||||
"title": "트래픽 재설정 로그",
|
|
||||||
"description": "시스템의 모든 트래픽 재설정 작업에 대한 상세 기록 보기",
|
|
||||||
"columns": {
|
|
||||||
"id": "로그 ID",
|
|
||||||
"user": "사용자",
|
|
||||||
"reset_type": "재설정 유형",
|
|
||||||
"trigger_source": "트리거 소스",
|
|
||||||
"cleared_traffic": "삭제된 트래픽",
|
|
||||||
"cleared": "삭제됨",
|
|
||||||
"upload": "업로드",
|
|
||||||
"download": "다운로드",
|
|
||||||
"reset_time": "재설정 시간",
|
|
||||||
"log_time": "로그 시간"
|
|
||||||
},
|
|
||||||
"filters": {
|
|
||||||
"search_user": "사용자 이메일 검색...",
|
|
||||||
"reset_type": "재설정 유형",
|
|
||||||
"trigger_source": "트리거 소스",
|
|
||||||
"all_types": "모든 유형",
|
|
||||||
"all_sources": "모든 소스",
|
|
||||||
"start_date": "시작 날짜",
|
|
||||||
"end_date": "종료 날짜",
|
|
||||||
"apply_date": "필터 적용",
|
|
||||||
"reset": "필터 초기화",
|
|
||||||
"filter_title": "필터 옵션",
|
|
||||||
"filter_description": "특정 트래픽 재설정 기록을 찾기 위한 필터 조건을 설정하세요",
|
|
||||||
"reset_types": {
|
|
||||||
"monthly": "월별 재설정",
|
|
||||||
"first_day_month": "매월 1일 재설정",
|
|
||||||
"yearly": "연별 재설정",
|
|
||||||
"first_day_year": "매년 1월 1일 재설정",
|
|
||||||
"manual": "수동 재설정"
|
|
||||||
},
|
|
||||||
"trigger_sources": {
|
|
||||||
"auto": "자동 트리거",
|
|
||||||
"manual": "수동 트리거",
|
|
||||||
"cron": "예약 작업"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"actions": {
|
|
||||||
"export": "로그 내보내기",
|
|
||||||
"exporting": "내보내는 중...",
|
|
||||||
"export_success": "내보내기 성공",
|
|
||||||
"export_failed": "내보내기 실패"
|
|
||||||
},
|
|
||||||
"trigger_descriptions": {
|
|
||||||
"manual": "관리자가 수동으로 실행한 트래픽 재설정",
|
|
||||||
"cron": "시스템 예약 작업에 의한 자동 실행",
|
|
||||||
"auto": "시스템이 조건에 따라 자동 트리거",
|
|
||||||
"other": "기타 방법으로 트리거"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"send_mail": {
|
"send_mail": {
|
||||||
"title": "이메일 보내기",
|
"title": "이메일 보내기",
|
||||||
"description": "선택하거나 필터링된 사용자에게 이메일 보내기",
|
"description": "선택하거나 필터링된 사용자에게 이메일 보내기",
|
||||||
|
|||||||
Vendored
+33
-1
@@ -196,6 +196,7 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
|
|||||||
},
|
},
|
||||||
"button": {
|
"button": {
|
||||||
"install": "安装",
|
"install": "安装",
|
||||||
|
"upgrade": "升级",
|
||||||
"config": "配置",
|
"config": "配置",
|
||||||
"enable": "启用",
|
"enable": "启用",
|
||||||
"disable": "禁用",
|
"disable": "禁用",
|
||||||
@@ -224,6 +225,11 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
|
|||||||
"description": "确定要卸载此插件吗?卸载后插件数据将被清除。",
|
"description": "确定要卸载此插件吗?卸载后插件数据将被清除。",
|
||||||
"button": "卸载"
|
"button": "卸载"
|
||||||
},
|
},
|
||||||
|
"upgrade": {
|
||||||
|
"title": "升级插件",
|
||||||
|
"description": "确定要升级此插件吗?升级过程中插件将暂时不可用。",
|
||||||
|
"button": "升级"
|
||||||
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"title": "配置",
|
"title": "配置",
|
||||||
"description": "修改插件配置",
|
"description": "修改插件配置",
|
||||||
@@ -237,6 +243,8 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
|
|||||||
"messages": {
|
"messages": {
|
||||||
"installSuccess": "插件安装成功",
|
"installSuccess": "插件安装成功",
|
||||||
"installError": "插件安装失败",
|
"installError": "插件安装失败",
|
||||||
|
"upgradeSuccess": "插件升级成功",
|
||||||
|
"upgradeError": "插件升级失败",
|
||||||
"uninstallSuccess": "插件卸载成功",
|
"uninstallSuccess": "插件卸载成功",
|
||||||
"uninstallError": "插件卸载失败",
|
"uninstallError": "插件卸载失败",
|
||||||
"enableSuccess": "插件启用成功",
|
"enableSuccess": "插件启用成功",
|
||||||
@@ -2106,8 +2114,31 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
|
|||||||
"shadowsocks": {
|
"shadowsocks": {
|
||||||
"cipher": {
|
"cipher": {
|
||||||
"label": "加密算法",
|
"label": "加密算法",
|
||||||
"placeholder": "选择加密算法"
|
"placeholder": "选择加密算法",
|
||||||
|
"search_placeholder": "搜索或输入自定义加密方式...",
|
||||||
|
"description": "选择预设加密方式或输入自定义加密方式",
|
||||||
|
"preset_group": "预设加密方式",
|
||||||
|
"custom_group": "自定义加密方式",
|
||||||
|
"current_value": "当前值",
|
||||||
|
"use_custom": "使用",
|
||||||
|
"no_results": "未找到匹配的加密方式",
|
||||||
|
"custom_hint": "你可以直接输入自定义的加密方式,如:aes-256-cfb",
|
||||||
|
"custom_label": "自定义"
|
||||||
},
|
},
|
||||||
|
"plugin": {
|
||||||
|
"label": "插件",
|
||||||
|
"placeholder": "选择插件",
|
||||||
|
"obfs_hint": "提示:配置格式如 obfs=http;obfs-host=www.bing.com;path=/",
|
||||||
|
"v2ray_hint": "提示:WebSocket模式格式为 mode=websocket;host=mydomain.me;path=/;tls=true,QUIC模式格式为 mode=quic;host=mydomain.me"
|
||||||
|
},
|
||||||
|
"plugin_opts": {
|
||||||
|
"label": "插件选项",
|
||||||
|
"description": "按照 key=value;key2=value2 格式输入插件选项",
|
||||||
|
"placeholder": "例如: mode=tls;host=bing.com"
|
||||||
|
},
|
||||||
|
"client_fingerprint": "客户端指纹",
|
||||||
|
"client_fingerprint_placeholder": "选择客户端指纹",
|
||||||
|
"client_fingerprint_description": "客户端伪装指纹,用于降低被识别风险",
|
||||||
"obfs": {
|
"obfs": {
|
||||||
"label": "混淆",
|
"label": "混淆",
|
||||||
"placeholder": "选择混淆方式",
|
"placeholder": "选择混淆方式",
|
||||||
@@ -2749,6 +2780,7 @@ window.XBOARD_TRANSLATIONS['zh-CN'] = {
|
|||||||
"three_yearly": "三年付",
|
"three_yearly": "三年付",
|
||||||
"onetime": "流量包",
|
"onetime": "流量包",
|
||||||
"reset_traffic": "重置包",
|
"reset_traffic": "重置包",
|
||||||
|
"no_price": "无价格",
|
||||||
"unit": {
|
"unit": {
|
||||||
"month": "元/月",
|
"month": "元/月",
|
||||||
"quarter": "元/季",
|
"quarter": "元/季",
|
||||||
|
|||||||
Reference in New Issue
Block a user