fix(settings): honor last-price switch and overlay only SuperAdmin access flags

Stored DB prices were treated as live quotes, and SuperAdmin overlay overwrote tenant keys like trading hours. Keep overlay to the 14 access flags and hide fallback prices when the panel says so.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-25 03:01:07 +03:30
co-authored by Cursor
parent 8984899042
commit b21b136711
9 changed files with 327 additions and 97 deletions
@@ -11,21 +11,16 @@ use Illuminate\Support\Facades\Cache;
class SuperAdminProgramSettingsOverlay
{
/**
* آخرین پاسخ سالم سوپرادمین.
*
* کلیدهای can_edit=0 مالکشان سوپرادمین است و کپی‌شان در جدول settings لوکال
* ممکن است ماندهٔ deploy باشد. بدون این کش، یک timeout لحظه‌ای باعث می‌شود
* ConfigService به آن کپی سقوط کند — مثلاً price_provider از zaarbox به montzar
* می‌پرید و مظنهٔ قدیمی montzar_live_price به‌جای قیمت زنده نمایش داده می‌شد.
* آخرین پاسخ سالم سوپرادمین — فقط کلیدهای دسترسی (SUPERADMIN_OWNED_KEYS).
* پاسخ remote ممکن است کلیدهای دیگری هم داشته باشد؛ آن‌ها ذخیره و اعمال نمی‌شوند.
*/
private const LAST_GOOD_CACHE_KEY = 'superadmin:program_settings:last_good';
private const LAST_GOOD_CACHE_MINUTES = 1440;
/**
* کلیدهایی که فقط سوپرادمین ویرایش می‌کند (پنل خانگی 403).
* این لیست فیلتر overlay نیست — برای load از blacklist زیر استفاده می‌شود
* تا مثلاً price_provider از سوپرادمین/کش last-good حذف نشود.
* فقط این ۱۴ دسترسی از سوپرادمین خوانده می‌شود.
* هر کلید دیگری (قیمت، ساعت کاری، برندینگ، …) مال پنل خانگی است.
*/
public const SUPERADMIN_OWNED_KEYS = [
'customer_direct_sms',
@@ -185,32 +180,47 @@ class SuperAdminProgramSettingsOverlay
return null;
}
$map = self::ownedItemsMap($items);
self::$memo = $map;
return $map;
}
/**
* فقط کلیدهای دسترسی سوپرادمین — بقیهٔ پاسخ remote دور ریخته می‌شود.
*
* @param array<int, array<string, mixed>> $items
* @return array<string, array<string, mixed>>
*/
public static function ownedItemsMap(array $items): array
{
$map = [];
foreach ($items as $row) {
if (! is_array($row)) {
continue;
}
$key = trim((string) ($row['key'] ?? ''));
// blacklist: فقط کلیدهای لوکال/runtime را دور بریز — نه whitelist قابلیت‌ها
// (whitelist قبلی price_provider را حذف می‌کرد و با قطعی SA به montzar لوکال می‌پرید)
if ($key === '' || self::isLocalOwned($key) || self::isRuntime($key)) {
if ($key === '' || ! self::isSuperAdminOwned($key)) {
continue;
}
$map[$key] = $row;
}
self::$memo = $map;
return $map;
}
/** @param array<int, array<string, mixed>> $items */
private function rememberLastGood(array $items): void
{
$owned = array_values(self::ownedItemsMap($items));
if ($owned === []) {
return;
}
try {
Cache::put(
self::LAST_GOOD_CACHE_KEY,
$items,
$owned,
now()->addMinutes(self::LAST_GOOD_CACHE_MINUTES)
);
} catch (\Throwable $e) {
@@ -255,7 +265,7 @@ class SuperAdminProgramSettingsOverlay
return null;
}
$key = trim($key);
if ($key === '' || ! array_key_exists($key, $map)) {
if ($key === '' || ! self::isSuperAdminOwned($key) || ! array_key_exists($key, $map)) {
return null;
}
@@ -267,9 +277,14 @@ class SuperAdminProgramSettingsOverlay
return in_array($key, self::RUNTIME_KEYS, true);
}
/**
* کلیدهای صفحهٔ «ساعت کاری» هم مالکشان پنل خانگی است — اگر از overlay خوانده شوند
* مقدار ذخیره‌شده در پنل هیچ‌وقت روی قیمت کاربر اعمال نمی‌شود.
*/
public static function isLocalOwned(string $key): bool
{
return in_array($key, self::LOCAL_OWNED_KEYS, true);
return in_array($key, self::LOCAL_OWNED_KEYS, true)
|| in_array($key, ConfigService::TRADING_HOURS_CLIENT_KEYS, true);
}
public static function isSuperAdminOwned(string $key): bool