feat: enhance plugin management

- Add command support for plugin management
- Optimize plugin management page layout
- Add email copy functionality for users
- Convert payment methods and Telegram Bot to plugin system
This commit is contained in:
xboard
2025-07-26 18:49:58 +08:00
parent 02d853d46a
commit 58868268dd
56 changed files with 3677 additions and 1329 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Services\Plugin\PluginManager;
use Illuminate\Console\Command;
use Illuminate\Encryption\Encrypter;
use App\Models\User;
@@ -15,6 +16,8 @@ use function Laravel\Prompts\confirm;
use function Laravel\Prompts\text;
use function Laravel\Prompts\note;
use function Laravel\Prompts\select;
use App\Models\Plugin;
use Illuminate\Support\Str;
class XboardInstall extends Command
{
@@ -157,6 +160,11 @@ class XboardInstall extends Command
if (!self::registerAdmin($email, $password)) {
abort(500, '管理员账号注册失败,请重试');
}
self::restoreProtectedPlugins($this);
$this->info('正在安装默认插件...');
PluginManager::installDefaultPlugins();
$this->info('默认插件安装完成');
$this->info('🎉:一切就绪');
$this->info("管理员邮箱:{$email}");
$this->info("管理员密码:{$password}");
@@ -356,4 +364,64 @@ class XboardInstall extends Command
}
}
}
/**
* 还原内置受保护插件(可在安装和更新时调用)
*/
public static function restoreProtectedPlugins(Command $console = null)
{
exec("git config core.filemode false", $output, $returnVar);
$cmd = "git status --porcelain plugins/ 2>/dev/null";
exec($cmd, $output, $returnVar);
if (!empty($output)) {
$hasNonNewFiles = false;
foreach ($output as $line) {
$status = trim(substr($line, 0, 2));
if ($status !== 'A') {
$hasNonNewFiles = true;
break;
}
}
if ($hasNonNewFiles) {
if ($console)
$console->info("检测到 plugins 目录有变更,正在还原...");
foreach ($output as $line) {
$status = trim(substr($line, 0, 2));
$filePath = trim(substr($line, 3));
if (strpos($filePath, 'plugins/') === 0 && $status !== 'A') {
$relativePath = substr($filePath, 8);
if ($console) {
$action = match ($status) {
'M' => '修改',
'D' => '删除',
'R' => '重命名',
'C' => '复制',
default => '变更'
};
$console->info("还原插件文件 [{$relativePath}] ({$action})");
}
$cmd = "git checkout HEAD -- {$filePath}";
exec($cmd, $gitOutput, $gitReturnVar);
if ($gitReturnVar === 0) {
if ($console)
$console->info("插件文件 [{$relativePath}] 已还原。");
} else {
if ($console)
$console->error("插件文件 [{$relativePath}] 还原失败。");
}
}
}
} else {
if ($console)
$console->info("plugins 目录状态正常,无需还原。");
}
} else {
if ($console)
$console->info("plugins 目录状态正常,无需还原。");
}
}
}

View File

@@ -6,7 +6,10 @@ use App\Services\ThemeService;
use App\Services\UpdateService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use App\Services\Plugin\PluginManager;
use App\Models\Plugin;
use Illuminate\Support\Str;
use App\Console\Commands\XboardInstall;
class XboardUpdate extends Command
{
@@ -44,12 +47,17 @@ class XboardUpdate extends Command
$this->info('正在导入数据库请稍等...');
Artisan::call("migrate");
$this->info(Artisan::output());
Artisan::call('horizon:terminate');
$this->info('正在检查内置插件文件...');
XboardInstall::restoreProtectedPlugins($this);
$this->info('正在检查并安装默认插件...');
PluginManager::installDefaultPlugins();
$this->info('默认插件检查完成');
Artisan::call('reset:traffic', ['--fix-null' => true]);
$updateService = new UpdateService();
$updateService->updateVersionCache();
$themeService = app(ThemeService::class);
$themeService->refreshCurrentTheme();
Artisan::call('horizon:terminate');
$this->info('更新完毕,队列服务已重启,你无需进行任何操作。');
}
}

View File

@@ -64,6 +64,10 @@ class Kernel extends ConsoleKernel
{
$this->load(__DIR__ . '/Commands');
try {
app(PluginManager::class)->initializeEnabledPlugins();
} catch (\Exception $e) {
}
require base_path('routes/console.php');
}
}