fix: lock new.gholaktala.ir as Khanegi newTheme (userTheme)
Correct prior modular/Hamkar misclassification. Protect locked newTheme domains from bulk-modular brand overrides. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,13 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* Apply Niazi-style modular brand colors to all Khanegi tenants
|
* Apply Niazi-style modular brand colors to Khanegi tenants
|
||||||
* that are NOT newTheme (Aygin stays newTheme).
|
* that are NOT locked newTheme (Aygin, new.gholaktala.ir, …).
|
||||||
*
|
*
|
||||||
* Colors (from app.niazitala.ir):
|
* Colors (from app.niazitala.ir):
|
||||||
* primary #0a1b37 (هدر)
|
* primary #0a1b37 (هدر)
|
||||||
* secondary #e0be7f (اکسنت)
|
* secondary #e0be7f (اکسنت)
|
||||||
* background #ffffff (پسزمینه)
|
* background #ffffff (پسزمینه)
|
||||||
*
|
*
|
||||||
* Also forces uiVariant=modular for non-Aygin Khanegi rows.
|
* Also forces uiVariant=modular for non-protected Khanegi rows.
|
||||||
*
|
*
|
||||||
* Dry-run:
|
* Dry-run:
|
||||||
* KHANEGI_BRAND_DRY_RUN=1 npm run prisma:bulk-khanegi-modular-brand
|
* KHANEGI_BRAND_DRY_RUN=1 npm run prisma:bulk-khanegi-modular-brand
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
* KHANEGI_BRAND_SOURCE_DOMAIN=app.niazitala.ir npm run prisma:bulk-khanegi-modular-brand
|
* KHANEGI_BRAND_SOURCE_DOMAIN=app.niazitala.ir npm run prisma:bulk-khanegi-modular-brand
|
||||||
*/
|
*/
|
||||||
import { KhanegiUiVariant, PrismaClient, ProductType } from '@prisma/client';
|
import { KhanegiUiVariant, PrismaClient, ProductType } from '@prisma/client';
|
||||||
|
import { isForcedKhanegiNewTheme } from './khanegi-variant-locks';
|
||||||
|
|
||||||
const prisma = new PrismaClient();
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
@@ -42,20 +43,9 @@ function normalizeHost(value?: string | null) {
|
|||||||
.split(':')[0];
|
.split(':')[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
function isAyginTenant(domain: string, domains: unknown, slug: string) {
|
function isProtectedNewThemeTenant(domain: string, domains: unknown, slug: string) {
|
||||||
const hosts = new Set<string>();
|
// Aygin + explicit locks (e.g. new.gholaktala.ir)
|
||||||
const primary = normalizeHost(domain);
|
return isForcedKhanegiNewTheme(domain, domains, slug);
|
||||||
if (primary) hosts.add(primary);
|
|
||||||
const extra = Array.isArray(domains) ? domains : [];
|
|
||||||
for (const d of extra) {
|
|
||||||
const n = normalizeHost(String(d));
|
|
||||||
if (n) hosts.add(n);
|
|
||||||
}
|
|
||||||
if (String(slug || '').toLowerCase().includes('aygin')) return true;
|
|
||||||
for (const h of hosts) {
|
|
||||||
if (h.includes('aygingold') || h === 'app.aygingold.ir') return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function bumpAppVersion(current?: string | null): string {
|
function bumpAppVersion(current?: string | null): string {
|
||||||
@@ -134,10 +124,10 @@ async function main() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const targets = tenants.filter(
|
const targets = tenants.filter(
|
||||||
(t) => !isAyginTenant(t.domain, t.domains, t.slug),
|
(t) => !isProtectedNewThemeTenant(t.domain, t.domains, t.slug),
|
||||||
);
|
);
|
||||||
const skippedAygin = tenants.filter((t) =>
|
const skippedNewTheme = tenants.filter((t) =>
|
||||||
isAyginTenant(t.domain, t.domains, t.slug),
|
isProtectedNewThemeTenant(t.domain, t.domains, t.slug),
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
@@ -153,7 +143,7 @@ async function main() {
|
|||||||
},
|
},
|
||||||
totalKhanegi: tenants.length,
|
totalKhanegi: tenants.length,
|
||||||
targets: targets.length,
|
targets: targets.length,
|
||||||
skippedAygin: skippedAygin.map((t) => ({
|
skippedNewTheme: skippedNewTheme.map((t) => ({
|
||||||
domain: t.domain,
|
domain: t.domain,
|
||||||
uiVariant: t.uiVariant,
|
uiVariant: t.uiVariant,
|
||||||
})),
|
})),
|
||||||
@@ -204,8 +194,8 @@ async function main() {
|
|||||||
updated += 1;
|
updated += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure Aygin stays newTheme (do not touch colors unless variant wrong)
|
// Ensure locked newTheme tenants stay newTheme
|
||||||
for (const t of skippedAygin) {
|
for (const t of skippedNewTheme) {
|
||||||
if (t.uiVariant === KhanegiUiVariant.newTheme) continue;
|
if (t.uiVariant === KhanegiUiVariant.newTheme) continue;
|
||||||
console.log(
|
console.log(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ const FORCE_KHANEGI: Array<{ domain?: string; name?: string; externalId?: number
|
|||||||
{ domain: 'abaeigold.ir' },
|
{ domain: 'abaeigold.ir' },
|
||||||
{ domain: 'niazitala.ir' },
|
{ domain: 'niazitala.ir' },
|
||||||
{ domain: 'garoosigholaktala.ir' },
|
{ domain: 'garoosigholaktala.ir' },
|
||||||
|
// Khanegi newTheme (userTheme front) — NOT modular, NOT Hamkar.
|
||||||
|
// Keep uiVariant=newTheme in DB; see prisma/khanegi-variant-locks.ts
|
||||||
{ domain: 'new.gholaktala.ir' },
|
{ domain: 'new.gholaktala.ir' },
|
||||||
{ domain: 'kh.kashkolgold.ir' },
|
{ domain: 'kh.kashkolgold.ir' },
|
||||||
{ domain: 'goldsun.zaarbox.ir' },
|
{ domain: 'goldsun.zaarbox.ir' },
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Khanegi tenants that MUST stay on userTheme (uiVariant=newTheme).
|
||||||
|
* Never force these to modular in bulk brand scripts.
|
||||||
|
*
|
||||||
|
* Frontend: KhanegiModular/userTheme
|
||||||
|
* API: /api/get_settings (NOT Hamkar /site/settings, NOT userModular-only assumptions)
|
||||||
|
*/
|
||||||
|
export const FORCE_KHANEGI_NEW_THEME_DOMAINS = [
|
||||||
|
'new.gholaktala.ir',
|
||||||
|
'app.aygingold.ir',
|
||||||
|
] as const
|
||||||
|
|
||||||
|
export function normalizeTenantHost(value?: string | null) {
|
||||||
|
if (!value) return ''
|
||||||
|
return String(value)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/^https?:\/\//, '')
|
||||||
|
.split('/')[0]
|
||||||
|
.split(':')[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectTenantHosts(domain: string, domains: unknown, slug = '') {
|
||||||
|
const hosts = new Set<string>()
|
||||||
|
const primary = normalizeTenantHost(domain)
|
||||||
|
if (primary) hosts.add(primary)
|
||||||
|
const extra = Array.isArray(domains) ? domains : []
|
||||||
|
for (const d of extra) {
|
||||||
|
const n = normalizeTenantHost(String(d))
|
||||||
|
if (n) hosts.add(n)
|
||||||
|
}
|
||||||
|
const s = String(slug || '').toLowerCase()
|
||||||
|
if (s) hosts.add(s)
|
||||||
|
return hosts
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isForcedKhanegiNewTheme(domain: string, domains: unknown, slug = '') {
|
||||||
|
const hosts = collectTenantHosts(domain, domains, slug)
|
||||||
|
const slugLower = String(slug || '').toLowerCase()
|
||||||
|
if (slugLower.includes('aygin') || slugLower.includes('gholak_new')) return true
|
||||||
|
|
||||||
|
for (const h of hosts) {
|
||||||
|
if (h === 'new.gholaktala.ir') return true
|
||||||
|
if (h.includes('aygingold') || h === 'app.aygingold.ir') return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const locked of FORCE_KHANEGI_NEW_THEME_DOMAINS) {
|
||||||
|
const needle = normalizeTenantHost(locked)
|
||||||
|
if (hosts.has(needle)) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore new.gholaktala.ir as Khanegi + newTheme (front: userTheme).
|
||||||
|
*
|
||||||
|
* On production:
|
||||||
|
* cd /var/www/superadmin/api && git pull
|
||||||
|
* set -a && . ./.env && set +a
|
||||||
|
* node scripts/fix-gholaktala-to-khanegi-newtheme.mjs
|
||||||
|
* node scripts/fix-gholaktala-to-khanegi-newtheme.mjs --apply
|
||||||
|
*
|
||||||
|
* Then verify:
|
||||||
|
* curl -s 'https://adm.montzar.ir/get_user?domain=new.gholaktala.ir' | head -c 500
|
||||||
|
* Expect: "product":"khanegi" and "ui_variant":"newTheme"
|
||||||
|
*/
|
||||||
|
const prisma = new PrismaClient()
|
||||||
|
const APPLY = process.argv.includes('--apply')
|
||||||
|
const DOMAIN = 'new.gholaktala.ir'
|
||||||
|
|
||||||
|
function bumpAppVersion(current) {
|
||||||
|
const v = String(current || '1').trim() || '1'
|
||||||
|
if (/^\d+$/.test(v)) return String(Number(v) + 1)
|
||||||
|
const semver = v.match(/^(\d+)\.(\d+)\.(\d+)$/)
|
||||||
|
if (semver) return `${semver[1]}.${semver[2]}.${Number(semver[3]) + 1}`
|
||||||
|
return String(Date.now())
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const tenants = await prisma.tenant.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ domain: { equals: DOMAIN, mode: 'insensitive' } },
|
||||||
|
{ domains: { string_contains: DOMAIN } },
|
||||||
|
{ domain: { contains: 'gholaktala', mode: 'insensitive' } },
|
||||||
|
{ slug: { contains: 'gholak', mode: 'insensitive' } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
nameFa: true,
|
||||||
|
domain: true,
|
||||||
|
slug: true,
|
||||||
|
product: true,
|
||||||
|
uiVariant: true,
|
||||||
|
appVersion: true,
|
||||||
|
notes: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('MATCHES', JSON.stringify(tenants, null, 2))
|
||||||
|
|
||||||
|
const tenant =
|
||||||
|
tenants.find((t) => String(t.domain).toLowerCase() === DOMAIN) || tenants[0]
|
||||||
|
|
||||||
|
if (!tenant) throw new Error(`Tenant not found for ${DOMAIN}`)
|
||||||
|
|
||||||
|
const alreadyOk = tenant.product === 'khanegi' && tenant.uiVariant === 'newTheme'
|
||||||
|
|
||||||
|
console.log('SELECTED', {
|
||||||
|
id: tenant.id,
|
||||||
|
domain: tenant.domain,
|
||||||
|
nameFa: tenant.nameFa,
|
||||||
|
product: tenant.product,
|
||||||
|
uiVariant: tenant.uiVariant,
|
||||||
|
alreadyOk,
|
||||||
|
expected: {
|
||||||
|
product: 'khanegi',
|
||||||
|
ui_variant: 'newTheme',
|
||||||
|
frontend: 'KhanegiModular/userTheme (NOT userModular, NOT Hamkar)',
|
||||||
|
api: '/server/api/get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!APPLY) {
|
||||||
|
console.log('DRY_RUN — re-run with --apply to set product=khanegi uiVariant=newTheme')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updated = await prisma.tenant.update({
|
||||||
|
where: { id: tenant.id },
|
||||||
|
data: {
|
||||||
|
product: 'khanegi',
|
||||||
|
uiVariant: 'newTheme',
|
||||||
|
appVersion: alreadyOk ? tenant.appVersion : bumpAppVersion(tenant.appVersion),
|
||||||
|
notes: [
|
||||||
|
String(tenant.notes || '')
|
||||||
|
.replace(/\s*\|\s*product=hamkar[^\|]*/gi, '')
|
||||||
|
.replace(/\s*\|\s*product=khanegi[^\|]*/gi, '')
|
||||||
|
.trim(),
|
||||||
|
'product=khanegi uiVariant=newTheme (userTheme locked)',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' | '),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('UPDATED', {
|
||||||
|
id: updated.id,
|
||||||
|
domain: updated.domain,
|
||||||
|
product: updated.product,
|
||||||
|
uiVariant: updated.uiVariant,
|
||||||
|
appVersion: updated.appVersion,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch((e) => {
|
||||||
|
console.error(e)
|
||||||
|
process.exitCode = 1
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect()
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user