setting pushed

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-24 10:23:31 +03:30
co-authored by Cursor
parent 82d961d984
commit 065f8af8ed
4 changed files with 85 additions and 21 deletions
+1 -1
View File
@@ -252,7 +252,7 @@ ${systemRows.map((r) => ` ${JSON.stringify(r.key)},`).join('\n')}
/**
* کلیدهایی که سوپرادمین مالکشان نیست (مالک: دیتابیس هر نصب).
* در هر بوت با purgeEditableFromSuperAdmin از قالب و ردیف همهٔ پروژه‌ها حذف می‌شوند.
* در هر بوت از قالب حذف می‌شوند؛ از ردیف هر پروژه فقط با دکمهٔ صفحهٔ تنظیمات همان پروژه.
*/
export const DEPLOY_SETTING_EDITABLE_KEYS = new Set<string>([
${editableRows.map((r) => ` ${JSON.stringify(r.key)},`).join('\n')}
@@ -20,7 +20,8 @@ export const DEPLOY_SETTING_CATALOG_KEYS = new Set<string>([
/**
* کلیدهایی که سوپرادمین مالکشان نیست (مالک: دیتابیس هر نصب).
* در هر بوت با purgeEditableFromSuperAdmin از قالب و ردیف همهٔ پروژه‌ها حذف می‌شوند.
* در هر بوت از قالب حذف می‌شوند؛ از ردیف هر پروژه فقط با دکمهٔ
* «حذف تنظیمات غیر دسترسی» در صفحهٔ تنظیمات همان پروژه.
*/
export const DEPLOY_SETTING_EDITABLE_KEYS = new Set<string>([
"about_software",
@@ -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);
}
}
@@ -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() {