From 065f8af8edb96613a14c1ad4823bb57c81305a33 Mon Sep 17 00:00:00 2001 From: adonis Date: Mon, 24 Aug 2026 10:23:31 +0330 Subject: [PATCH] setting pushed Co-authored-by: Cursor --- scripts/sync-superadmin-setting-template.mjs | 2 +- .../deploy-setting-catalog-keys.ts | 3 +- .../program-settings.controller.ts | 10 ++ .../program-settings.service.ts | 91 +++++++++++++++---- 4 files changed, 85 insertions(+), 21 deletions(-) diff --git a/scripts/sync-superadmin-setting-template.mjs b/scripts/sync-superadmin-setting-template.mjs index 670993f..34866c5 100644 --- a/scripts/sync-superadmin-setting-template.mjs +++ b/scripts/sync-superadmin-setting-template.mjs @@ -252,7 +252,7 @@ ${systemRows.map((r) => ` ${JSON.stringify(r.key)},`).join('\n')} /** * کلیدهایی که سوپرادمین مالکشان نیست (مالک: دیتابیس هر نصب). - * در هر بوت با purgeEditableFromSuperAdmin از قالب و ردیف همهٔ پروژه‌ها حذف می‌شوند. + * در هر بوت از قالب حذف می‌شوند؛ از ردیف هر پروژه فقط با دکمهٔ صفحهٔ تنظیمات همان پروژه. */ export const DEPLOY_SETTING_EDITABLE_KEYS = new Set([ ${editableRows.map((r) => ` ${JSON.stringify(r.key)},`).join('\n')} diff --git a/src/program-settings/deploy-setting-catalog-keys.ts b/src/program-settings/deploy-setting-catalog-keys.ts index c5dc3eb..0367b0f 100644 --- a/src/program-settings/deploy-setting-catalog-keys.ts +++ b/src/program-settings/deploy-setting-catalog-keys.ts @@ -20,7 +20,8 @@ export const DEPLOY_SETTING_CATALOG_KEYS = new Set([ /** * کلیدهایی که سوپرادمین مالکشان نیست (مالک: دیتابیس هر نصب). - * در هر بوت با purgeEditableFromSuperAdmin از قالب و ردیف همهٔ پروژه‌ها حذف می‌شوند. + * در هر بوت از قالب حذف می‌شوند؛ از ردیف هر پروژه فقط با دکمهٔ + * «حذف تنظیمات غیر دسترسی» در صفحهٔ تنظیمات همان پروژه. */ export const DEPLOY_SETTING_EDITABLE_KEYS = new Set([ "about_software", diff --git a/src/program-settings/program-settings.controller.ts b/src/program-settings/program-settings.controller.ts index 7f9664e..5021282 100644 --- a/src/program-settings/program-settings.controller.ts +++ b/src/program-settings/program-settings.controller.ts @@ -46,4 +46,14 @@ export class ProgramSettingsController { user.id, ); } + + /** + * بعد از restore لوکال: فقط کلیدهای «دسترسی مشتری به قابلیت‌ها» روی این پروژه + * در سوپرادمین می‌مانند؛ بقیه از جدول settings همین tenant حذف می‌شوند. + */ + @Post('purge-non-owned') + @RequirePermissions('khanegi:write') + purgeNonOwned(@Param('tenantId') tenantId: string, @CurrentUser() user: { id: string }) { + return this.programSettings.purgeNonOwnedFromTenant(tenantId, user.id); + } } diff --git a/src/program-settings/program-settings.service.ts b/src/program-settings/program-settings.service.ts index c7c5076..88a4a35 100644 --- a/src/program-settings/program-settings.service.ts +++ b/src/program-settings/program-settings.service.ts @@ -70,11 +70,10 @@ export class ProgramSettingsService implements OnModuleInit { if (result.seeded) { this.logger.log(`setting_templates seeded (${result.count} keys)`); } - const purged = await this.purgeEditableFromSuperAdmin(); - if (purged.templates || purged.settings) { - this.logger.log( - `purged can_edit=1 from superadmin (templates=${purged.templates}, settings=${purged.settings})`, - ); + // فقط قالب: حذف ردیف پروژه‌ها دونه به دونه با دکمهٔ صفحهٔ تنظیمات همان پروژه + const purged = await this.purgeNonOwnedTemplates(); + if (purged.templates) { + this.logger.log(`purged non-owned keys from setting_templates (templates=${purged.templates})`); } await this.seedEmptyKhanegiTenants(); } catch (error: any) { @@ -599,25 +598,79 @@ export class ProgramSettingsService implements OnModuleInit { return false; } - /** حذف can_edit=1 از قالب و همهٔ پروژه‌های خانگی در DB سوپرادمین */ - async purgeEditableFromSuperAdmin() { - const keys = [...DEPLOY_SETTING_EDITABLE_KEYS]; - if (!keys.length) return { templates: 0, settings: 0 }; + /** + * قالب سوپرادمین فقط کلیدهای مالکیت سوپرادمین (دسترسی مشتری) می‌ماند. + * ردیف پروژه‌ها را پاک نمی‌کند — آن کار با purgeNonOwnedFromTenant است. + */ + async purgeNonOwnedTemplates() { + const keep = [...DEPLOY_SETTING_CATALOG_KEYS]; + if (!keep.length) return { templates: 0 }; - const result = await this.prisma.$transaction(async (tx: any) => { - const templates = await tx.settingTemplate.deleteMany({ - where: { key: { in: keys } }, - }); - const settings = await tx.setting.deleteMany({ - where: { key: { in: keys } }, - }); - return { templates: templates.count, settings: settings.count }; + const templates = await this.prisma.settingTemplate.deleteMany({ + where: { NOT: { key: { in: keep } } }, }); - if (result.templates || result.settings) { + if (templates.count) { this.replicaWatch.kick(); } - return result; + return { templates: templates.count }; + } + + /** سازگاری با اندپوینت قدیمی seed/purge-editable — فقط قالب */ + async purgeEditableFromSuperAdmin() { + const templates = await this.purgeNonOwnedTemplates(); + return { templates: templates.templates, settings: 0 }; + } + + /** + * بعد از restore لوکال روی همان نصب: همهٔ تنظیمات این پروژه در DB سوپرادمین + * که خارج از «دسترسی مشتری به قابلیت‌ها»اند حذف می‌شوند. + */ + async purgeNonOwnedFromTenant(tenantId: string, userId?: string) { + const tenant = await this.requireKhanegiTenant(tenantId); + const keep = [...DEPLOY_SETTING_CATALOG_KEYS]; + + const before = await this.prisma.setting.findMany({ + where: { + tenantId, + NOT: { key: { in: keep } }, + }, + select: { key: true }, + }); + const removedKeys = before.map((row: { key: string }) => row.key).sort(); + + const deleted = await this.prisma.setting.deleteMany({ + where: { + tenantId, + NOT: { key: { in: keep } }, + }, + }); + + await this.audit.log({ + action: 'tenant.program_settings.purge_non_owned', + entityType: 'tenant_program_settings', + entityId: tenantId, + tenantId, + userId, + after: { + removed: deleted.count, + keys: removedKeys, + kept: keep, + } as Prisma.InputJsonValue, + }); + + if (deleted.count) { + this.replicaWatch.kick(); + } + + const items = await this.listTenantRows(tenantId); + return { + tenant: this.tenantMeta(tenant), + removed: deleted.count, + removedKeys, + items, + count: items.length, + }; } private deployCatalogCanEditMap() {