fix(settings): PHP sync for runtime can_edit after deploy

Force can_edit=1 for Montzar/price runtime keys via settings:deploy
and settings:sync-runtime-can-edit, so tenant DB stays correct even
when an older deploy SQL is still on the server.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-19 21:07:12 +03:30
co-authored by Cursor
parent b7eed369c6
commit 924234bb19
6 changed files with 169 additions and 0 deletions
@@ -2,6 +2,7 @@
namespace App\Console\Commands;
use App\Services\SettingsRuntimeCanEditSync;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
@@ -100,6 +101,8 @@ class DeploySettingsConstCommand extends Command
return self::FAILURE;
}
$this->syncRuntimeCanEdit($db);
$this->info(" done: {$db}");
}
@@ -129,6 +132,38 @@ class DeploySettingsConstCommand extends Command
return $path;
}
private function syncRuntimeCanEdit(string $database): void
{
try {
$sync = app(SettingsRuntimeCanEditSync::class);
$sync->useDatabase($database);
$updated = $sync->sync();
$this->line(" runtime can_edit sync: {$updated} row(s) updated");
$status = $sync->statusMap();
if ($status !== []) {
$preview = collect($status)
->map(fn ($canEdit, $key) => "{$key}={$canEdit}")
->implode(', ');
$this->line(" runtime keys: {$preview}");
}
$violations = $sync->violations();
if ($violations !== []) {
$this->warn(' WARN: runtime keys still can_edit≠1: '.implode(', ', array_keys($violations)));
}
$fee = \App\Models\Setting::query()
->where('key', 'fee_deduct_from_gold')
->first(['key', 'can_edit']);
if ($fee) {
$this->line(" check fee_deduct_from_gold can_edit={$fee->can_edit}");
}
} catch (\Throwable $e) {
$this->warn(' runtime can_edit sync skipped: '.$e->getMessage());
}
}
private function findMysqlBinary(): ?string
{
$process = new Process(['command', '-v', 'mysql']);
@@ -0,0 +1,43 @@
<?php
namespace App\Console\Commands;
use App\Services\SettingsRuntimeCanEditSync;
use Illuminate\Console\Command;
class SyncRuntimeSettingsCanEditCommand extends Command
{
protected $signature = 'settings:sync-runtime-can-edit
{database? : نام دیتابیس (پیش‌فرض DB_DATABASE)}';
protected $description = 'تثبیت can_edit=1 برای کلیدهای runtime tenant (Montzar، up/down_price، …)';
public function handle(SettingsRuntimeCanEditSync $sync): int
{
$database = trim((string) ($this->argument('database') ?: config('database.connections.'.config('database.default').'.database', '')));
if ($database === '') {
$this->error('DB_DATABASE خالی است یا database را بدهید.');
return self::FAILURE;
}
$sync->useDatabase($database);
$updated = $sync->sync();
$this->info("DB {$database}: {$updated} row(s) updated");
foreach ($sync->statusMap() as $key => $canEdit) {
$this->line(" {$key} => can_edit={$canEdit}");
}
$violations = $sync->violations();
if ($violations !== []) {
$this->error('هنوز can_edit≠1: '.implode(', ', array_keys($violations)));
return self::FAILURE;
}
$this->info('OK — همه کلیدهای runtime can_edit=1');
return self::SUCCESS;
}
}
@@ -0,0 +1,75 @@
<?php
namespace App\Services;
use App\Models\Setting;
use Illuminate\Support\Facades\DB;
/**
* تثبیت can_edit=1 برای کلیدهای runtime tenant (Montzar، up/down_price، …).
* بعد از deploy_settings_const.sql هم اجرا می‌شود تا حتی با SQL قدیمی روی سرور درست بماند.
*/
class SettingsRuntimeCanEditSync
{
/** @return list<string> */
public function keys(): array
{
return SuperAdminProgramSettingsOverlay::RUNTIME_KEYS;
}
public function sync(): int
{
$keys = $this->keys();
if ($keys === []) {
return 0;
}
return Setting::query()
->whereIn('key', $keys)
->where(function ($query) {
$query->where('can_edit', '!=', 1)
->orWhereNull('can_edit');
})
->update(['can_edit' => 1]);
}
/**
* @return array<string, mixed> key => can_edit for rows still not 1
*/
public function violations(): array
{
$keys = $this->keys();
if ($keys === []) {
return [];
}
return Setting::query()
->whereIn('key', $keys)
->where(function ($query) {
$query->where('can_edit', '!=', 1)
->orWhereNull('can_edit');
})
->orderBy('key')
->pluck('can_edit', 'key')
->all();
}
/**
* @return array<string, mixed> key => can_edit for all runtime keys present in DB
*/
public function statusMap(): array
{
return Setting::query()
->whereIn('key', $this->keys())
->orderBy('key')
->pluck('can_edit', 'key')
->all();
}
public function useDatabase(string $database): void
{
$connection = (string) config('database.default', 'mysql');
config(["database.connections.{$connection}.database" => $database]);
DB::purge($connection);
}
}