2026-02-09 14:09:19 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Admin;
|
|
|
|
|
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
|
|
use App\Models\Setting;
|
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-08-18 18:32:41 +03:30
|
|
|
$skipOverlay = $request->boolean('skip_overlay');
|
|
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($request->boolean('system_only')) {
|
2026-08-18 18:32:41 +03:30
|
|
|
if (! $skipOverlay) {
|
|
|
|
|
$overlay = app(SuperAdminProgramSettingsOverlay::class)->items();
|
|
|
|
|
if ($overlay) {
|
|
|
|
|
return response()->json($this->authorizeSettingsPayload($this->overlayToRows($overlay)));
|
|
|
|
|
}
|
2026-08-18 16:10:37 +03:30
|
|
|
}
|
|
|
|
|
|
2026-07-24 16:50:21 +03:30
|
|
|
$rows = $query
|
|
|
|
|
->where(function ($q) {
|
|
|
|
|
$q->where('can_edit', 0)->orWhere('can_edit', false);
|
|
|
|
|
})
|
|
|
|
|
->where(function ($q) {
|
|
|
|
|
$q->where('is_active', 1)
|
|
|
|
|
->orWhere('is_active', true)
|
|
|
|
|
->orWhere('is_active', '1');
|
|
|
|
|
})
|
|
|
|
|
->get();
|
|
|
|
|
|
|
|
|
|
return response()->json($this->authorizeSettingsPayload($rows));
|
2026-06-04 16:01:13 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($request->boolean('editable_only')) {
|
2026-07-24 16:50:21 +03:30
|
|
|
return response()->json($this->authorizeSettingsPayload($query->where('can_edit', 1)->get()));
|
2026-06-21 21:44:44 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-18 18:32:41 +03:30
|
|
|
$local = $query->get();
|
|
|
|
|
if ($skipOverlay) {
|
|
|
|
|
return response()->json($this->authorizeSettingsPayload($local));
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
return response()->json($this->authorizeSettingsPayload(
|
2026-08-18 18:32:41 +03:30
|
|
|
$this->mergeOverlayIntoLocal($local)
|
2026-08-18 16:10:37 +03:30
|
|
|
));
|
2026-02-09 14:09:19 +03:30
|
|
|
}
|
2026-06-21 21:44:44 +03:30
|
|
|
|
2026-02-09 14:09:19 +03:30
|
|
|
public function edit(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$setting = Setting::where('key', $request->key)->first();
|
2026-06-21 21:44:44 +03:30
|
|
|
$isNew = !$setting;
|
2026-02-09 14:09:19 +03:30
|
|
|
|
2026-06-21 21:44:44 +03:30
|
|
|
if ($isNew) {
|
2026-08-18 16:10:37 +03:30
|
|
|
if ($request->boolean('system_deploy')) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => 'تنظیمات پیشرفته فقط از سوپرادمین اضافه میشوند',
|
|
|
|
|
], 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();
|
|
|
|
|
$setting->key = $request->key;
|
2026-08-18 16:10:37 +03:30
|
|
|
$setting->can_edit = 1;
|
|
|
|
|
} elseif (!$setting->can_edit && !$this->isPanelManagedKey((string) $request->key)) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => 'این تنظیم از سوپرادمین کنترل میشود و در پنل خانگی قابل ویرایش نیست',
|
|
|
|
|
], 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-06-21 21:44:44 +03:30
|
|
|
$setting->value = $request->value;
|
|
|
|
|
$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-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
|
|
|
|
|
{
|
|
|
|
|
return in_array($key, self::PANEL_MANAGED_KEYS, true);
|
|
|
|
|
}
|
2026-07-24 16:50:21 +03:30
|
|
|
|
2026-08-18 16:10:37 +03:30
|
|
|
private function mergeOverlayIntoLocal($rows)
|
|
|
|
|
{
|
|
|
|
|
$overlay = app(SuperAdminProgramSettingsOverlay::class)->items();
|
|
|
|
|
if (! $overlay) {
|
|
|
|
|
return $rows;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$byKey = [];
|
|
|
|
|
foreach ($rows as $row) {
|
|
|
|
|
$byKey[(string) $row->key] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($overlay as $item) {
|
|
|
|
|
$key = trim((string) ($item['key'] ?? ''));
|
|
|
|
|
if ($key === '' || SuperAdminProgramSettingsOverlay::isLocalOwned($key)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (isset($byKey[$key])) {
|
|
|
|
|
$byKey[$key]->value = $item['value'] ?? $byKey[$key]->value;
|
|
|
|
|
if (array_key_exists('title_fa', $item) && $item['title_fa'] !== null && $item['title_fa'] !== '') {
|
|
|
|
|
$byKey[$key]->title_fa = $item['title_fa'];
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('description', $item) && $item['description'] !== null) {
|
|
|
|
|
$byKey[$key]->description = $item['description'];
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('type', $item) && $item['type'] !== null && $item['type'] !== '') {
|
|
|
|
|
$byKey[$key]->type = $item['type'];
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('is_active', $item)) {
|
|
|
|
|
$byKey[$key]->is_active = $item['is_active'];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$byKey[$key] = $this->overlayRowToSetting($item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return collect(array_values($byKey));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function overlayRowToSetting(array $item): Setting
|
|
|
|
|
{
|
|
|
|
|
$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;
|
|
|
|
|
|
|
|
|
|
return $setting;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-24 16:50:21 +03:30
|
|
|
/**
|
|
|
|
|
* اپ کاربر (بدون auth ادمین) هرگز secret/credential نمیبیند.
|
|
|
|
|
* پنل ادمین با auth:user همه کلیدها را میبیند.
|
|
|
|
|
*
|
|
|
|
|
* @param \Illuminate\Support\Collection|\Illuminate\Database\Eloquent\Collection|iterable $rows
|
|
|
|
|
* @return \Illuminate\Support\Collection
|
|
|
|
|
*/
|
|
|
|
|
private function authorizeSettingsPayload($rows)
|
|
|
|
|
{
|
|
|
|
|
if (auth('user')->check()) {
|
|
|
|
|
return collect($rows)->values();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Setting::forPublicApi($rows);
|
|
|
|
|
}
|
2026-02-09 14:09:19 +03:30
|
|
|
}
|