mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-28 06:47:24 +08:00
Revert "refactor: core plugins to plugins-core"
This reverts commit c0b6ee1763.
This commit is contained in:
@@ -1,11 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "[entrypoint] Running database migrations..."
|
|
||||||
php /www/artisan migrate --force
|
|
||||||
|
|
||||||
echo "[entrypoint] Checking core plugins..."
|
|
||||||
php /www/artisan tinker --execute="App\Services\Plugin\PluginManager::installDefaultPlugins();" 2>/dev/null || true
|
|
||||||
|
|
||||||
echo "[entrypoint] Starting services..."
|
|
||||||
exec "$@"
|
|
||||||
+1
-3
@@ -32,6 +32,7 @@ COPY .docker/supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
|||||||
|
|
||||||
RUN composer install --no-cache --no-dev \
|
RUN composer install --no-cache --no-dev \
|
||||||
&& php artisan storage:link \
|
&& php artisan storage:link \
|
||||||
|
&& cp -r plugins/ /opt/default-plugins/ \
|
||||||
&& chown -R www:www /www \
|
&& chown -R www:www /www \
|
||||||
&& chmod -R 775 /www \
|
&& chmod -R 775 /www \
|
||||||
&& mkdir -p /data \
|
&& mkdir -p /data \
|
||||||
@@ -43,7 +44,4 @@ ENV ENABLE_WEB=true \
|
|||||||
ENABLE_WS_SERVER=false
|
ENABLE_WS_SERVER=false
|
||||||
|
|
||||||
EXPOSE 7001
|
EXPOSE 7001
|
||||||
COPY .docker/entrypoint.sh /entrypoint.sh
|
|
||||||
RUN chmod +x /entrypoint.sh
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
|
||||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class HookList extends Command
|
|||||||
|
|
||||||
public function handle()
|
public function handle()
|
||||||
{
|
{
|
||||||
$paths = [base_path('app'), base_path('plugins-core'), base_path('plugins')];
|
$paths = [base_path('app'), base_path('plugins')];
|
||||||
$hooks = collect();
|
$hooks = collect();
|
||||||
$pattern = '/HookManager::(call|filter|register|registerFilter)\([\'\"]([a-zA-Z0-9_.-]+)[\'\"]/';
|
$pattern = '/HookManager::(call|filter|register|registerFilter)\([\'\"]([a-zA-Z0-9_.-]+)[\'\"]/';
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ use function Laravel\Prompts\confirm;
|
|||||||
use function Laravel\Prompts\text;
|
use function Laravel\Prompts\text;
|
||||||
use function Laravel\Prompts\note;
|
use function Laravel\Prompts\note;
|
||||||
use function Laravel\Prompts\select;
|
use function Laravel\Prompts\select;
|
||||||
|
use App\Models\Plugin;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
class XboardInstall extends Command
|
class XboardInstall extends Command
|
||||||
{
|
{
|
||||||
@@ -158,6 +160,7 @@ class XboardInstall extends Command
|
|||||||
if (!self::registerAdmin($email, $password)) {
|
if (!self::registerAdmin($email, $password)) {
|
||||||
abort(500, '管理员账号注册失败,请重试');
|
abort(500, '管理员账号注册失败,请重试');
|
||||||
}
|
}
|
||||||
|
self::restoreProtectedPlugins($this);
|
||||||
$this->info('正在安装默认插件...');
|
$this->info('正在安装默认插件...');
|
||||||
PluginManager::installDefaultPlugins();
|
PluginManager::installDefaultPlugins();
|
||||||
$this->info('默认插件安装完成');
|
$this->info('默认插件安装完成');
|
||||||
@@ -361,4 +364,34 @@ class XboardInstall extends Command
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 还原内置受保护插件(可在安装和更新时调用)
|
||||||
|
* Docker 部署时 plugins/ 目录被外部挂载覆盖,需要从镜像备份中还原默认插件
|
||||||
|
*/
|
||||||
|
public static function restoreProtectedPlugins(Command $console = null)
|
||||||
|
{
|
||||||
|
$backupBase = '/opt/default-plugins';
|
||||||
|
$pluginsBase = base_path('plugins');
|
||||||
|
|
||||||
|
if (!File::isDirectory($backupBase)) {
|
||||||
|
$console?->info('非 Docker 环境或备份目录不存在,跳过插件还原。');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (Plugin::PROTECTED_PLUGINS as $pluginCode) {
|
||||||
|
$dirName = Str::studly($pluginCode);
|
||||||
|
$source = "{$backupBase}/{$dirName}";
|
||||||
|
$target = "{$pluginsBase}/{$dirName}";
|
||||||
|
|
||||||
|
if (!File::isDirectory($source)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先清除旧文件再复制,避免重命名后残留旧文件
|
||||||
|
File::deleteDirectory($target);
|
||||||
|
File::copyDirectory($source, $target);
|
||||||
|
$console?->info("已同步默认插件 [{$dirName}]");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ use App\Services\UpdateService;
|
|||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use App\Services\Plugin\PluginManager;
|
use App\Services\Plugin\PluginManager;
|
||||||
|
use App\Models\Plugin;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
use App\Console\Commands\XboardInstall;
|
||||||
|
|
||||||
class XboardUpdate extends Command
|
class XboardUpdate extends Command
|
||||||
{
|
{
|
||||||
@@ -44,6 +47,8 @@ class XboardUpdate extends Command
|
|||||||
$this->info('正在导入数据库请稍等...');
|
$this->info('正在导入数据库请稍等...');
|
||||||
Artisan::call("migrate", ['--force' => true]);
|
Artisan::call("migrate", ['--force' => true]);
|
||||||
$this->info(Artisan::output());
|
$this->info(Artisan::output());
|
||||||
|
$this->info('正在检查内置插件文件...');
|
||||||
|
XboardInstall::restoreProtectedPlugins($this);
|
||||||
$this->info('正在检查并安装默认插件...');
|
$this->info('正在检查并安装默认插件...');
|
||||||
PluginManager::installDefaultPlugins();
|
PluginManager::installDefaultPlugins();
|
||||||
$this->info('默认插件检查完成');
|
$this->info('默认插件检查完成');
|
||||||
|
|||||||
@@ -60,67 +60,56 @@ class PluginController extends Controller
|
|||||||
->keyBy('code')
|
->keyBy('code')
|
||||||
->toArray();
|
->toArray();
|
||||||
|
|
||||||
|
$pluginPath = base_path('plugins');
|
||||||
$plugins = [];
|
$plugins = [];
|
||||||
$seenCodes = [];
|
|
||||||
|
|
||||||
foreach ($this->pluginManager->getPluginPaths() as $pluginPath) {
|
if (File::exists($pluginPath)) {
|
||||||
if (!File::exists($pluginPath)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$directories = File::directories($pluginPath);
|
$directories = File::directories($pluginPath);
|
||||||
foreach ($directories as $directory) {
|
foreach ($directories as $directory) {
|
||||||
|
$pluginName = basename($directory);
|
||||||
$configFile = $directory . '/config.json';
|
$configFile = $directory . '/config.json';
|
||||||
if (!File::exists($configFile)) {
|
if (File::exists($configFile)) {
|
||||||
continue;
|
$config = json_decode(File::get($configFile), true);
|
||||||
}
|
$code = $config['code'];
|
||||||
$config = json_decode(File::get($configFile), true);
|
$pluginType = $config['type'] ?? Plugin::TYPE_FEATURE;
|
||||||
if (!$config || !isset($config['code'])) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$code = $config['code'];
|
|
||||||
|
|
||||||
if (isset($seenCodes[$code])) {
|
// 如果指定了类型,过滤插件
|
||||||
continue;
|
if ($type && $pluginType !== $type) {
|
||||||
}
|
continue;
|
||||||
$seenCodes[$code] = true;
|
|
||||||
|
|
||||||
$pluginType = $config['type'] ?? Plugin::TYPE_FEATURE;
|
|
||||||
if ($type && $pluginType !== $type) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$installed = isset($installedPlugins[$code]);
|
|
||||||
$pluginConfig = $installed ? $this->configService->getConfig($code) : ($config['config'] ?? []);
|
|
||||||
$readmeFile = collect(['README.md', 'readme.md'])
|
|
||||||
->map(fn($f) => $directory . '/' . $f)
|
|
||||||
->first(fn($path) => File::exists($path));
|
|
||||||
$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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$installed = isset($installedPlugins[$code]);
|
||||||
|
$pluginConfig = $installed ? $this->configService->getConfig($code) : ($config['config'] ?? []);
|
||||||
|
$readmeFile = collect(['README.md', 'readme.md'])
|
||||||
|
->map(fn($f) => $directory . '/' . $f)
|
||||||
|
->first(fn($path) => File::exists($path));
|
||||||
|
$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[] = [
|
||||||
|
'code' => $config['code'],
|
||||||
|
'name' => $config['name'],
|
||||||
|
'version' => $config['version'],
|
||||||
|
'description' => $config['description'],
|
||||||
|
'author' => $config['author'],
|
||||||
|
'type' => $pluginType,
|
||||||
|
'is_installed' => $installed,
|
||||||
|
'is_enabled' => $installed ? $installedPlugins[$code]['is_enabled'] : false,
|
||||||
|
'is_protected' => in_array($code, Plugin::PROTECTED_PLUGINS),
|
||||||
|
'can_be_deleted' => !in_array($code, Plugin::PROTECTED_PLUGINS),
|
||||||
|
'config' => $pluginConfig,
|
||||||
|
'readme' => $readmeContent,
|
||||||
|
'need_upgrade' => $needUpgrade,
|
||||||
|
'admin_menus' => $config['admin_menus'] ?? null,
|
||||||
|
'admin_crud' => $config['admin_crud'] ?? null,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
$isCore = $this->pluginManager->isCorePlugin($code);
|
|
||||||
$plugins[] = [
|
|
||||||
'code' => $config['code'],
|
|
||||||
'name' => $config['name'],
|
|
||||||
'version' => $config['version'],
|
|
||||||
'description' => $config['description'],
|
|
||||||
'author' => $config['author'],
|
|
||||||
'type' => $pluginType,
|
|
||||||
'is_installed' => $installed,
|
|
||||||
'is_enabled' => $installed ? $installedPlugins[$code]['is_enabled'] : false,
|
|
||||||
'is_protected' => $isCore,
|
|
||||||
'can_be_deleted' => !$isCore,
|
|
||||||
'config' => $pluginConfig,
|
|
||||||
'readme' => $readmeContent,
|
|
||||||
'need_upgrade' => $needUpgrade,
|
|
||||||
'admin_menus' => $config['admin_menus'] ?? null,
|
|
||||||
'admin_crud' => $config['admin_crud'] ?? null,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -325,10 +314,10 @@ class PluginController extends Controller
|
|||||||
|
|
||||||
$code = $request->input('code');
|
$code = $request->input('code');
|
||||||
|
|
||||||
// 检查是否为核心插件
|
// 检查是否为受保护的插件
|
||||||
if ($this->pluginManager->isCorePlugin($code)) {
|
if (in_array($code, Plugin::PROTECTED_PLUGINS)) {
|
||||||
return response()->json([
|
return response()->json([
|
||||||
'message' => '该插件为系统核心插件,不允许删除'
|
'message' => '该插件为系统默认插件,不允许删除'
|
||||||
], 403);
|
], 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,8 @@ class PluginServiceProvider extends ServiceProvider
|
|||||||
|
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
foreach (['plugins', 'plugins-core'] as $dir) {
|
if (!file_exists(base_path('plugins'))) {
|
||||||
$path = base_path($dir);
|
mkdir(base_path('plugins'), 0755, true);
|
||||||
if (!file_exists($path)) {
|
|
||||||
mkdir($path, 0755, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,6 @@ use Illuminate\Support\Str;
|
|||||||
class PluginManager
|
class PluginManager
|
||||||
{
|
{
|
||||||
protected string $pluginPath;
|
protected string $pluginPath;
|
||||||
protected string $corePluginPath;
|
|
||||||
protected array $loadedPlugins = [];
|
protected array $loadedPlugins = [];
|
||||||
protected bool $pluginsInitialized = false;
|
protected bool $pluginsInitialized = false;
|
||||||
protected array $configTypesCache = [];
|
protected array $configTypesCache = [];
|
||||||
@@ -22,7 +21,6 @@ class PluginManager
|
|||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->pluginPath = base_path('plugins');
|
$this->pluginPath = base_path('plugins');
|
||||||
$this->corePluginPath = base_path('plugins-core');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,42 +31,14 @@ class PluginManager
|
|||||||
return 'Plugin\\' . Str::studly($pluginCode);
|
return 'Plugin\\' . Str::studly($pluginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function resolvePluginPath(string $pluginCode): ?string
|
/**
|
||||||
{
|
* 获取插件的基础路径
|
||||||
$dirName = Str::studly($pluginCode);
|
*/
|
||||||
$corePath = $this->corePluginPath . '/' . $dirName;
|
|
||||||
if (File::isDirectory($corePath)) {
|
|
||||||
return $corePath;
|
|
||||||
}
|
|
||||||
$userPath = $this->pluginPath . '/' . $dirName;
|
|
||||||
if (File::isDirectory($userPath)) {
|
|
||||||
return $userPath;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPluginPath(string $pluginCode): string
|
public function getPluginPath(string $pluginCode): string
|
||||||
{
|
|
||||||
return $this->resolvePluginPath($pluginCode)
|
|
||||||
?? $this->pluginPath . '/' . Str::studly($pluginCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getUserPluginPath(string $pluginCode): string
|
|
||||||
{
|
{
|
||||||
return $this->pluginPath . '/' . Str::studly($pluginCode);
|
return $this->pluginPath . '/' . Str::studly($pluginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isCorePlugin(string $pluginCode): bool
|
|
||||||
{
|
|
||||||
$dirName = Str::studly($pluginCode);
|
|
||||||
return File::isDirectory($this->corePluginPath . '/' . $dirName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPluginPaths(): array
|
|
||||||
{
|
|
||||||
return [$this->corePluginPath, $this->pluginPath];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 加载插件类
|
* 加载插件类
|
||||||
*/
|
*/
|
||||||
@@ -429,19 +399,17 @@ class PluginManager
|
|||||||
*/
|
*/
|
||||||
public function delete(string $pluginCode): bool
|
public function delete(string $pluginCode): bool
|
||||||
{
|
{
|
||||||
|
// 先卸载插件
|
||||||
if (Plugin::where('code', $pluginCode)->exists()) {
|
if (Plugin::where('code', $pluginCode)->exists()) {
|
||||||
$this->uninstall($pluginCode);
|
$this->uninstall($pluginCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->isCorePlugin($pluginCode)) {
|
$pluginPath = $this->getPluginPath($pluginCode);
|
||||||
throw new \Exception('核心插件不允许删除');
|
|
||||||
}
|
|
||||||
|
|
||||||
$pluginPath = $this->getUserPluginPath($pluginCode);
|
|
||||||
if (!File::exists($pluginPath)) {
|
if (!File::exists($pluginPath)) {
|
||||||
throw new \Exception('插件不存在');
|
throw new \Exception('插件不存在');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 删除插件目录
|
||||||
File::deleteDirectory($pluginPath);
|
File::deleteDirectory($pluginPath);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -559,7 +527,7 @@ class PluginManager
|
|||||||
throw new \Exception('插件配置文件格式错误');
|
throw new \Exception('插件配置文件格式错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
$targetPath = $this->getUserPluginPath($config['code']);
|
$targetPath = $this->pluginPath . '/' . Str::studly($config['code']);
|
||||||
if (File::exists($targetPath)) {
|
if (File::exists($targetPath)) {
|
||||||
$installedConfigPath = $targetPath . '/config.json';
|
$installedConfigPath = $targetPath . '/config.json';
|
||||||
if (!File::exists($installedConfigPath)) {
|
if (!File::exists($installedConfigPath)) {
|
||||||
@@ -710,27 +678,12 @@ class PluginManager
|
|||||||
*/
|
*/
|
||||||
public static function installDefaultPlugins(): void
|
public static function installDefaultPlugins(): void
|
||||||
{
|
{
|
||||||
$pluginManager = app(self::class);
|
foreach (Plugin::PROTECTED_PLUGINS as $pluginCode) {
|
||||||
$coreDir = base_path('plugins-core');
|
if (!Plugin::where('code', $pluginCode)->exists()) {
|
||||||
|
$pluginManager = app(self::class);
|
||||||
if (!File::isDirectory($coreDir)) {
|
$pluginManager->install($pluginCode);
|
||||||
return;
|
$pluginManager->enable($pluginCode);
|
||||||
}
|
Log::info("Installed and enabled default plugin: {$pluginCode}");
|
||||||
|
|
||||||
foreach (File::directories($coreDir) as $directory) {
|
|
||||||
$configFile = $directory . '/config.json';
|
|
||||||
if (!File::exists($configFile)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$config = json_decode(File::get($configFile), true);
|
|
||||||
$code = $config['code'] ?? null;
|
|
||||||
if (!$code) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!Plugin::where('code', $code)->exists()) {
|
|
||||||
$pluginManager->install($code);
|
|
||||||
$pluginManager->enable($code);
|
|
||||||
Log::info("Installed and enabled core plugin: {$code}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-1
@@ -1,2 +1,8 @@
|
|||||||
*
|
*
|
||||||
!.gitignore
|
!.gitignore
|
||||||
|
!AlipayF2f/
|
||||||
|
!Btcpay
|
||||||
|
!Coinbase
|
||||||
|
!Epay
|
||||||
|
!Mgate
|
||||||
|
!Telegram
|
||||||
Reference in New Issue
Block a user