From 027170a33faf728ca973bbbe2b0a65bb066041a8 Mon Sep 17 00:00:00 2001 From: adonis Date: Wed, 19 Aug 2026 19:55:29 +0330 Subject: [PATCH] fix(settings): fee only from fee_service_charge_percentage; deploy:tenant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove service_charge_percentage alias and config/fee.php fallback (empty → 0). Move code-managed Montzar/price runtime keys to can_edit=1 in deploy SQL. Add settings:deploy and deploy:tenant artisan commands for server deploy. Advanced admin settings merge runtime keys from local DB; block manual edits. Co-authored-by: Cursor --- README.md | 2 +- .../Commands/DeploySettingsConstCommand.php | 151 ++++++++++++++++++ app/Console/Commands/DeployTenantCommand.php | 55 +++++++ .../Controllers/Admin/SettingsController.php | 67 +++++--- app/Services/ConfigService.php | 12 +- .../SuperAdminProgramSettingsOverlay.php | 2 +- config/fee.php | 8 - database/sql/DEPLOY_CHECKLIST.md | 17 +- database/sql/deploy_settings_const.sql | 57 ++++--- ...ngs_const_preserve_value_sync_can_edit.sql | 54 ++++--- database/sql/run_deploy_settings.sh | 59 ++----- database/sql/run_deploy_tenant.sh | 11 ++ 12 files changed, 353 insertions(+), 142 deletions(-) create mode 100644 app/Console/Commands/DeploySettingsConstCommand.php create mode 100644 app/Console/Commands/DeployTenantCommand.php delete mode 100644 config/fee.php create mode 100644 database/sql/run_deploy_tenant.sh diff --git a/README.md b/README.md index bd53e66..489bd32 100644 --- a/README.md +++ b/README.md @@ -304,7 +304,7 @@ JWT_SECRET |------|--------| | PHP-FPM + Nginx | production استاندارد | | Docker | `docker-compose.yml` — app + MySQL | -| DB موجود | `baseline_migrations.sql` → `migrate` → `deploy_settings_const.sql` | +| DB موجود | `baseline_migrations.sql` → `php artisan deploy:tenant` | مستندات: diff --git a/app/Console/Commands/DeploySettingsConstCommand.php b/app/Console/Commands/DeploySettingsConstCommand.php new file mode 100644 index 0000000..5a408c3 --- /dev/null +++ b/app/Console/Commands/DeploySettingsConstCommand.php @@ -0,0 +1,151 @@ +resolveSqlPath(); + if ($path === null) { + return self::FAILURE; + } + + $cfg = config('database.connections.'.config('database.default')); + if (($cfg['driver'] ?? '') !== 'mysql') { + $this->error('فقط mysql پشتیبانی می‌شود.'); + + return self::FAILURE; + } + + $host = (string) ($cfg['host'] ?? '127.0.0.1'); + $port = (string) ($cfg['port'] ?? '3306'); + $user = (string) ($cfg['username'] ?? ''); + $pass = (string) ($cfg['password'] ?? ''); + $defaultDb = (string) ($cfg['database'] ?? ''); + + if ($user === '') { + $this->error('DB_USERNAME در .env خالی است.'); + + return self::FAILURE; + } + + $databases = $this->argument('database'); + if ($databases === [] || $databases === null) { + if ($defaultDb === '') { + $this->error('نام DB ندهید → DB_DATABASE را در .env بگذارید یا آرگومان بدهید.'); + + return self::FAILURE; + } + $databases = [$defaultDb]; + } + + $mysql = $this->findMysqlBinary(); + if ($mysql === null) { + $this->error('mysql client پیدا نشد. نصب کنید یا اجرا کنید: bash database/sql/run_deploy_settings.sh'); + + return self::FAILURE; + } + + $sql = file_get_contents($path); + if ($sql === false || trim($sql) === '') { + $this->error("فایل SQL خالی یا غیرقابل خواندن: {$path}"); + + return self::FAILURE; + } + + foreach ($databases as $db) { + $db = trim((string) $db); + if ($db === '') { + continue; + } + + $this->info("==> {$path} → {$db} @ {$host}:{$port}"); + + $process = new Process([ + $mysql, + '-h', $host, + '-P', $port, + '-u', $user, + '--default-character-set=utf8mb4', + $db, + ], null, ['MYSQL_PWD' => $pass]); + $process->setInput($sql); + $process->setTimeout(null); + $process->run(function ($type, $buffer) { + $line = trim($buffer); + if ($line === '') { + return; + } + if ($type === Process::ERR) { + $this->error($line); + } else { + $this->line($line); + } + }); + + if (! $process->isSuccessful()) { + $this->error("خطا روی دیتابیس: {$db}"); + + return self::FAILURE; + } + + $this->info(" done: {$db}"); + } + + $this->info('==> all done'); + + return self::SUCCESS; + } + + private function resolveSqlPath(): ?string + { + if ($this->option('preserve')) { + $relative = 'database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql'; + } else { + $relative = $this->option('file') ?: 'database/sql/deploy_settings_const.sql'; + } + + $path = str_starts_with($relative, '/') + ? $relative + : base_path($relative); + + if (! is_file($path)) { + $this->error("فایل SQL پیدا نشد: {$path}"); + + return null; + } + + return $path; + } + + private function findMysqlBinary(): ?string + { + $process = new Process(['command', '-v', 'mysql']); + $process->run(); + if ($process->isSuccessful()) { + $bin = trim($process->getOutput()); + if ($bin !== '') { + return $bin; + } + } + + foreach (['/usr/bin/mysql', '/usr/local/bin/mysql'] as $candidate) { + if (is_executable($candidate)) { + return $candidate; + } + } + + return null; + } +} diff --git a/app/Console/Commands/DeployTenantCommand.php b/app/Console/Commands/DeployTenantCommand.php new file mode 100644 index 0000000..056f655 --- /dev/null +++ b/app/Console/Commands/DeployTenantCommand.php @@ -0,0 +1,55 @@ +option('skip-migrate')) { + $this->info('==> 1/2 php artisan migrate --force'); + if ($this->call('migrate', ['--force' => true]) !== self::SUCCESS) { + $this->error('migrate ناموفق بود.'); + + return self::FAILURE; + } + } + + if (! $this->option('skip-settings')) { + $this->info('==> 2/2 php artisan settings:deploy'); + + $settingsArgs = []; + $databases = $this->argument('database'); + if ($databases !== [] && $databases !== null) { + $settingsArgs['database'] = $databases; + } + if ($this->option('preserve')) { + $settingsArgs['--preserve'] = true; + } + if ($this->option('file')) { + $settingsArgs['--file'] = $this->option('file'); + } + + if ($this->call('settings:deploy', $settingsArgs) !== self::SUCCESS) { + $this->error('settings:deploy ناموفق بود.'); + + return self::FAILURE; + } + } + + $this->info('==> deploy:tenant done'); + + return self::SUCCESS; + } +} diff --git a/app/Http/Controllers/Admin/SettingsController.php b/app/Http/Controllers/Admin/SettingsController.php index 38c4dd0..054088e 100644 --- a/app/Http/Controllers/Admin/SettingsController.php +++ b/app/Http/Controllers/Admin/SettingsController.php @@ -29,25 +29,11 @@ class SettingsController extends Controller ->orderBy('key'); if ($request->boolean('system_only')) { - if (! $request->boolean('skip_overlay')) { - $overlay = app(SuperAdminProgramSettingsOverlay::class)->items(); - if ($overlay) { - return response()->json($this->authorizeSettingsPayload($this->overlayToRows($overlay))); - } - } + $local = $query->get(); - $rows = $query - ->where(function ($q) { - $q->where('can_edit', 0)->orWhere('can_edit', false)->orWhere('can_edit', '0'); - }) - ->where(function ($q) { - $q->where('is_active', 1) - ->orWhere('is_active', true) - ->orWhere('is_active', '1'); - }) - ->get(); - - return response()->json($this->authorizeSettingsPayload($rows)); + return response()->json($this->authorizeSettingsPayload( + $this->composeSystemSettings($local) + )); } if ($request->boolean('editable_only')) { @@ -92,6 +78,13 @@ class SettingsController extends Controller $setting = new Setting(); $setting->key = $request->key; $setting->can_edit = 1; + } elseif ( + SuperAdminProgramSettingsOverlay::isRuntime((string) $request->key) + && ! $this->isPanelManagedKey((string) $request->key) + ) { + return response()->json([ + 'message' => 'این مقدار توسط سیستم به‌روز می‌شود و از پنل قابل ویرایش نیست', + ], 403); } elseif (!$setting->can_edit && !$this->isPanelManagedKey((string) $request->key)) { return response()->json([ 'message' => 'این تنظیم از سوپرادمین کنترل می‌شود و در پنل خانگی قابل ویرایش نیست', @@ -163,6 +156,44 @@ class SettingsController extends Controller return in_array($key, self::PANEL_MANAGED_KEYS, true); } + /** + * تنظیمات پیشرفته: can_edit=0 (سوپرادمین) + کلیدهای can_edit=1 که کد مستقیم می‌نویسد. + */ + private function composeSystemSettings($localRows) + { + $overlay = app(SuperAdminProgramSettingsOverlay::class); + $saMap = $overlay->map(); + + $byKey = []; + + if ($saMap !== null) { + foreach ($saMap as $key => $item) { + if (! is_array($item)) { + continue; + } + $byKey[(string) $key] = $this->overlayRowToSetting($item); + } + } 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) + ) { + $byKey[$key] = $row; + } + } + + return collect(array_values($byKey))->sortBy(fn ($row) => (string) ($row->key ?? ''))->values(); + } + /** * get_settings: کلیدهای سوپرادمین (can_edit=0) + ردیف‌های جدول لوکال با can_edit=1. * اگر سوپرادمین در دسترس نباشد کل جدول لوکال برمی‌گردد. diff --git a/app/Services/ConfigService.php b/app/Services/ConfigService.php index d226ea4..029a434 100644 --- a/app/Services/ConfigService.php +++ b/app/Services/ConfigService.php @@ -197,17 +197,15 @@ class ConfigService return max(0.0, (float) $this->get('montzar_diff', 0)); } - /** درصد کارمزد سفارش — fee_service_charge_percentage، service_charge_percentage، config/fee.php */ + /** درصد کارمزد سفارش — فقط fee_service_charge_percentage؛ خالی = ۰ */ public function orderServiceChargePercentage(): float { - foreach (['fee_service_charge_percentage', 'service_charge_percentage'] as $key) { - $raw = $this->getString($key, ''); - if ($raw !== '') { - return $this->normalizeServiceChargeRatio((float) $raw); - } + $raw = trim($this->getString('fee_service_charge_percentage', '')); + if ($raw === '') { + return 0.0; } - return $this->normalizeServiceChargeRatio((float) config('fee.service_charge_percentage', 0)); + return $this->normalizeServiceChargeRatio((float) $raw); } /** diff --git a/app/Services/SuperAdminProgramSettingsOverlay.php b/app/Services/SuperAdminProgramSettingsOverlay.php index 4abb73a..f51d899 100644 --- a/app/Services/SuperAdminProgramSettingsOverlay.php +++ b/app/Services/SuperAdminProgramSettingsOverlay.php @@ -13,7 +13,7 @@ class SuperAdminProgramSettingsOverlay 'tracking_systems', ]; - /** لاگ/قیمت لحظه‌ای همکار — مال دیتابیس نسخه است نه سوپرادمین */ + /** لاگ/قیمت لحظه‌ای همکار — can_edit=1 در DB؛ کد مستقیم UpdateConfig می‌زند، نه سوپرادمین */ public const RUNTIME_KEYS = [ 'montzar_last_product_log', 'montzar_order_response', diff --git a/config/fee.php b/config/fee.php deleted file mode 100644 index 5a4e44e..0000000 --- a/config/fee.php +++ /dev/null @@ -1,8 +0,0 @@ - 500000, - 'service_charge_percentage' => 0.01, - 'gold' => 0.01, - 'single' => 500000, - 'major' => 500000, -]; diff --git a/database/sql/DEPLOY_CHECKLIST.md b/database/sql/DEPLOY_CHECKLIST.md index 0f1096b..79a5f9a 100644 --- a/database/sql/DEPLOY_CHECKLIST.md +++ b/database/sql/DEPLOY_CHECKLIST.md @@ -38,15 +38,13 @@ mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USERNAME" -p"$DB_PASSWORD" "$DB_DATABA # ۳ status — ۱۰ تا همه Yes php artisan migrate:status -# ۴ migrate -php artisan migrate - -# ۵ settings -mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USERNAME" -p"$DB_PASSWORD" "$DB_DATABASE" \ - < database/sql/deploy_settings_const.sql +# ۴ migrate + ۵ settings (یک دستور) +php artisan deploy:tenant ``` -**یا:** `bash database/sql/run_deploy_existing_db.sh` +**یا جدا:** `php artisan migrate --force` سپس `php artisan settings:deploy` + +**یا:** `bash database/sql/run_deploy_tenant.sh` ### چک بعد از مرحله ۲ @@ -75,8 +73,7 @@ mysql -h 127.0.0.1 -P 3030 -u root -p soroushgold \ php artisan migrate:status php artisan migrate -mysql -h 127.0.0.1 -P 3030 -u root -p soroushgold \ - < database/sql/deploy_settings_const.sql +php artisan settings:deploy ``` --- @@ -87,7 +84,7 @@ mysql -h 127.0.0.1 -P 3030 -u root -p soroushgold \ mysql ... < database/schema/mysql-schema.sql.reference mysql ... < database/sql/baseline_migrations.sql php artisan migrate -mysql ... < database/sql/deploy_settings_const.sql +php artisan settings:deploy ``` --- diff --git a/database/sql/deploy_settings_const.sql b/database/sql/deploy_settings_const.sql index 317a76c..2830664 100644 --- a/database/sql/deploy_settings_const.sql +++ b/database/sql/deploy_settings_const.sql @@ -5,11 +5,12 @@ -- 0) بکاپ -- 1) mysql ... < database/sql/baseline_migrations.sql ← حتماً قبل از migrate -- 2) php artisan migrate --- 3) mysql ... < database/sql/deploy_settings_const.sql +-- 3) php artisan settings:deploy +-- (یا: bash database/sql/run_deploy_settings.sh) -- -- workflow نصب از صفر (DB خالی): -- 1) php artisan migrate ← schema dump + incremental --- 2) mysql ... < database/sql/deploy_settings_const.sql +-- 2) php artisan settings:deploy -- -- اصول: -- • چند بار قابل اجرا (idempotent) @@ -190,11 +191,11 @@ SELECT 'has_page_earn_money', 'false', 'صفحه کسب درآمد', 1, 1, NULL, WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'has_page_earn_money' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) -SELECT 'montzar_order_response', '[]', 'پاسخ سفارش همکار', 0, 1, NULL, 'longtext' +SELECT 'montzar_order_response', '[]', 'پاسخ سفارش همکار', 1, 1, NULL, 'longtext' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_order_response' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) -SELECT 'montzar_last_product_log', '{}', 'لاگ محصول همکار', 0, 1, NULL, 'longtext' +SELECT 'montzar_last_product_log', '{}', 'لاگ محصول همکار', 1, 1, NULL, 'longtext' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_last_product_log' LIMIT 1); -- قیمت zaarbox (اگر از قبل دارید value دست نخورده) @@ -281,7 +282,7 @@ SELECT 'montzar_base_url', 'https://limangold.ir', 'آدرس سرور همکار WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_base_url' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_token', '', 'توکن همکار (خودکار)', 0, 1, NULL +SELECT 'montzar_token', '', 'توکن همکار (خودکار)', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_token' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) @@ -293,23 +294,23 @@ SELECT 'montzar_open_sell', 'true', 'باز بودن فروش همکار', 1, 1, WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_open_sell' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_diff', '0', 'اختلاف قیمت همکار', 0, 1, 'سیستم پر می‌کند' +SELECT 'montzar_diff', '0', 'اختلاف قیمت همکار', 1, 1, 'سیستم پر می‌کند' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_diff' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_live_price', '0', 'قیمت لحظه‌ای همکار', 0, 1, NULL +SELECT 'montzar_live_price', '0', 'قیمت لحظه‌ای همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_live_price' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_mazaneh', '0', 'مظنه همکار', 0, 1, NULL +SELECT 'montzar_mazaneh', '0', 'مظنه همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_mazaneh' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_truly_buy_mazaneh', '0', 'مظنه خرید همکار', 0, 1, NULL +SELECT 'montzar_truly_buy_mazaneh', '0', 'مظنه خرید همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_truly_buy_mazaneh' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_truly_sell_mazaneh', '0', 'مظنه فروش همکار', 0, 1, NULL +SELECT 'montzar_truly_sell_mazaneh', '0', 'مظنه فروش همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_truly_sell_mazaneh' LIMIT 1); -- API خارجی Node (HMAC) @@ -1049,7 +1050,6 @@ UPDATE `settings` SET `title_fa` = 'وضعیت فروشگاه' WHERE `key` = 'st UPDATE `settings` SET `title_fa` = 'حالت فروشگاهی' WHERE `key` = 'is_store' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'محصولات متصل به کیف اصلی' WHERE `key` = 'main_wallet_products' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'واحد وزن' WHERE `key` = 'unit' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); -UPDATE `settings` SET `title_fa` = 'کارمزد درصدی (alias)' WHERE `key` = 'service_charge_percentage' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'حذف مشتری از تراز' WHERE `key` = 'balance_report_exclude_customer_id' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'منبع قیمت'), @@ -1190,7 +1190,7 @@ UPDATE `settings` SET `type` = 'price' WHERE `key` IN ( -- number (شماره، شناسه، درصد، تعداد — بدون جداکننده هزارگان) UPDATE `settings` SET `type` = 'number' WHERE `key` IN ( - 'fee_service_charge_percentage', 'fee_free', 'service_charge_percentage', + 'fee_service_charge_percentage', 'fee_free', 'montzar_product_id', 'gift_for_referrer', 'gift_for_referral', 'payping_fin_customer_id', 'tahesab_port', 'referral_limit', 'deposit_sms_support_number', @@ -1229,11 +1229,8 @@ INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_ SELECT 'price_provider', 'منبع قیمت', 'montzar', 1, 0, 1, 'select', 'منبع فعال قیمت|montzar,zaarbox,main_product' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_provider' LIMIT 1); -INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_active`, `type`) -SELECT 'service_charge_percentage', 'کارمزد درصدی (alias)', COALESCE( - (SELECT `value` FROM `settings` WHERE `key` = 'fee_service_charge_percentage' LIMIT 1), '0.005' -), 0, 0, 1, 'number' -WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'service_charge_percentage' LIMIT 1); +-- حذف alias قدیمی؛ منبع واحد: fee_service_charge_percentage +DELETE FROM `settings` WHERE `key` = 'service_charge_percentage'; INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_active`, `type`) SELECT 'title_pwa', 'عنوان PWA', '', 0, 0, 0, 'text' @@ -1878,7 +1875,6 @@ UPDATE `settings` SET `title_fa` = 'خرید پاداش معرفی از همکا UPDATE `settings` SET `title_fa` = 'سود فروش نمایش' WHERE `key` = 'sell_fee'; UPDATE `settings` SET `title_fa` = 'ارسال به ته‌حساب' WHERE `key` = 'send_to_tahesab'; UPDATE `settings` SET `title_fa` = 'آدرس API سرور' WHERE `key` = 'server_url'; -UPDATE `settings` SET `title_fa` = 'کارمزد درصدی (alias)' WHERE `key` = 'service_charge_percentage'; UPDATE `settings` SET `title_fa` = 'نمایش آخرین قیمت ذخیره‌شده' WHERE `key` = 'show_last_price'; UPDATE `settings` SET `title_fa` = 'نمایش قیمت انس طلا' WHERE `key` = 'show_gold_ounce_price'; UPDATE `settings` SET `title_fa` = 'نمایش قیمت صفحه اصلی خارج از ساعت کاری' WHERE `key` = 'show_home_price_when_closed'; @@ -1958,24 +1954,20 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN ( 'logo_login', 'logo_register', 'title_pwa', 'title_fav', 'main_product', 'main_wallet_products', 'metal_type', 'main_metal', 'montify_name', - 'montzar_base_url', 'montzar_closed', - 'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh', - 'montzar_order_response', 'montzar_token', - 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', + 'montzar_base_url', 'node_client_id', 'node_shared_secret', 'payping_fin_customer_id', 'payping_fin_sync_enabled', 'payping_fin_token', 'payping_fin_url', 'payping_gateway_items', 'payping_gateway_name', 'payping_round_toman', 'payping_start_url', 'payping_strict_failed_withdraw', 'payping_verify_url', 'permision__enabled', - 'price_extreme_date', 'price_provider', 'profit_gold_api_enabled', - 'send_to_tahesab', 'service_charge_percentage', + 'price_provider', 'profit_gold_api_enabled', + 'send_to_tahesab', 'sse_path', 'tahesab_bank_account', 'tahesab_db', 'tahesab_host', 'tahesab_port', 'tahesab_token', 'telegram_bot_token', 'rubika_bot_token', 'price_channel_access', 'price_channel_bale_to', 'price_channel_telegram_to', 'price_channel_rubika_to', 'unit', - 'up_price', 'down_price', 'wss_enabled', 'zaarbox_api_url', 'show_to_users_chat', 'chat_api', 'chat_api_key', 'chat_upload_url', 'chat_download_url', 'chat_webhook_secret', 'chat_webhook_base_url' @@ -2005,6 +1997,9 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'min_first_buy', 'min_withdraw', 'min_deposit', 'minimum_sell', 'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell', 'montzar_password', 'montzar_product_id', + 'montzar_closed', 'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh', + 'montzar_order_response', 'montzar_token', + 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', 'open_register', 'referral_id', 'referral_show', 'referral_limit', 'referral_gift_on_first_order', 'referral_gift_buy_from_montzar', 'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer', @@ -2013,6 +2008,7 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'products_site_link_enabled', 'products_site_link_url', 'products_site_link_text', 'startDay', 'support_messenger', 'support_messenger_id', 'support_messenger_text', 'support_messengers', + 'up_price', 'down_price', 'price_extreme_date', 'do_send_liman', 'has_chat', 'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_account_number', 'deposit_sheba', 'deposit_method_card_to_card', 'deposit_method_gateway', 'deposit_method_identified', 'deposit_method_shetab', @@ -2026,6 +2022,15 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'zaarbox_price_key' ); +-- کلیدهای runtime — فقط tenant DB؛ can_edit=1 (خارج از سوپرادمین) +UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( + 'montzar_last_product_log', 'montzar_order_response', + 'montzar_live_price', 'montzar_mazaneh', 'montzar_diff', + 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', + 'montzar_token', 'montzar_closed', + 'up_price', 'down_price', 'price_extreme_date', 'do_send_liman' +); + -- تفکیک نهایی type (بعد از همه batchها — override روی دیتابیس موجود) UPDATE `settings` SET `type` = 'price' WHERE `key` IN ( 'buy_fee', 'sell_fee', @@ -2033,7 +2038,7 @@ UPDATE `settings` SET `type` = 'price' WHERE `key` IN ( 'up_price', 'down_price' ); UPDATE `settings` SET `type` = 'number' WHERE `key` IN ( - 'fee_service_charge_percentage', 'fee_free', 'service_charge_percentage', + 'fee_service_charge_percentage', 'fee_free', 'montzar_product_id', 'gift_for_referrer', 'gift_for_referral', 'payping_fin_customer_id', 'tahesab_port', 'referral_limit', 'deposit_sms_support_number', diff --git a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql index 8f0f707..12e6d32 100644 --- a/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql +++ b/database/sql/deploy_settings_const_preserve_value_sync_can_edit.sql @@ -5,11 +5,12 @@ -- 0) بکاپ -- 1) mysql ... < database/sql/baseline_migrations.sql ← حتماً قبل از migrate -- 2) php artisan migrate --- 3) mysql ... < database/sql/deploy_settings_const.sql +-- 3) php artisan settings:deploy +-- (یا: php artisan settings:deploy --preserve) -- -- workflow نصب از صفر (DB خالی): -- 1) php artisan migrate ← schema dump + incremental --- 2) mysql ... < database/sql/deploy_settings_const.sql +-- 2) php artisan settings:deploy --preserve -- -- اصول: -- • چند بار قابل اجرا (idempotent) @@ -148,7 +149,7 @@ SELECT 'has_page_earn_money', 'false', 'صفحه کسب درآمد', 1, 1, NULL, WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'has_page_earn_money' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`, `type`) -SELECT 'montzar_order_response', '[]', 'پاسخ سفارش همکار', 0, 1, NULL, 'longtext' +SELECT 'montzar_order_response', '[]', 'پاسخ سفارش همکار', 1, 1, NULL, 'longtext' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_order_response' LIMIT 1); -- قیمت zaarbox (اگر از قبل دارید value دست نخورده) @@ -231,7 +232,7 @@ SELECT 'montzar_base_url', 'https://limangold.ir', 'آدرس سرور همکار WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_base_url' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_token', '', 'توکن همکار (خودکار)', 0, 1, NULL +SELECT 'montzar_token', '', 'توکن همکار (خودکار)', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_token' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) @@ -243,23 +244,23 @@ SELECT 'montzar_open_sell', 'true', 'باز بودن فروش همکار', 1, 1, WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_open_sell' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_diff', '0', 'اختلاف قیمت همکار', 0, 1, 'سیستم پر می‌کند' +SELECT 'montzar_diff', '0', 'اختلاف قیمت همکار', 1, 1, 'سیستم پر می‌کند' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_diff' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_live_price', '0', 'قیمت لحظه‌ای همکار', 0, 1, NULL +SELECT 'montzar_live_price', '0', 'قیمت لحظه‌ای همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_live_price' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_mazaneh', '0', 'مظنه همکار', 0, 1, NULL +SELECT 'montzar_mazaneh', '0', 'مظنه همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_mazaneh' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_truly_buy_mazaneh', '0', 'مظنه خرید همکار', 0, 1, NULL +SELECT 'montzar_truly_buy_mazaneh', '0', 'مظنه خرید همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_truly_buy_mazaneh' LIMIT 1); INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) -SELECT 'montzar_truly_sell_mazaneh', '0', 'مظنه فروش همکار', 0, 1, NULL +SELECT 'montzar_truly_sell_mazaneh', '0', 'مظنه فروش همکار', 1, 1, NULL WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'montzar_truly_sell_mazaneh' LIMIT 1); -- API خارجی Node (HMAC) @@ -792,7 +793,6 @@ UPDATE `settings` SET `title_fa` = 'وضعیت فروشگاه' WHERE `key` = 'st UPDATE `settings` SET `title_fa` = 'حالت فروشگاهی' WHERE `key` = 'is_store' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'محصولات متصل به کیف اصلی' WHERE `key` = 'main_wallet_products' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'واحد وزن' WHERE `key` = 'unit' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); -UPDATE `settings` SET `title_fa` = 'کارمزد درصدی (alias)' WHERE `key` = 'service_charge_percentage' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = 'حذف مشتری از تراز' WHERE `key` = 'balance_report_exclude_customer_id' AND (`title_fa` IS NULL OR TRIM(`title_fa`) = ''); UPDATE `settings` SET `title_fa` = COALESCE(NULLIF(TRIM(`title_fa`), ''), 'منبع قیمت'), @@ -928,7 +928,7 @@ UPDATE `settings` SET `type` = 'price' WHERE `key` IN ( -- number (شماره، شناسه، درصد، تعداد — بدون جداکننده هزارگان) UPDATE `settings` SET `type` = 'number' WHERE `key` IN ( - 'fee_service_charge_percentage', 'fee_free', 'service_charge_percentage', + 'fee_service_charge_percentage', 'fee_free', 'montzar_product_id', 'gift_for_referrer', 'gift_for_referral', 'payping_fin_customer_id', 'tahesab_port', 'referral_limit', 'deposit_sms_support_number' @@ -963,11 +963,8 @@ INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_ SELECT 'price_provider', 'منبع قیمت', 'montzar', 1, 0, 1, 'select', 'منبع فعال قیمت|montzar,zaarbox,main_product' WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'price_provider' LIMIT 1); -INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_active`, `type`) -SELECT 'service_charge_percentage', 'کارمزد درصدی (alias)', COALESCE( - (SELECT `value` FROM `settings` WHERE `key` = 'fee_service_charge_percentage' LIMIT 1), '0.005' -), 0, 0, 1, 'number' -WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'service_charge_percentage' LIMIT 1); +-- حذف alias قدیمی؛ منبع واحد: fee_service_charge_percentage +DELETE FROM `settings` WHERE `key` = 'service_charge_percentage'; INSERT INTO `settings` (`key`, `title_fa`, `value`, `can_edit`, `is_exist`, `is_active`, `type`) SELECT 'title_pwa', 'عنوان PWA', '', 0, 0, 0, 'text' @@ -1482,22 +1479,18 @@ UPDATE `settings` SET `can_edit` = 0 WHERE `key` IN ( 'logo_login', 'logo_register', 'title_pwa', 'title_fav', 'main_product', 'main_wallet_products', 'metal_type', 'main_metal', 'montify_name', - 'montzar_base_url', 'montzar_closed', - 'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh', - 'montzar_order_response', 'montzar_token', - 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', + 'montzar_base_url', 'node_client_id', 'node_shared_secret', 'payping_fin_customer_id', 'payping_fin_sync_enabled', 'payping_fin_token', 'payping_fin_url', 'payping_gateway_items', 'payping_gateway_name', 'payping_round_toman', 'payping_start_url', 'payping_strict_failed_withdraw', 'payping_verify_url', 'permision__enabled', - 'price_extreme_date', 'price_provider', 'profit_gold_api_enabled', - 'send_to_tahesab', 'service_charge_percentage', + 'price_provider', 'profit_gold_api_enabled', + 'send_to_tahesab', 'sse_path', 'tahesab_bank_account', 'tahesab_db', 'tahesab_host', 'tahesab_port', 'tahesab_token', 'telegram_bot_token', 'unit', - 'up_price', 'down_price', 'wss_enabled', 'zaarbox_api_url' ); @@ -1525,6 +1518,9 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'min_first_buy', 'min_withdraw', 'min_deposit', 'minimum_sell', 'montzar_mobile', 'montzar_open_buy', 'montzar_open_sell', 'montzar_password', 'montzar_product_id', + 'montzar_closed', 'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh', + 'montzar_order_response', 'montzar_token', + 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', 'open_register', 'referral_id', 'referral_show', 'referral_limit', 'referral_gift_on_first_order', 'referral_gift_buy_from_montzar', 'register_jibit_verify', 'register_referral_bonus_message', 'register_referral_sms_to_referrer', @@ -1533,6 +1529,7 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'products_site_link_enabled', 'products_site_link_url', 'products_site_link_text', 'startDay', 'support_messenger', 'support_messenger_id', 'support_messenger_text', + 'up_price', 'down_price', 'price_extreme_date', 'do_send_liman', 'deposit_support_message_text', 'deposit_account_holder', 'deposit_card_number', 'deposit_sheba', 'text_about', 'text_rule', 'tracking_systems', 'tracking_systems_enabled', @@ -1540,6 +1537,15 @@ UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( 'zaarbox_price_key' ); +-- کلیدهای runtime — فقط tenant DB؛ can_edit=1 (خارج از سوپرادمین) +UPDATE `settings` SET `can_edit` = 1 WHERE `key` IN ( + 'montzar_last_product_log', 'montzar_order_response', + 'montzar_live_price', 'montzar_mazaneh', 'montzar_diff', + 'montzar_truly_buy_mazaneh', 'montzar_truly_sell_mazaneh', + 'montzar_token', 'montzar_closed', + 'up_price', 'down_price', 'price_extreme_date', 'do_send_liman' +); + UPDATE `settings` SET `can_edit` = 1 WHERE `key` = 'min_deposit'; UPDATE `settings` SET `title_fa` = 'حداقل واریز کیف پول (ریال)' WHERE `key` = 'min_deposit'; @@ -1550,7 +1556,7 @@ UPDATE `settings` SET `type` = 'price' WHERE `key` IN ( 'up_price', 'down_price' ); UPDATE `settings` SET `type` = 'number' WHERE `key` IN ( - 'fee_service_charge_percentage', 'fee_free', 'service_charge_percentage', + 'fee_service_charge_percentage', 'fee_free', 'montzar_product_id', 'gift_for_referrer', 'gift_for_referral', 'payping_fin_customer_id', 'tahesab_port', 'referral_limit', 'deposit_sms_support_number' diff --git a/database/sql/run_deploy_settings.sh b/database/sql/run_deploy_settings.sh index 88a654b..7e1bfc4 100644 --- a/database/sql/run_deploy_settings.sh +++ b/database/sql/run_deploy_settings.sh @@ -1,60 +1,25 @@ #!/usr/bin/env bash -# اجرای deploy_settings_const.sql روی یک یا چند دیتابیس — بدون کپی در Navicat. +# اجرای deploy_settings_const.sql — ترجیحاً از artisan (اتصال .env): # -# از ریشهٔ سرور: -# bash database/sql/run_deploy_settings.sh -# bash database/sql/run_deploy_settings.sh abaeigold -# bash database/sql/run_deploy_settings.sh abaeigold gholaktala +# php artisan settings:deploy +# php artisan settings:deploy tenant_db_name +# php artisan settings:deploy --preserve +# php artisan settings:deploy db1 db2 # -# اتصال از .env (DB_HOST / DB_PORT / DB_USERNAME / DB_PASSWORD). -# اگر نام DB ندهی، همان DB_DATABASE داخل .env استفاده می‌شود. +# این اسکریپت همان artisan را صدا می‌زند. set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOT" -SQL_FILE="${SQL_FILE:-database/sql/deploy_settings_const.sql}" - -read_env() { - grep "^${1}=" .env | cut -d= -f2- | tr -d '\r"' | head -1 -} - -DB_HOST=$(read_env DB_HOST) -DB_PORT=$(read_env DB_PORT) -DB_USERNAME=$(read_env DB_USERNAME) -DB_PASSWORD=$(read_env DB_PASSWORD) -DB_DATABASE=$(read_env DB_DATABASE) - -if [[ ! -f "$SQL_FILE" ]]; then - echo "ERROR: SQL file not found: $SQL_FILE" >&2 +if [[ ! -f artisan ]]; then + echo "ERROR: artisan not found in $ROOT" >&2 exit 1 fi -if [[ -z "${DB_HOST:-}" || -z "${DB_USERNAME:-}" ]]; then - echo "ERROR: DB_HOST / DB_USERNAME missing in .env" >&2 - exit 1 +ARGS=() +if [[ -n "${SQL_FILE:-}" && "${SQL_FILE}" != "database/sql/deploy_settings_const.sql" ]]; then + ARGS+=(--file="$SQL_FILE") fi -if [[ $# -gt 0 ]]; then - DBS=("$@") -else - if [[ -z "${DB_DATABASE:-}" ]]; then - echo "ERROR: no database name. Pass it as argument or set DB_DATABASE in .env" >&2 - exit 1 - fi - DBS=("$DB_DATABASE") -fi - -mysql_run() { - local db="$1" - mysql -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USERNAME" -p"$DB_PASSWORD" \ - --default-character-set=utf8mb4 "$db" -} - -for db in "${DBS[@]}"; do - echo "==> $SQL_FILE → $db @ $DB_HOST:${DB_PORT:-3306}" - mysql_run "$db" < "$SQL_FILE" - echo " done: $db" -done - -echo "==> all done" +exec php artisan settings:deploy "${ARGS[@]}" "$@" diff --git a/database/sql/run_deploy_tenant.sh b/database/sql/run_deploy_tenant.sh new file mode 100644 index 0000000..2ac7de8 --- /dev/null +++ b/database/sql/run_deploy_tenant.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +# migrate + deploy_settings_const.sql — یک دستور از ریشهٔ khaneghi_server +# +# bash database/sql/run_deploy_tenant.sh +# php artisan deploy:tenant +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "$ROOT" + +exec php artisan deploy:tenant "$@"