fix(settings): never overwrite existing values on restore-from-backup
Prefer superadmin over stale backup defaults like limangold.ir when filling empty keys. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,27 +13,37 @@ use Illuminate\Support\Facades\Schema;
|
||||
* بازگرداندن مقادیر تنظیمات can_edit=0 از جدول بکاپ (پیشفرض settings_copy1)
|
||||
* به جدول settings — بعد از اینکه مالکیت این کلیدها از سوپرادمین به همین نصب برگشت.
|
||||
*
|
||||
* کلیدهای گروه «دسترسی مشتری به قابلیتها» (مالک: سوپرادمین) و کلیدهای runtime رد میشوند.
|
||||
* قاعده سخت: اگر ردیف لوکال value غیرخالی دارد، هرگز عوض نمیشود
|
||||
* (حتی با --overwrite؛ برای اجبار از --force-overwrite استفاده کن).
|
||||
*
|
||||
* فالبک --fill-from-superadmin باید قبل از دیپلوی سوپرادمینِ جدید اجرا شود؛
|
||||
* بعد از آن دیپلوی، این کلیدها از سوپرادمین حذف شدهاند و فالبکی نمیماند.
|
||||
* پر کردن فقط برای ردیف غایب یا value خالی:
|
||||
* 1) اگر --fill-from-superadmin و سوپرادمین مقدار دارد → همان
|
||||
* 2) وگرنه مقدار بکاپ
|
||||
*
|
||||
* کلیدهای گروه «دسترسی مشتری به قابلیتها» و runtime رد میشوند.
|
||||
*/
|
||||
class SettingsRestoreFromBackupCommand extends Command
|
||||
{
|
||||
protected $signature = 'settings:restore-from-backup
|
||||
{--table=settings_copy1 : جدول بکاپ}
|
||||
{--dry-run : فقط گزارش، بدون نوشتن}
|
||||
{--overwrite : مقدار لوکال غیرخالی را هم بازنویسی کن}
|
||||
{--fill-from-superadmin : کلیدهای غایب/خالی در بکاپ را از سوپرادمین پر کن}';
|
||||
{--fill-from-superadmin : برای کلید خالی/غایب اول از سوپرادمین پر کن، بعد بکاپ}
|
||||
{--force-overwrite : خطرناک — value غیرخالی لوکال را هم بازنویسی کن}';
|
||||
|
||||
protected $description = 'بازگرداندن مقادیر تنظیمات پیشرفته از جدول بکاپ به settings';
|
||||
protected $description = 'بازگرداندن مقادیر تنظیمات پیشرفته از جدول بکاپ به settings (بدون دست زدن به value موجود)';
|
||||
|
||||
/** پیشفرضهای deploy که در بکاپ ماندهاند و نباید بر مقدار واقعی سوپرادمین بچربند */
|
||||
private const DEPLOY_DEFAULT_VALUES = [
|
||||
'https://limangold.ir',
|
||||
'http://limangold.ir',
|
||||
];
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
$table = trim((string) $this->option('table')) ?: 'settings_copy1';
|
||||
$dryRun = (bool) $this->option('dry-run');
|
||||
$overwrite = (bool) $this->option('overwrite');
|
||||
$fillFromSuperAdmin = (bool) $this->option('fill-from-superadmin');
|
||||
$forceOverwrite = (bool) $this->option('force-overwrite');
|
||||
|
||||
if (! Schema::hasTable($table)) {
|
||||
$this->error("جدول بکاپ «{$table}» وجود ندارد.");
|
||||
@@ -41,6 +51,10 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
return self::FAILURE;
|
||||
}
|
||||
|
||||
if ($forceOverwrite) {
|
||||
$this->warn('--force-overwrite روشن است: value غیرخالی لوکال هم ممکن است عوض شود.');
|
||||
}
|
||||
|
||||
$backupRows = $this->readBackup($table);
|
||||
$superAdminValues = $fillFromSuperAdmin ? $this->superAdminValues() : [];
|
||||
|
||||
@@ -63,18 +77,16 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
|
||||
$hasType = Setting::hasTypeColumn();
|
||||
$report = [];
|
||||
$counts = ['inserted' => 0, 'updated' => 0, 'skipped' => 0];
|
||||
$counts = ['inserted' => 0, 'updated' => 0, 'skipped' => 0, 'kept' => 0];
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$backup = $backupRows[$key] ?? null;
|
||||
$local = $localRows->get($key);
|
||||
|
||||
$value = $backup['value'] ?? null;
|
||||
$source = $value === null || $value === '' ? '' : 'backup';
|
||||
if (($value === null || $value === '') && array_key_exists($key, $superAdminValues)) {
|
||||
$value = $superAdminValues[$key];
|
||||
$source = 'superadmin';
|
||||
}
|
||||
[$value, $source] = $this->resolveFillValue(
|
||||
$backup['value'] ?? null,
|
||||
$superAdminValues[$key] ?? null,
|
||||
$fillFromSuperAdmin
|
||||
);
|
||||
|
||||
if (! $local) {
|
||||
$counts['inserted']++;
|
||||
@@ -96,9 +108,28 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
continue;
|
||||
}
|
||||
|
||||
$localValue = (string) ($local->value ?? '');
|
||||
$localValue = trim((string) ($local->value ?? ''));
|
||||
$localIsDeployDefault = $this->isDeployDefault($localValue);
|
||||
|
||||
// قاعده سخت: value واقعی موجود دست نخوره
|
||||
// استثنا: لوکال فقط پیشفرض deploy (مثل limangold.ir) است و SA مقدار واقعی دارد
|
||||
$allowReplaceDefault = $fillFromSuperAdmin
|
||||
&& $localIsDeployDefault
|
||||
&& isset($superAdminValues[$key])
|
||||
&& ! $this->isDeployDefault($superAdminValues[$key]);
|
||||
|
||||
if ($localValue !== '' && ! $forceOverwrite && ! $allowReplaceDefault) {
|
||||
$counts['kept']++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$writeValue = $value !== null && $value !== ''
|
||||
&& ($localValue === '' || ($overwrite && $localValue !== (string) $value));
|
||||
&& (
|
||||
$localValue === ''
|
||||
|| $allowReplaceDefault
|
||||
|| ($forceOverwrite && $localValue !== (string) $value)
|
||||
);
|
||||
|
||||
$metaChanged = false;
|
||||
if ($backup) {
|
||||
@@ -143,14 +174,16 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
}
|
||||
|
||||
$this->info(sprintf(
|
||||
'%sinsert=%d update=%d skip=%d (backup=%s, superadmin=%d)',
|
||||
'%sinsert=%d update=%d kept=%d skip=%d (backup=%s, superadmin=%d)',
|
||||
$dryRun ? '[dry-run] ' : '',
|
||||
$counts['inserted'],
|
||||
$counts['updated'],
|
||||
$counts['kept'],
|
||||
$counts['skipped'],
|
||||
$table,
|
||||
count($superAdminValues)
|
||||
));
|
||||
$this->comment('kept = ردیفهایی که value غیرخالی داشتند و دست نخورده ماندند.');
|
||||
|
||||
if ($dryRun) {
|
||||
$this->comment('هیچ چیزی نوشته نشد. برای اجرا همین دستور را بدون --dry-run بزن.');
|
||||
@@ -159,6 +192,37 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* برای پر کردن خالی/غایب: با --fill-from-superadmin اول SA، بعد بکاپ.
|
||||
* پیشفرضهای deploy در بکاپ (مثل limangold.ir) اگر SA مقدار واقعی دارد، نادیده گرفته میشوند.
|
||||
*
|
||||
* @return array{0: ?string, 1: string}
|
||||
*/
|
||||
private function resolveFillValue(?string $backupValue, ?string $superAdminValue, bool $preferSuperAdmin): array
|
||||
{
|
||||
$backupValue = $backupValue === null ? null : trim($backupValue);
|
||||
$superAdminValue = $superAdminValue === null ? null : trim($superAdminValue);
|
||||
$backupIsDefault = $backupValue !== null && $backupValue !== ''
|
||||
&& in_array(rtrim(strtolower($backupValue), '/'), array_map(
|
||||
static fn ($v) => rtrim(strtolower($v), '/'),
|
||||
self::DEPLOY_DEFAULT_VALUES
|
||||
), true);
|
||||
|
||||
if ($preferSuperAdmin && $superAdminValue !== null && $superAdminValue !== '') {
|
||||
return [$superAdminValue, 'superadmin'];
|
||||
}
|
||||
|
||||
if ($backupValue !== null && $backupValue !== '' && ! ($backupIsDefault && $preferSuperAdmin)) {
|
||||
return [$backupValue, 'backup'];
|
||||
}
|
||||
|
||||
if ($backupValue !== null && $backupValue !== '') {
|
||||
return [$backupValue, 'backup'];
|
||||
}
|
||||
|
||||
return [null, ''];
|
||||
}
|
||||
|
||||
/**
|
||||
* ردیفهای can_edit=0 بکاپ، بدون کلیدهای سوپرادمین و runtime.
|
||||
*
|
||||
@@ -217,10 +281,10 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
continue;
|
||||
}
|
||||
$value = $item['value'] ?? null;
|
||||
if ($value === null || $value === '') {
|
||||
if ($value === null || trim((string) $value) === '') {
|
||||
continue;
|
||||
}
|
||||
$values[$key] = (string) $value;
|
||||
$values[$key] = trim((string) $value);
|
||||
}
|
||||
|
||||
return $values;
|
||||
@@ -244,6 +308,7 @@ class SettingsRestoreFromBackupCommand extends Command
|
||||
if (($backup['description'] ?? '') !== '' && (string) ($local->description ?? '') !== $backup['description']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $hasType && ($backup['type'] ?? '') !== '' && (string) ($local->type ?? '') !== $backup['type'];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user