2026-02-09 14:09:19 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
|
|
use App\Models\Setting;
|
2026-08-25 02:29:00 +03:30
|
|
|
use App\Services\ConfigService;
|
2026-07-08 13:15:29 +03:30
|
|
|
use App\Services\PriceChannelBroadcastService;
|
2026-08-18 16:10:37 +03:30
|
|
|
use App\Services\SuperAdminProgramSettingsOverlay;
|
2026-02-09 14:09:19 +03:30
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
|
|
|
|
|
class SettingsController extends Controller
|
|
|
|
|
{
|
2026-06-21 21:44:44 +03:30
|
|
|
private const DEPLOY_ADMIN_MOBILE_SUFFIX = '9128081392';
|
|
|
|
|
|
|
|
|
|
private const NEW_SETTING_NOTIFY_MOBILE = '09134469985';
|
|
|
|
|
|
2026-07-19 15:53:05 +03:30
|
|
|
/**
|
|
|
|
|
* کلیدهایی که پنل ادمین باید بتواند ویرایش کند حتی اگر در DB هنوز can_edit=0 باشد
|
|
|
|
|
* (بنرها / سایتهای ریگیری).
|
|
|
|
|
*/
|
|
|
|
|
private const PANEL_MANAGED_KEYS = [
|
|
|
|
|
'tracking_systems',
|
|
|
|
|
];
|
|
|
|
|
|
2026-06-04 16:01:13 +03:30
|
|
|
public function index(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$query = Setting::query()
|
2026-06-04 14:19:54 +03:30
|
|
|
->select(Setting::adminSelectColumns())
|
2026-06-04 16:01:13 +03:30
|
|
|
->orderBy('key');
|
2026-06-04 14:19:54 +03:30
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($request->boolean('system_only')) {
|
2026-08-19 19:55:29 +03:30
|
|
|
$local = $query->get();
|
2026-08-18 16:10:37 +03:30
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
return response()->json($this->composeSystemSettings($local));
|
2026-06-04 16:01:13 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($request->boolean('editable_only')) {
|
2026-08-19 20:15:28 +03:30
|
|
|
$local = $query->get();
|
|
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
return response()->json($this->composeEditableSettings($local));
|
2026-06-21 21:44:44 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:32:41 +03:30
|
|
|
$local = $query->get();
|
|
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
return response()->json($this->composeGetSettings($local));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* اپ کاربر (khanegi_user / theme / ghatreh) — فقط ردیفهای show_to_user=1، بدون credential.
|
|
|
|
|
* پنل ادمین از admin/get_settings استفاده میکند.
|
|
|
|
|
*/
|
|
|
|
|
public function publicIndex()
|
|
|
|
|
{
|
|
|
|
|
if (! Setting::hasShowToUserColumn()) {
|
|
|
|
|
return response()->json([]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 14:19:22 +03:30
|
|
|
$clientFeatureKeys = SuperAdminProgramSettingsOverlay::CLIENT_FEATURE_KEYS;
|
|
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
$local = Setting::query()
|
|
|
|
|
->select(Setting::adminSelectColumns())
|
2026-08-25 14:19:22 +03:30
|
|
|
->where(function ($query) use ($clientFeatureKeys) {
|
|
|
|
|
$query->where('show_to_user', 1)
|
|
|
|
|
->orWhereIn('key', $clientFeatureKeys);
|
|
|
|
|
})
|
2026-08-24 15:32:35 +03:30
|
|
|
->orderBy('key')
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
return response()->json(Setting::forClientApi($this->composeGetSettings($local)));
|
2026-08-18 18:52:29 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* جدول settings همین نسخه — بدون overlay سوپرادمین.
|
|
|
|
|
* سوپرادمین برای ایمپورت از این مسیر can_edit=0 را برمیدارد.
|
|
|
|
|
*/
|
|
|
|
|
public function localTable()
|
|
|
|
|
{
|
|
|
|
|
$rows = Setting::query()
|
|
|
|
|
->select(Setting::adminSelectColumns())
|
|
|
|
|
->orderBy('key')
|
|
|
|
|
->get();
|
|
|
|
|
|
2026-10-05 17:12:43 +03:30
|
|
|
return response()->json($this->maskSecretSettingValues($rows));
|
2026-02-09 14:09:19 +03:30
|
|
|
}
|
2026-06-21 21:44:44 +03:30
|
|
|
|
2026-08-24 21:22:44 +03:30
|
|
|
/**
|
|
|
|
|
* پاک کردن کش overlay و خواندن دوباره از سوپرادمین
|
2026-08-24 21:46:13 +03:30
|
|
|
* (همهٔ کلیدهای can_edit=0 مالک سوپرادمین — دسترسی مشتری به قابلیتها).
|
2026-08-24 21:22:44 +03:30
|
|
|
*/
|
|
|
|
|
public function refreshSuperAdminOverlay()
|
|
|
|
|
{
|
|
|
|
|
$result = app(SuperAdminProgramSettingsOverlay::class)->refreshFromSuperAdmin();
|
|
|
|
|
|
|
|
|
|
if (! ($result['ok'] ?? false)) {
|
|
|
|
|
return response()->json([
|
2026-08-29 22:39:08 +03:30
|
|
|
'message' => 'ارتباط با سوپرادمین برقرار نشد؛ در درخواست بعدی دوباره تلاش میشود',
|
2026-08-24 21:22:44 +03:30
|
|
|
'ok' => false,
|
|
|
|
|
'count' => 0,
|
2026-08-24 21:46:13 +03:30
|
|
|
'items' => [],
|
2026-08-29 22:39:08 +03:30
|
|
|
'meta' => $result['meta'] ?? [],
|
2026-08-24 21:22:44 +03:30
|
|
|
], 502);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 21:46:13 +03:30
|
|
|
$count = (int) ($result['count'] ?? 0);
|
2026-08-29 23:47:58 +03:30
|
|
|
$domain = trim((string) (($result['meta']['domain'] ?? '') ?: ''));
|
|
|
|
|
$csp = (string) (($result['items']['customer_special_pricing'] ?? '') ?: '');
|
|
|
|
|
$message = "همگام شد ({$count} کلید دسترسی از سوپرادمین)";
|
|
|
|
|
if ($domain !== '') {
|
|
|
|
|
$message .= " — دامنه: {$domain}";
|
|
|
|
|
}
|
|
|
|
|
if ($csp !== '') {
|
|
|
|
|
$message .= " — قیمت ویژه: {$csp}";
|
|
|
|
|
}
|
2026-08-24 21:46:13 +03:30
|
|
|
|
2026-08-24 21:22:44 +03:30
|
|
|
return response()->json([
|
2026-08-29 23:47:58 +03:30
|
|
|
'message' => $message,
|
2026-08-24 21:22:44 +03:30
|
|
|
'ok' => true,
|
2026-08-24 21:46:13 +03:30
|
|
|
'count' => $count,
|
|
|
|
|
'items' => $result['items'] ?? [],
|
2026-08-24 21:22:44 +03:30
|
|
|
'source' => $result['source'] ?? 'superadmin',
|
2026-08-29 22:39:08 +03:30
|
|
|
'meta' => $result['meta'] ?? [],
|
2026-08-24 21:22:44 +03:30
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-09 14:09:19 +03:30
|
|
|
public function edit(Request $request)
|
|
|
|
|
{
|
2026-08-23 22:34:18 +03:30
|
|
|
$key = trim((string) $request->key);
|
|
|
|
|
$isDeployAdmin = $this->isDeployAdminUser();
|
|
|
|
|
$isSystemDeploy = $request->boolean('system_deploy');
|
|
|
|
|
$setting = Setting::where('key', $key)->first();
|
2026-06-21 21:44:44 +03:30
|
|
|
$isNew = !$setting;
|
2026-02-09 14:09:19 +03:30
|
|
|
|
2026-08-23 22:34:18 +03:30
|
|
|
if (SuperAdminProgramSettingsOverlay::isSuperAdminOwned($key)) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => 'این تنظیم از سوپرادمین کنترل میشود و در پنل خانگی قابل ویرایش نیست',
|
|
|
|
|
], 403);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($isNew) {
|
2026-08-23 22:34:18 +03:30
|
|
|
if ($isSystemDeploy && !$isDeployAdmin) {
|
2026-08-18 16:10:37 +03:30
|
|
|
return response()->json([
|
2026-08-23 22:34:18 +03:30
|
|
|
'message' => 'افزودن تنظیم پیشرفته فقط برای ادمین دیپلوی مجاز است',
|
2026-08-18 16:10:37 +03:30
|
|
|
], 403);
|
2026-06-05 17:43:02 +03:30
|
|
|
}
|
2026-06-21 21:44:44 +03:30
|
|
|
|
2026-02-09 14:09:19 +03:30
|
|
|
$setting = new Setting();
|
2026-08-23 22:34:18 +03:30
|
|
|
$setting->key = $key;
|
|
|
|
|
$setting->can_edit = $isSystemDeploy ? 0 : 1;
|
2026-08-19 19:55:29 +03:30
|
|
|
} elseif (
|
2026-08-23 22:34:18 +03:30
|
|
|
SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
&& ! $this->isPanelManagedKey($key)
|
2026-08-19 19:55:29 +03:30
|
|
|
) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => 'این مقدار توسط سیستم بهروز میشود و از پنل قابل ویرایش نیست',
|
|
|
|
|
], 403);
|
2026-08-23 22:34:18 +03:30
|
|
|
} elseif (!$setting->can_edit && !$isDeployAdmin && !$this->isPanelManagedKey($key)) {
|
2026-08-18 16:10:37 +03:30
|
|
|
return response()->json([
|
2026-08-23 22:34:18 +03:30
|
|
|
'message' => 'این تنظیم پیشرفته است و فقط ادمین دیپلوی میتواند آن را تغییر دهد',
|
2026-08-18 16:10:37 +03:30
|
|
|
], 403);
|
2026-06-21 21:44:44 +03:30
|
|
|
}
|
|
|
|
|
|
2026-07-08 13:15:29 +03:30
|
|
|
$previousValue = $setting->exists ? (string) ($setting->value ?? '') : null;
|
|
|
|
|
|
2026-10-05 17:12:43 +03:30
|
|
|
$incomingValue = $request->value;
|
|
|
|
|
if (
|
|
|
|
|
in_array($key, ['shayegan_db_password', 'shayegan_auth_password'], true)
|
|
|
|
|
&& is_string($incomingValue)
|
|
|
|
|
&& ($incomingValue === '********' || $incomingValue === '')
|
|
|
|
|
&& $previousValue !== null
|
|
|
|
|
&& $previousValue !== ''
|
|
|
|
|
) {
|
|
|
|
|
// ماسک frontend را بهجای رمز واقعی ذخیره نکن
|
|
|
|
|
$incomingValue = $previousValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$setting->value = $incomingValue;
|
2026-06-21 21:44:44 +03:30
|
|
|
$setting->description = $request->description;
|
|
|
|
|
if ($request->has('is_active')) {
|
|
|
|
|
$setting->is_active = filter_var($request->is_active, FILTER_VALIDATE_BOOLEAN) ? 1 : 0;
|
|
|
|
|
} elseif ($isNew) {
|
|
|
|
|
$setting->is_active = 1;
|
|
|
|
|
}
|
|
|
|
|
$setting->title_fa = $request->title_fa ?? '';
|
|
|
|
|
if (Setting::hasTypeColumn()) {
|
|
|
|
|
$setting->type = $request->type ?? '';
|
|
|
|
|
}
|
|
|
|
|
$setting->save();
|
|
|
|
|
|
2026-07-08 13:15:29 +03:30
|
|
|
if (
|
|
|
|
|
$request->key === 'price_channel_message_text'
|
|
|
|
|
&& $previousValue !== (string) ($request->value ?? '')
|
|
|
|
|
) {
|
|
|
|
|
dispatch(function () {
|
|
|
|
|
app(PriceChannelBroadcastService::class)->broadcastAfterTemplateChange();
|
|
|
|
|
})->afterResponse();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-25 02:29:00 +03:30
|
|
|
if (app(ConfigService::class)->isTradingHoursSettingKey($key)) {
|
|
|
|
|
try {
|
|
|
|
|
app(ConfigService::class)->notifyTradingHoursChanged();
|
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
|
report($e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
if ($isNew) {
|
2026-06-21 21:44:44 +03:30
|
|
|
$projectName = Setting::where('key', 'name_fa')->value('value') ?: 'پروژه';
|
|
|
|
|
$settingTitle = trim($request->title_fa ?? '') ?: $request->key;
|
|
|
|
|
$this->Send(
|
|
|
|
|
self::NEW_SETTING_NOTIFY_MOBILE,
|
|
|
|
|
"تنظیم جدید: {$settingTitle}\nپروژه: {$projectName}",
|
|
|
|
|
false
|
|
|
|
|
);
|
2026-02-09 14:09:19 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json($setting);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
private function normalizeAdminMobile(?string $mobile): string
|
|
|
|
|
{
|
|
|
|
|
$digits = preg_replace('/\D+/', '', (string) $mobile) ?? '';
|
|
|
|
|
if (str_starts_with($digits, '98') && strlen($digits) >= 12) {
|
|
|
|
|
$digits = substr($digits, 2);
|
|
|
|
|
}
|
|
|
|
|
if (strlen($digits) === 11 && str_starts_with($digits, '0')) {
|
|
|
|
|
$digits = substr($digits, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $digits;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function isDeployAdminUser(): bool
|
|
|
|
|
{
|
|
|
|
|
$user = auth('user')->user() ?? auth()->user();
|
|
|
|
|
if (!$user) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->normalizeAdminMobile($user->mobile ?? '') === self::DEPLOY_ADMIN_MOBILE_SUFFIX;
|
|
|
|
|
}
|
2026-07-19 15:53:05 +03:30
|
|
|
|
|
|
|
|
private function isPanelManagedKey(string $key): bool
|
|
|
|
|
{
|
2026-08-25 03:01:07 +03:30
|
|
|
return in_array($key, self::PANEL_MANAGED_KEYS, true)
|
|
|
|
|
|| app(ConfigService::class)->isTradingHoursSettingKey($key);
|
2026-07-19 15:53:05 +03:30
|
|
|
}
|
2026-07-24 16:50:21 +03:30
|
|
|
|
2026-08-19 19:55:29 +03:30
|
|
|
/**
|
2026-09-05 17:01:39 +03:30
|
|
|
* تنظیمات پیشرفته: ۱۶ دسترسی از سوپرادمین + بقیهٔ can_edit=0 از DB لوکال.
|
2026-08-19 19:55:29 +03:30
|
|
|
*/
|
|
|
|
|
private function composeSystemSettings($localRows)
|
|
|
|
|
{
|
|
|
|
|
$overlay = app(SuperAdminProgramSettingsOverlay::class);
|
2026-08-19 22:02:10 +03:30
|
|
|
$saMap = $overlay->catalogMap();
|
2026-08-19 19:55:29 +03:30
|
|
|
|
|
|
|
|
$byKey = [];
|
2026-08-24 15:32:35 +03:30
|
|
|
$localByKey = [];
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
$localByKey[(string) $row->key] = $row;
|
|
|
|
|
}
|
2026-08-19 19:55:29 +03:30
|
|
|
|
|
|
|
|
if ($saMap !== null) {
|
|
|
|
|
foreach ($saMap as $key => $item) {
|
|
|
|
|
if (! is_array($item)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-08-25 03:01:07 +03:30
|
|
|
if (! SuperAdminProgramSettingsOverlay::isSuperAdminOwned((string) $key)) {
|
2026-08-19 20:15:28 +03:30
|
|
|
continue;
|
|
|
|
|
}
|
2026-08-19 22:02:10 +03:30
|
|
|
$value = $item['value'] ?? null;
|
|
|
|
|
if ($value !== null && $value !== '') {
|
2026-08-24 15:32:35 +03:30
|
|
|
$byKey[(string) $key] = $this->overlayRowToSetting($item, $localByKey[(string) $key] ?? null);
|
2026-08-19 22:02:10 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
$key = (string) $row->key;
|
|
|
|
|
if (isset($byKey[$key])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (
|
|
|
|
|
SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isLocalOwned($key)
|
|
|
|
|
) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (! $this->isEditableCanEdit($row->can_edit ?? null)) {
|
|
|
|
|
$byKey[$key] = $row;
|
|
|
|
|
}
|
2026-08-19 19:55:29 +03:30
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
if (! $this->isEditableCanEdit($row->can_edit ?? null)) {
|
|
|
|
|
$byKey[(string) $row->key] = $row;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
$key = (string) $row->key;
|
|
|
|
|
if (
|
|
|
|
|
SuperAdminProgramSettingsOverlay::isLocalOwned($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
) {
|
2026-08-19 20:15:28 +03:30
|
|
|
$byKey[$key] = $this->normalizeTenantOwnedRow($row);
|
2026-08-19 19:55:29 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return collect(array_values($byKey))->sortBy(fn ($row) => (string) ($row->key ?? ''))->values();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 20:15:28 +03:30
|
|
|
/**
|
|
|
|
|
* تنظیمات عادی پنل — can_edit=1 در DB + کلیدهای runtime (کد tenant مینویسد).
|
|
|
|
|
*/
|
|
|
|
|
private function composeEditableSettings($localRows)
|
|
|
|
|
{
|
|
|
|
|
$rows = collect($localRows)->filter(function ($row) {
|
|
|
|
|
$key = (string) ($row->key ?? '');
|
|
|
|
|
|
|
|
|
|
return $this->isEditableCanEdit($row->can_edit ?? null)
|
2026-08-25 03:01:07 +03:30
|
|
|
|| SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isLocalOwned($key);
|
2026-08-19 20:15:28 +03:30
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return $rows
|
|
|
|
|
->map(fn ($row) => $this->normalizeTenantOwnedRow($row))
|
|
|
|
|
->sortBy(fn ($row) => (string) ($row->key ?? ''))
|
|
|
|
|
->values();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:52:29 +03:30
|
|
|
/**
|
2026-08-24 15:32:35 +03:30
|
|
|
* get_settings ادمین (و پایهٔ publicIndex قبل از فیلتر show_to_user):
|
2026-08-25 03:01:07 +03:30
|
|
|
* can_edit=1 (+ runtime + local-owned) از DB لوکال
|
2026-09-05 17:01:39 +03:30
|
|
|
* فقط ۱۶ دسترسی سوپرادمین از overlay؛ بقیهٔ can_edit=0 از کپی لوکال
|
2026-08-18 18:52:29 +03:30
|
|
|
* اگر سوپرادمین در دسترس نباشد کل جدول لوکال برمیگردد.
|
|
|
|
|
*/
|
|
|
|
|
private function composeGetSettings($localRows)
|
2026-08-18 16:10:37 +03:30
|
|
|
{
|
2026-08-18 18:52:29 +03:30
|
|
|
$overlay = app(SuperAdminProgramSettingsOverlay::class);
|
2026-08-19 22:02:10 +03:30
|
|
|
$saMap = $overlay->catalogMap();
|
2026-08-18 18:52:29 +03:30
|
|
|
if ($saMap === null) {
|
2026-10-05 17:12:43 +03:30
|
|
|
return $this->maskSecretSettingValues($localRows);
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
$localByKey = [];
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
$localByKey[(string) $row->key] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
$byKey = [];
|
2026-08-18 18:52:29 +03:30
|
|
|
|
|
|
|
|
foreach ($localRows as $row) {
|
2026-08-19 22:02:10 +03:30
|
|
|
$key = (string) $row->key;
|
|
|
|
|
if (
|
|
|
|
|
$this->isEditableCanEdit($row->can_edit ?? null)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isLocalOwned($key)
|
|
|
|
|
) {
|
|
|
|
|
$byKey[$key] = $this->normalizeTenantOwnedRow($row);
|
2026-08-18 18:52:29 +03:30
|
|
|
}
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:52:29 +03:30
|
|
|
foreach ($saMap as $key => $item) {
|
|
|
|
|
if (! is_array($item)) {
|
2026-08-18 16:10:37 +03:30
|
|
|
continue;
|
|
|
|
|
}
|
2026-08-19 22:02:10 +03:30
|
|
|
$key = (string) $key;
|
2026-08-25 03:01:07 +03:30
|
|
|
if (! SuperAdminProgramSettingsOverlay::isSuperAdminOwned($key)) {
|
2026-08-19 20:15:28 +03:30
|
|
|
continue;
|
|
|
|
|
}
|
2026-08-19 22:02:10 +03:30
|
|
|
$value = $item['value'] ?? null;
|
|
|
|
|
if ($value !== null && $value !== '') {
|
2026-08-24 15:32:35 +03:30
|
|
|
$byKey[$key] = $this->overlayRowToSetting($item, $localByKey[$key] ?? null);
|
2026-08-19 22:02:10 +03:30
|
|
|
}
|
2026-08-18 18:52:29 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($localRows as $row) {
|
|
|
|
|
$key = (string) $row->key;
|
2026-08-19 22:02:10 +03:30
|
|
|
if (isset($byKey[$key])) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2026-08-18 18:52:29 +03:30
|
|
|
if (
|
2026-08-19 22:02:10 +03:30
|
|
|
SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isLocalOwned($key)
|
2026-08-18 18:52:29 +03:30
|
|
|
) {
|
2026-08-19 20:15:28 +03:30
|
|
|
$byKey[$key] = $this->normalizeTenantOwnedRow($row);
|
2026-08-19 22:02:10 +03:30
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (! $this->isEditableCanEdit($row->can_edit ?? null)) {
|
|
|
|
|
$byKey[$key] = $row;
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-05 17:12:43 +03:30
|
|
|
return $this->maskSecretSettingValues(
|
|
|
|
|
collect(array_values($byKey))->sortBy(fn ($row) => (string) ($row->key ?? ''))->values()
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* رمزهای شایگان و مشابه در پاسخ frontend برنگردند (فقط پر/خالی بودن).
|
|
|
|
|
*
|
|
|
|
|
* @param mixed $rows
|
|
|
|
|
* @return mixed
|
|
|
|
|
*/
|
|
|
|
|
private function maskSecretSettingValues($rows)
|
|
|
|
|
{
|
|
|
|
|
$secretKeys = [
|
|
|
|
|
'shayegan_db_password',
|
|
|
|
|
'shayegan_auth_password',
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$maskOne = function ($row) use ($secretKeys) {
|
|
|
|
|
if (is_array($row)) {
|
|
|
|
|
$key = (string) ($row['key'] ?? '');
|
|
|
|
|
if (in_array($key, $secretKeys, true)) {
|
|
|
|
|
$val = (string) ($row['value'] ?? '');
|
|
|
|
|
$row['value'] = $val !== '' ? '********' : '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
}
|
|
|
|
|
if (is_object($row) && isset($row->key)) {
|
|
|
|
|
$key = (string) $row->key;
|
|
|
|
|
if (in_array($key, $secretKeys, true)) {
|
|
|
|
|
$val = (string) ($row->value ?? '');
|
|
|
|
|
$row->value = $val !== '' ? '********' : '';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if ($rows instanceof \Illuminate\Support\Collection) {
|
|
|
|
|
return $rows->map($maskOne)->values();
|
|
|
|
|
}
|
|
|
|
|
if (is_array($rows)) {
|
|
|
|
|
return array_map($maskOne, $rows);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $rows;
|
2026-08-18 18:52:29 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function isEditableCanEdit(mixed $canEdit): bool
|
|
|
|
|
{
|
|
|
|
|
return $canEdit === 1 || $canEdit === true || $canEdit === '1';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function mergeOverlayIntoLocal($rows)
|
|
|
|
|
{
|
|
|
|
|
return $this->composeGetSettings($rows);
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param array<int, array<string, mixed>> $overlay
|
|
|
|
|
*/
|
|
|
|
|
private function overlayToRows(array $overlay)
|
|
|
|
|
{
|
|
|
|
|
return collect($overlay)
|
|
|
|
|
->filter(fn ($item) => is_array($item) && trim((string) ($item['key'] ?? '')) !== '')
|
|
|
|
|
->map(fn ($item) => $this->overlayRowToSetting($item))
|
|
|
|
|
->values();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-24 15:32:35 +03:30
|
|
|
private function overlayRowToSetting(array $item, ?Setting $local = null): Setting
|
2026-08-18 16:10:37 +03:30
|
|
|
{
|
|
|
|
|
$setting = new Setting();
|
|
|
|
|
$setting->id = isset($item['id']) && is_numeric($item['id']) ? (int) $item['id'] : null;
|
|
|
|
|
$setting->key = (string) ($item['key'] ?? '');
|
|
|
|
|
$setting->value = $item['value'] ?? '';
|
|
|
|
|
$setting->title_fa = $item['title_fa'] ?? '';
|
|
|
|
|
$setting->description = $item['description'] ?? '';
|
|
|
|
|
$setting->type = $item['type'] ?? '';
|
|
|
|
|
$setting->is_active = $item['is_active'] ?? 1;
|
|
|
|
|
$setting->can_edit = 0;
|
2026-08-24 15:32:35 +03:30
|
|
|
if ($local) {
|
|
|
|
|
if (Setting::hasMenuColumn()) {
|
|
|
|
|
$setting->menu = $local->menu;
|
|
|
|
|
}
|
|
|
|
|
if (Setting::hasShowToUserColumn()) {
|
|
|
|
|
$setting->show_to_user = $local->show_to_user;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-25 14:19:22 +03:30
|
|
|
// دسترسیهای منوی اپ کاربر همیشه به کلاینت میرسند (حتی اگر local show_to_user=0 یا absent)
|
|
|
|
|
if (
|
|
|
|
|
Setting::hasShowToUserColumn()
|
|
|
|
|
&& SuperAdminProgramSettingsOverlay::isClientFeatureKey((string) $setting->key)
|
|
|
|
|
) {
|
|
|
|
|
$setting->show_to_user = 1;
|
|
|
|
|
}
|
2026-08-18 16:10:37 +03:30
|
|
|
|
|
|
|
|
return $setting;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 20:15:28 +03:30
|
|
|
/** کلیدهای tenant-only — can_edit=1 در پاسخ API حتی اگر DB قدیمی 0 باشد */
|
|
|
|
|
private function normalizeTenantOwnedRow(Setting $row): Setting
|
|
|
|
|
{
|
|
|
|
|
$key = (string) ($row->key ?? '');
|
|
|
|
|
if (
|
|
|
|
|
SuperAdminProgramSettingsOverlay::isRuntime($key)
|
|
|
|
|
|| SuperAdminProgramSettingsOverlay::isLocalOwned($key)
|
|
|
|
|
) {
|
|
|
|
|
$row->can_edit = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $row;
|
|
|
|
|
}
|
2026-02-09 14:09:19 +03:30
|
|
|
}
|