feat(admin): Add subscription template configuration and fix minor issues

This commit is contained in:
xboard
2025-02-16 18:05:03 +08:00
parent 30a140f2d8
commit 8098cf3ee2
14 changed files with 682 additions and 499 deletions
+2 -2
View File
@@ -202,7 +202,7 @@ class XboardInstall extends Command
$this->info(Artisan::output());
$this->info('数据库导入完成');
$this->info('开始注册管理员账号');
if (!$this->registerAdmin($email, $password)) {
if (!self::registerAdmin($email, $password)) {
abort(500, '管理员账号注册失败,请重试');
}
$this->info('🎉:一切就绪');
@@ -218,7 +218,7 @@ class XboardInstall extends Command
}
}
public function registerAdmin($email, $password)
public static function registerAdmin($email, $password)
{
$user = new User();
$user->email = $email;
@@ -10,6 +10,7 @@ use App\Services\TelegramService;
use App\Services\ThemeService;
use App\Utils\Dict;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
class ConfigController extends Controller
{
@@ -63,6 +64,12 @@ class ConfigController extends Controller
return $this->success(true);
}
private function getTemplateContent(string $filename): string
{
$path = resource_path("rules/{$filename}");
return File::exists($path) ? File::get($path) : '';
}
public function fetch(Request $request)
{
$key = $request->input('key');
@@ -162,6 +169,47 @@ class ConfigController extends Controller
'password_limit_enable' => (bool) admin_setting('password_limit_enable', 1),
'password_limit_count' => admin_setting('password_limit_count', 5),
'password_limit_expire' => admin_setting('password_limit_expire', 60)
],
'subscribe_template' => [
'subscribe_template_singbox' => (function () {
$template = admin_setting('subscribe_template_singbox');
if (!empty($template)) {
return is_array($template)
? json_encode($template, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
: $template;
}
$content = file_exists(base_path('resources/rules/custom.sing-box.json'))
? file_get_contents(base_path('resources/rules/custom.sing-box.json'))
: file_get_contents(base_path('resources/rules/default.sing-box.json'));
// 确保返回格式化的 JSON 字符串
return json_encode(json_decode($content), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
})(),
'subscribe_template_clash' => (string) (admin_setting('subscribe_template_clash') ?: (
file_exists(base_path('resources/rules/custom.clash.yaml'))
? file_get_contents(base_path('resources/rules/custom.clash.yaml'))
: file_get_contents(base_path('resources/rules/default.clash.yaml'))
)),
'subscribe_template_clashmeta' => (string) (admin_setting('subscribe_template_clashmeta') ?: (
file_exists(base_path('resources/rules/custom.clashmeta.yaml'))
? file_get_contents(base_path('resources/rules/custom.clashmeta.yaml'))
: (file_exists(base_path('resources/rules/custom.clash.yaml'))
? file_get_contents(base_path('resources/rules/custom.clash.yaml'))
: file_get_contents(base_path('resources/rules/default.clash.yaml')))
)),
'subscribe_template_stash' => (string) (admin_setting('subscribe_template_stash') ?: (
file_exists(base_path('resources/rules/custom.stash.yaml'))
? file_get_contents(base_path('resources/rules/custom.stash.yaml'))
: (file_exists(base_path('resources/rules/custom.clash.yaml'))
? file_get_contents(base_path('resources/rules/custom.clash.yaml'))
: file_get_contents(base_path('resources/rules/default.clash.yaml')))
)),
'subscribe_template_surge' => (string) (admin_setting('subscribe_template_surge') ?: (
file_exists(base_path('resources/rules/custom.surge.conf'))
? file_get_contents(base_path('resources/rules/custom.surge.conf'))
: file_get_contents(base_path('resources/rules/default.surge.conf'))
)),
]
];
if ($key && isset($data[$key])) {
@@ -179,7 +227,7 @@ class ConfigController extends Controller
$data = $request->validated();
foreach ($data as $k => $v) {
if ($k == 'frontend_theme') {
$themeService = new ThemeService();
$themeService = app(ThemeService::class);
$themeService->switch($v);
}
admin_setting([$k => $v]);
+6 -1
View File
@@ -95,7 +95,12 @@ class ConfigSave extends FormRequest
'password_limit_count' => 'integer',
'password_limit_expire' => 'integer',
'default_remind_expire' => 'boolean',
'default_remind_traffic' => 'boolean'
'default_remind_traffic' => 'boolean',
'subscribe_template_singbox' => 'nullable',
'subscribe_template_clash' => 'nullable',
'subscribe_template_clashmeta' => 'nullable',
'subscribe_template_stash' => 'nullable',
'subscribe_template_surge' => 'nullable',
];
/**
* Get the validation rules that apply to the request.
+13 -6
View File
@@ -28,13 +28,20 @@ class Clash implements ProtocolInterface
$servers = $this->servers;
$user = $this->user;
$appName = admin_setting('app_name', 'XBoard');
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customConfig = base_path() . '/resources/rules/custom.clash.yaml';
if (\File::exists($customConfig)) {
$config = Yaml::parseFile($customConfig);
} else {
$config = Yaml::parseFile($defaultConfig);
// 优先从 admin_setting 获取模板
$template = admin_setting('subscribe_template_clash');
if (empty($template)) {
$defaultConfig = base_path('resources/rules/default.clash.yaml');
$customConfig = base_path('resources/rules/custom.clash.yaml');
if (file_exists($customConfig)) {
$template = file_get_contents($customConfig);
} else {
$template = file_get_contents($defaultConfig);
}
}
$config = Yaml::parse($template);
$proxy = [];
$proxies = [];
+16 -9
View File
@@ -29,16 +29,23 @@ class ClashMeta implements ProtocolInterface
$servers = $this->servers;
$user = $this->user;
$appName = admin_setting('app_name', 'XBoard');
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customClashConfig = base_path() . '/resources/rules/custom.clash.yaml';
$customConfig = base_path() . '/resources/rules/custom.clashmeta.yaml';
if (\File::exists($customConfig)) {
$config = Yaml::parseFile($customConfig);
} elseif (\File::exists($customClashConfig)) {
$config = Yaml::parseFile($customClashConfig);
} else {
$config = Yaml::parseFile($defaultConfig);
// 优先从 admin_setting 获取模板
$template = admin_setting('subscribe_template_clashmeta');
if (empty($template)) {
$defaultConfig = base_path('resources/rules/default.clash.yaml');
$customClashConfig = base_path('resources/rules/custom.clash.yaml');
$customConfig = base_path('resources/rules/custom.clashmeta.yaml');
if (file_exists($customConfig)) {
$template = file_get_contents($customConfig);
} elseif (file_exists($customClashConfig)) {
$template = file_get_contents($customClashConfig);
} else {
$template = file_get_contents($defaultConfig);
}
}
$config = Yaml::parse($template);
$proxy = [];
$proxies = [];
+6
View File
@@ -39,6 +39,12 @@ class SingBox implements ProtocolInterface
protected function loadConfig()
{
// 优先从 admin_setting 获取模板
$template = admin_setting('subscribe_template_singbox');
if (!empty($template)) {
return is_array($template) ? $template : json_decode($template, true);
}
$defaultConfig = base_path('resources/rules/default.sing-box.json');
$customConfig = base_path('resources/rules/custom.sing-box.json');
$jsonData = file_exists($customConfig) ? file_get_contents($customConfig) : file_get_contents($defaultConfig);
+18 -12
View File
@@ -28,17 +28,23 @@ class Stash implements ProtocolInterface
$servers = $this->servers;
$user = $this->user;
$appName = admin_setting('app_name', 'XBoard');
// 暂时使用clash配置文件,后续根据Stash更新情况更新
$defaultConfig = base_path() . '/resources/rules/default.clash.yaml';
$customClashConfig = base_path() . '/resources/rules/custom.clash.yaml';
$customStashConfig = base_path() . '/resources/rules/custom.stash.yaml';
if (\File::exists($customStashConfig)) {
$config = Yaml::parseFile($customStashConfig);
} elseif (\File::exists($customClashConfig)) {
$config = Yaml::parseFile($customClashConfig);
} else {
$config = Yaml::parseFile($defaultConfig);
// 优先从 admin_setting 获取模板
$template = admin_setting('subscribe_template_stash');
if (empty($template)) {
$defaultConfig = base_path('resources/rules/default.clash.yaml');
$customClashConfig = base_path('resources/rules/custom.clash.yaml');
$customStashConfig = base_path('resources/rules/custom.stash.yaml');
if (file_exists($customStashConfig)) {
$template = file_get_contents($customStashConfig);
} elseif (file_exists($customClashConfig)) {
$template = file_get_contents($customClashConfig);
} else {
$template = file_get_contents($defaultConfig);
}
}
$config = Yaml::parse($template);
$proxy = [];
$proxies = [];
@@ -153,8 +159,8 @@ class Stash implements ProtocolInterface
switch (data_get($protocol_settings, 'network')) {
case 'tcp':
$array['network'] = data_get($protocol_settings, 'network_settings.header.type');
$array['http-opts']['path'] = data_get($protocol_settings, 'network_settings.header.request.path', ['/'])[0];
$array['network'] = data_get($protocol_settings, 'network_settings.header.type', 'tcp');
$array['http-opts']['path'] = data_get($protocol_settings, 'network_settings.header.request.path', ['/']);
break;
case 'ws':
$array['network'] = 'ws';
+10 -6
View File
@@ -59,12 +59,16 @@ class Surge implements ProtocolInterface
}
}
$defaultConfig = base_path() . '/resources/rules/default.surge.conf';
$customConfig = base_path() . '/resources/rules/custom.surge.conf';
if (\File::exists($customConfig)) {
$config = file_get_contents("$customConfig");
} else {
$config = file_get_contents("$defaultConfig");
// 优先从 admin_setting 获取模板
$config = admin_setting('subscribe_template_surge');
if (empty($config)) {
$defaultConfig = base_path('resources/rules/default.surge.conf');
$customConfig = base_path('resources/rules/custom.surge.conf');
if (file_exists($customConfig)) {
$config = file_get_contents($customConfig);
} else {
$config = file_get_contents($defaultConfig);
}
}
// Subscription link