refactor: rewrite restoreProtectedPlugins to use file copy instead of git

- Dockerfile: backup plugins/ to /opt/default-plugins/ at build time
- Replace exec/git-based restore with pure PHP File::copyDirectory()
- Only restore plugins defined in Plugin::PROTECTED_PLUGINS
- Delete target directory before copy to prevent stale files
- Remove function_exists('exec') guard (no longer needed)
This commit is contained in:
xboard
2026-03-11 08:30:12 +08:00
parent 1e069587fe
commit 0c6ec87ce5
2 changed files with 22 additions and 53 deletions
+1
View File
@@ -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 \
+21 -53
View File
@@ -160,9 +160,7 @@ class XboardInstall extends Command
if (!self::registerAdmin($email, $password)) { if (!self::registerAdmin($email, $password)) {
abort(500, '管理员账号注册失败,请重试'); abort(500, '管理员账号注册失败,请重试');
} }
if (function_exists('exec')) { self::restoreProtectedPlugins($this);
self::restoreProtectedPlugins($this);
}
$this->info('正在安装默认插件...'); $this->info('正在安装默认插件...');
PluginManager::installDefaultPlugins(); PluginManager::installDefaultPlugins();
$this->info('默认插件安装完成'); $this->info('默认插件安装完成');
@@ -369,61 +367,31 @@ class XboardInstall extends Command
/** /**
* 还原内置受保护插件(可在安装和更新时调用) * 还原内置受保护插件(可在安装和更新时调用)
* Docker 部署时 plugins/ 目录被外部挂载覆盖,需要从镜像备份中还原默认插件
*/ */
public static function restoreProtectedPlugins(Command $console = null) public static function restoreProtectedPlugins(Command $console = null)
{ {
exec("git config core.filemode false", $output, $returnVar); $backupBase = '/opt/default-plugins';
$cmd = "git status --porcelain plugins/ 2>/dev/null"; $pluginsBase = base_path('plugins');
exec($cmd, $output, $returnVar);
if (!empty($output)) { if (!File::isDirectory($backupBase)) {
$hasNonNewFiles = false; $console?->info('非 Docker 环境或备份目录不存在,跳过插件还原。');
foreach ($output as $line) { return;
$status = trim(substr($line, 0, 2)); }
if ($status !== 'A') {
$hasNonNewFiles = true; foreach (Plugin::PROTECTED_PLUGINS as $pluginCode) {
break; $dirName = Str::studly($pluginCode);
} $source = "{$backupBase}/{$dirName}";
$target = "{$pluginsBase}/{$dirName}";
if (!File::isDirectory($source)) {
continue;
} }
if ($hasNonNewFiles) {
if ($console)
$console->info("检测到 plugins 目录有变更,正在还原...");
foreach ($output as $line) { // 先清除旧文件再复制,避免重命名后残留旧文件
$status = trim(substr($line, 0, 2)); File::deleteDirectory($target);
$filePath = trim(substr($line, 3)); File::copyDirectory($source, $target);
$console?->info("已同步默认插件 [{$dirName}]");
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 目录状态正常,无需还原。");
} }
} }
} }