feat(settings): import can_edit=0 values from khanegi settings table

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-18 18:52:31 +03:30
co-authored by Cursor
parent c4b814727a
commit 71f1fa1092
@@ -212,13 +212,15 @@ export class ProgramSettingsService implements OnModuleInit {
}> = [];
for (const [key, row] of remoteByKey) {
if (!this.isNewKhanegiCatalogKey(key, catalogKeys)) continue;
const localRow = localByKey.get(key);
const khanegiValue = String(row.value ?? '');
const superadminValue = localRow ? String(localRow.value ?? '') : null;
if (localRow && khanegiValue === superadminValue) continue;
decisionsNeeded.push({
key,
title_fa: String(row.title_fa || localRow?.title_fa || key),
khanegi_value: String(row.value ?? ''),
superadmin_value: localRow ? String(localRow.value ?? '') : null,
khanegi_value: khanegiValue,
superadmin_value: superadminValue,
status: localRow ? 'both' : 'new_in_khanegi',
});
}
@@ -230,9 +232,9 @@ export class ProgramSettingsService implements OnModuleInit {
apiBase,
decisionsNeeded,
onlySuperAdminKeys: onlySuperAdmin,
skippedCatalogCount: [...remoteByKey.keys()].filter((key) => catalogKeys.has(key)).length,
importableCount: remoteByKey.size,
note:
'فقط کلیدهایی که در قالب سوپرادمین نیستند. کلیدهای deploy_settings_const از قبل در قالب‌اند و از خانگی بازنویسی نمی‌شوند. کلیدهای فقط-سوپرادمین روی این پروژه با پیش‌فرض قالب تکمیل می‌شوند.',
'مقدار کلیدهای can_edit=0 خانگی روی همین پروژه نوشته می‌شود. اگر تصمیم نفرستی، پیش‌فرض «فقط این پروژه» است.',
};
}
@@ -251,12 +253,10 @@ export class ProgramSettingsService implements OnModuleInit {
const { apiBase, token } = await this.loginKhanegiAdmin(tenant, mobileNorm, password);
await this.ensureTemplatesSeeded();
const remote = await this.fetchSystemSettings(apiBase, token);
const catalogKeys = await this.catalogKeySet();
const remoteByKey = new Map(
remote
.map((row) => this.normalizeIncoming(row))
.filter((row) => row.key && !isRuntimeProgramSettingKey(row.key))
.filter((row) => this.isNewKhanegiCatalogKey(row.key, catalogKeys))
.map((row) => [row.key, row]),
);
@@ -283,7 +283,7 @@ export class ProgramSettingsService implements OnModuleInit {
await this.ensureTemplateDefaultsForTenant(tx, tenantId);
for (const [key, row] of remoteByKey) {
const scope = decisionMap.get(key) ?? 'none';
const scope = decisionMap.get(key) ?? 'this';
if (scope === 'none') continue;
const targetIds = scope === 'all' ? khanegiTenantIds : [tenantId];
@@ -525,13 +525,6 @@ export class ProgramSettingsService implements OnModuleInit {
return keys;
}
private isNewKhanegiCatalogKey(key: string, catalogKeys: Set<string>) {
if (!key || catalogKeys.has(key)) return false;
if (isRuntimeProgramSettingKey(key)) return false;
if (isSecretOrServerOnlySettingKey(key)) return false;
return true;
}
private async seedEmptyKhanegiTenants() {
const tenants = await this.prisma.tenant.findMany({
where: { product: ProductType.khanegi },
@@ -804,11 +797,10 @@ export class ProgramSettingsService implements OnModuleInit {
private async fetchSystemSettings(apiBase: string, token: string) {
const headers = { Authorization: `Bearer ${token}` };
const url = this.joinUrl(apiBase, '/admin/get_settings');
const url = this.joinUrl(apiBase, '/admin/settings/table');
try {
const { data } = await axios.get(url, {
headers,
params: { system_only: 1 },
timeout: 25000,
});
const items = this.asSettingsArray(data)
@@ -819,19 +811,23 @@ export class ProgramSettingsService implements OnModuleInit {
const status = axios.isAxiosError(error) ? error.response?.status : undefined;
if (status === 401 || status === 403) {
throw new BadRequestException(
this.axiosMessage(error, 'توکن ادمین خانگی برای get_settings رد شد'),
this.axiosMessage(error, 'توکن ادمین خانگی برای settings/table رد شد'),
);
}
}
try {
const { data } = await axios.get(url, { headers, timeout: 25000 });
const { data } = await axios.get(this.joinUrl(apiBase, '/admin/get_settings'), {
headers,
params: { system_only: 1, skip_overlay: 1 },
timeout: 25000,
});
return this.asSettingsArray(data)
.filter((row) => this.isSystemSetting(row))
.filter((row) => !isRuntimeProgramSettingKey(String(row.key || '')));
} catch (error) {
throw new BadGatewayException(
this.axiosMessage(error, 'خواندن get_settings از اپ خانگی ناموفق بود'),
this.axiosMessage(error, 'خواندن جدول settings از اپ خانگی ناموفق بود'),
);
}
}