@open-mercato/core 0.6.6-develop.6535.1.5cf43724de → 0.6.6-develop.6540.1.ae856df1ec

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/api_keys/cli.js +2 -1
  3. package/dist/modules/api_keys/cli.js.map +2 -2
  4. package/dist/modules/attachments/api/file/[id]/route.js +9 -7
  5. package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
  6. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +9 -7
  7. package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
  8. package/dist/modules/attachments/api/route.js +21 -17
  9. package/dist/modules/attachments/api/route.js.map +2 -2
  10. package/dist/modules/attachments/lib/reconcileOrganization.js +110 -0
  11. package/dist/modules/attachments/lib/reconcileOrganization.js.map +7 -0
  12. package/dist/modules/attachments/lib/requestScope.js +10 -0
  13. package/dist/modules/attachments/lib/requestScope.js.map +7 -0
  14. package/dist/modules/auth/api/users/route.js +6 -2
  15. package/dist/modules/auth/api/users/route.js.map +2 -2
  16. package/dist/modules/auth/cli.js +3 -2
  17. package/dist/modules/auth/cli.js.map +2 -2
  18. package/dist/modules/configs/lib/upgrade-actions.js +22 -0
  19. package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
  20. package/dist/modules/dashboards/cli.js +4 -3
  21. package/dist/modules/dashboards/cli.js.map +2 -2
  22. package/dist/modules/directory/utils/organizationScope.js +10 -3
  23. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  24. package/dist/modules/entities/api/records.js +5 -4
  25. package/dist/modules/entities/api/records.js.map +2 -2
  26. package/dist/modules/entities/cli.js +2 -1
  27. package/dist/modules/entities/cli.js.map +2 -2
  28. package/package.json +7 -7
  29. package/src/modules/api_keys/cli.ts +2 -3
  30. package/src/modules/attachments/api/file/[id]/route.ts +11 -7
  31. package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +11 -7
  32. package/src/modules/attachments/api/route.ts +26 -21
  33. package/src/modules/attachments/lib/reconcileOrganization.ts +188 -0
  34. package/src/modules/attachments/lib/requestScope.ts +27 -0
  35. package/src/modules/auth/api/users/route.ts +11 -2
  36. package/src/modules/auth/cli.ts +3 -2
  37. package/src/modules/configs/i18n/de.json +5 -1
  38. package/src/modules/configs/i18n/en.json +5 -1
  39. package/src/modules/configs/i18n/es.json +5 -1
  40. package/src/modules/configs/i18n/pl.json +5 -1
  41. package/src/modules/configs/lib/upgrade-actions.ts +24 -0
  42. package/src/modules/dashboards/cli.ts +4 -9
  43. package/src/modules/directory/utils/organizationScope.ts +36 -2
  44. package/src/modules/entities/api/records.ts +5 -7
  45. package/src/modules/entities/cli.ts +2 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/auth/cli.ts"],
4
- "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { OM_LOG_LEVEL_ENV, resetLogLevelCache } from '@open-mercato/shared/lib/logger'\nimport { resetServerLoggerCache } from '@open-mercato/shared/lib/logger'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant, ensureDefaultRoleAcls, ensureCustomRoleAcls, OrgSlugExistsError, DerivedUserPasswordRequiredError } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { computeEmailHash, emailHashLookupValues } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\nimport { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { getCliModules } from '@open-mercato/shared/modules/registry'\n\nasync function resolveTenantScopedRole(em: any, name: string, normalizedTenantId: string | null) {\n const existing = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (existing) return existing\n const role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persist(role).flush()\n return role\n}\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persist(u).flush()\n if (rolesCsv) {\n const names = rolesCsv.split(',').map(s => s.trim()).filter(Boolean)\n for (const name of names) {\n const role = await resolveTenantScopedRole(em, name, normalizedTenantId)\n const link = em.create(UserRole, { user: u, role })\n await em.persist(link).flush()\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nfunction ensurePasswordPolicy(password: string): boolean {\n const policy = getPasswordPolicy()\n const result = validatePassword(password, policy)\n if (result.ok) return true\n const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)\n const suffix = requirements ? `: ${requirements}` : ''\n console.error(`Password does not meet the requirements${suffix}.`)\n return false\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persist(tenant).flush()\n const org = em.create(Organization, { name, tenant })\n await em.persist(org).flush()\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst SETUP_USAGE = 'Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--orgSlug <slug>] [--roles superadmin,admin,employee] [--skip-password-policy] [--include-demo-users] [--with-examples] [--json]'\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args = parseArgs(rest)\n const orgName = typeof args.orgName === 'string'\n ? args.orgName\n : typeof args.name === 'string'\n ? args.name\n : undefined\n const email = typeof args.email === 'string' ? args.email : undefined\n const password = typeof args.password === 'string' ? args.password : undefined\n const orgSlugRaw = typeof args.orgSlug === 'string'\n ? args.orgSlug\n : typeof args.slug === 'string'\n ? args.slug\n : undefined\n const orgSlug = orgSlugRaw && orgSlugRaw.trim().length ? orgSlugRaw.trim() : undefined\n if (orgSlug !== undefined && !ORG_SLUG_PATTERN.test(orgSlug)) {\n process.stderr.write(`Invalid --orgSlug: must match ${ORG_SLUG_PATTERN.source} (lowercase, digits, dashes; 1-63 chars; cannot start or end with a dash)\\n`)\n process.exitCode = 2\n return\n }\n const rolesCsv = typeof args.roles === 'string'\n ? args.roles.trim()\n : 'superadmin,admin,employee'\n const skipPasswordPolicyRaw =\n args['skip-password-policy'] ??\n args.skipPasswordPolicy ??\n args['allow-weak-password'] ??\n args.allowWeakPassword\n const skipPasswordPolicy = typeof skipPasswordPolicyRaw === 'boolean'\n ? skipPasswordPolicyRaw\n : parseBooleanToken(typeof skipPasswordPolicyRaw === 'string' ? skipPasswordPolicyRaw : null) ?? false\n const withExamplesRaw = args['with-examples'] ?? args.withExamples\n const withExamples = typeof withExamplesRaw === 'boolean'\n ? withExamplesRaw\n : parseBooleanToken(typeof withExamplesRaw === 'string' ? withExamplesRaw : null) ?? false\n const includeDemoUsersRaw = args['include-demo-users'] ?? args.includeDemoUsers\n const includeDemoUsers = typeof includeDemoUsersRaw === 'boolean'\n ? includeDemoUsersRaw\n : parseBooleanToken(typeof includeDemoUsersRaw === 'string' ? includeDemoUsersRaw : null) ?? false\n const jsonModeRaw = args.json\n const jsonMode = typeof jsonModeRaw === 'boolean'\n ? jsonModeRaw\n : parseBooleanToken(typeof jsonModeRaw === 'string' ? jsonModeRaw : null) ?? false\n if (!orgName || !email || !password) {\n process.stderr.write(`${SETUP_USAGE}\\n`)\n process.exitCode = 2\n return\n }\n if (!skipPasswordPolicy && !ensurePasswordPolicy(password)) {\n process.exitCode = 2\n return\n }\n if (skipPasswordPolicy && !jsonMode) {\n console.warn('\u26A0\uFE0F Password policy validation skipped for setup.')\n }\n let restoreConsole: (() => void) | null = null\n if (jsonMode) {\n // Force quiet mode so module bootstrap / lifecycle hooks cannot pollute\n // stdout. The JSON contract relies on a single line, so any console.log\n // from setupInitialTenant or seedExamples would otherwise break the\n // consumer's `jq` pipe. We additionally rebind console.log/info to a\n // no-op for the duration of the call because OM_CLI_QUIET is honored\n // unevenly across module setup hooks. Rebind AFTER early-return\n // validation so the missing-args and password-policy paths can't\n // leak the rebinding into a long-lived process (e.g. test runner).\n process.env.OM_CLI_QUIET = process.env.OM_CLI_QUIET ?? '1'\n const originalLog = console.log\n const originalInfo = console.info\n console.log = () => undefined\n console.info = () => undefined\n const originalLogLevel = process.env[OM_LOG_LEVEL_ENV]\n process.env[OM_LOG_LEVEL_ENV] = 'error'\n resetLogLevelCache()\n resetServerLoggerCache()\n restoreConsole = () => {\n console.log = originalLog\n console.info = originalInfo\n if (originalLogLevel === undefined) delete process.env[OM_LOG_LEVEL_ENV]\n else process.env[OM_LOG_LEVEL_ENV] = originalLogLevel\n resetLogLevelCache()\n resetServerLoggerCache()\n }\n }\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? rolesCsv.split(',').map((s) => s.trim()).filter(Boolean)\n : undefined\n\n try {\n const modules = getCliModules()\n const result = await setupInitialTenant(em, {\n orgName,\n orgSlug,\n roleNames,\n primaryUser: { email, password, confirm: true },\n // Derived admin/employee demo accounts only land when the operator\n // explicitly opts in via --include-demo-users. Default-deny prevents\n // production deployments from silently seeding well-known emails.\n includeDerivedUsers: includeDemoUsers,\n // When demo users are explicitly requested, the operator has consented\n // to having admin@/employee@ accounts created with autogenerated\n // passwords (surfaced in stdout) when no env override is provided.\n allowDemoDerivedPasswords: includeDemoUsers,\n // When the caller passes an explicit slug, treat it as a \"fresh tenant\"\n // signal \u2014 silent reuse of an existing user's tenant defeats the point\n // of slugging the new tenant for downstream tooling.\n failIfUserExists: orgSlug !== undefined,\n modules,\n })\n\n if (withExamples) {\n const seedCtx = {\n em,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n container,\n }\n for (const mod of modules) {\n if (mod.setup?.seedExamples) {\n await mod.setup.seedExamples(seedCtx as any)\n }\n }\n }\n\n if (jsonMode) {\n const adminSnapshot = pickAdminSnapshot(result.users, email)\n const adminUserId = adminSnapshot && adminSnapshot.user.id != null\n ? String(adminSnapshot.user.id)\n : null\n const payload = {\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n adminUserId,\n adminEmail: email,\n reusedExistingUser: result.reusedExistingUser,\n }\n if (restoreConsole) restoreConsole()\n process.stdout.write(`${JSON.stringify(payload)}\\n`)\n return\n }\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if (env.NODE_ENV !== 'test') {\n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.generatedPassword) {\n console.log('\u26A0\uFE0F GENERATED password \u2014 copy now; it is not stored in plain text')\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', snapshot.generatedPassword)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if (env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (restoreConsole) restoreConsole()\n if (err instanceof OrgSlugExistsError) {\n process.stderr.write(`${err.message}\\n`)\n process.exitCode = 1\n return\n }\n if (err instanceof DerivedUserPasswordRequiredError) {\n process.stderr.write(\n `Setup aborted: ${err.message}. In production, set OM_INIT_ADMIN_PASSWORD and OM_INIT_EMPLOYEE_PASSWORD before passing --include-demo-users, or omit --include-demo-users to skip derived demo accounts entirely.\\n`,\n )\n process.exitCode = 2\n return\n }\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n process.stderr.write('Setup aborted: user already exists with the provided email.\\n')\n process.exitCode = 1\n return\n }\n throw err\n } finally {\n if (restoreConsole) restoreConsole()\n }\n },\n}\n\nconst ORG_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/\n\nfunction pickAdminSnapshot(\n users: Array<{ user: { id: string; email: string }; roles: string[]; created: boolean }>,\n adminEmail: string,\n): { user: { id: string; email: string } } | undefined {\n const normalizedEmail = adminEmail.toLowerCase()\n const exact = users.find(\n (snapshot) =>\n snapshot.created &&\n snapshot.roles.includes('superadmin') &&\n typeof snapshot.user.email === 'string' &&\n snapshot.user.email.toLowerCase() === normalizedEmail,\n )\n if (exact) return exact\n return users.find((snapshot) => snapshot.roles.includes('superadmin'))\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n\n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n\n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n\n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n\n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n\n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n\n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n\n const users = await em.find(User, where)\n\n if (users.length === 0) {\n console.log('No users found')\n return\n }\n\n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n\n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n\n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n\n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name ? `${org.name.substring(0, 19)}...` : `${user.organizationId.substring(0, 8)}...`\n }\n\n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name ? `${tenant.name.substring(0, 19)}...` : `${user.tenantId.substring(0, 8)}...`\n }\n\n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n\n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n\n const email = args.email\n const password = args.password\n\n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const user = await findOneWithDecryption(\n em,\n User,\n { $or: [{ email }, { emailHash: { $in: emailHashLookupValues(email) } }] } as any,\n undefined,\n { tenantId: null, organizationId: null },\n )\n\n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n\n user.passwordHash = await hash(password, 10)\n await em.persist(user).flush()\n\n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\nconst syncRoleAcls: ModuleCli = {\n command: 'sync-role-acls',\n async run(rest) {\n const args: Record<string, string> = {}\n const flags = new Set<string>()\n for (let i = 0; i < rest.length; i++) {\n const token = rest[i]\n if (!token?.startsWith('--')) continue\n const key = token.replace(/^--/, '')\n const next = rest[i + 1]\n if (next && !next.startsWith('--')) {\n args[key] = next\n i++\n } else {\n flags.add(key)\n }\n }\n const tenantArg = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const includeSuperadminRole = !flags.has('no-superadmin')\n\n const modules = getCliModules()\n if (!modules.length) {\n console.error('\u274C No CLI modules registered. Run `yarn generate` first.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n const targetTenantIds: string[] = []\n if (tenantArg) {\n const normalized = normalizeTenantId(tenantArg)\n if (!normalized) {\n console.error(`\u274C Invalid --tenant value: ${tenantArg}`)\n return\n }\n const tenant = await em.findOne(Tenant, { id: normalized })\n if (!tenant) {\n console.error(`\u274C Tenant not found: ${normalized}`)\n return\n }\n targetTenantIds.push(normalized)\n } else {\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to sync.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (id) targetTenantIds.push(id)\n }\n }\n\n for (const tenantId of targetTenantIds) {\n await ensureDefaultRoleAcls(em, tenantId, modules, { includeSuperadminRole })\n await ensureCustomRoleAcls(em, tenantId, modules)\n console.log(`\u2705 Synced role ACLs for tenant ${tenantId}`)\n }\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, syncRoleAcls, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { OM_LOG_LEVEL_ENV, resetLogLevelCache } from '@open-mercato/shared/lib/logger'\nimport { resetServerLoggerCache } from '@open-mercato/shared/lib/logger'\nimport { hash } from 'bcryptjs'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { User, Role, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { Tenant, Organization } from '@open-mercato/core/modules/directory/data/entities'\nimport { rebuildHierarchyForTenant } from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport { ensureRoles, setupInitialTenant, ensureDefaultRoleAcls, ensureCustomRoleAcls, OrgSlugExistsError, DerivedUserPasswordRequiredError } from './lib/setup-app'\nimport { normalizeTenantId } from './lib/tenantAccess'\nimport { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'\nimport { computeEmailHash, emailHashLookupValues } from './lib/emailHash'\nimport { findWithDecryption, findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { createKmsService } from '@open-mercato/shared/lib/encryption/kms'\nimport { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { decryptWithAesGcm } from '@open-mercato/shared/lib/encryption/aes'\nimport { env } from 'process'\nimport type { KmsService, TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport crypto from 'node:crypto'\nimport { formatPasswordRequirements, getPasswordPolicy, validatePassword } from '@open-mercato/shared/lib/auth/passwordPolicy'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { getCliModules } from '@open-mercato/shared/modules/registry'\n\nasync function resolveTenantScopedRole(em: any, name: string, normalizedTenantId: string | null) {\n const existing = await em.findOne(Role, { name, tenantId: normalizedTenantId })\n if (existing) return existing\n const role = em.create(Role, { name, tenantId: normalizedTenantId, createdAt: new Date() })\n await em.persist(role).flush()\n return role\n}\n\nconst addUser: ModuleCli = {\n command: 'add-user',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const email = args.email\n const password = args.password\n const organizationId = String(args.organizationId ?? args.orgId ?? args.org)\n const rolesCsv = (args.roles ?? '').trim()\n if (!email || !password || !organizationId) {\n console.error('Usage: mercato auth add-user --email <email> --password <password> --organizationId <id> [--roles customer,employee]')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const org =\n (await findOneWithDecryption(\n em,\n Organization,\n { id: organizationId },\n { populate: ['tenant'] },\n { tenantId: null, organizationId },\n )) ?? null\n if (!org) throw new Error('Organization not found')\n const orgTenantId = org.tenant?.id ? String(org.tenant.id) : null\n const normalizedTenantId = normalizeTenantId(orgTenantId ?? null) ?? null\n const u = em.create(User, {\n email,\n emailHash: computeEmailHash(email),\n passwordHash: await hash(password, 10),\n isConfirmed: true,\n organizationId: org.id,\n tenantId: org.tenant.id,\n })\n await em.persist(u).flush()\n if (rolesCsv) {\n const names = parseCommaSeparatedList(rolesCsv)\n for (const name of names) {\n const role = await resolveTenantScopedRole(em, name, normalizedTenantId)\n const link = em.create(UserRole, { user: u, role })\n await em.persist(link).flush()\n }\n }\n console.log('User created with id', u.id)\n },\n}\n\nfunction parseArgs(rest: string[]) {\n const args: Record<string, string | boolean> = {}\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]\n if (!a) continue\n if (a.startsWith('--')) {\n const [k, v] = a.replace(/^--/, '').split('=')\n if (v !== undefined) args[k] = v\n else if (rest[i + 1] && !rest[i + 1]!.startsWith('--')) { args[k] = rest[i + 1]!; i++ }\n else args[k] = true\n }\n }\n return args\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\n}\n\nfunction hashSecret(value: string | null | undefined): string | null {\n if (!value) return null\n return crypto.createHash('sha256').update(normalizeKeyInput(value)).digest('hex').slice(0, 12)\n}\n\nfunction ensurePasswordPolicy(password: string): boolean {\n const policy = getPasswordPolicy()\n const result = validatePassword(password, policy)\n if (result.ok) return true\n const requirements = formatPasswordRequirements(policy, (_key, fallback) => fallback)\n const suffix = requirements ? `: ${requirements}` : ''\n console.error(`Password does not meet the requirements${suffix}.`)\n return false\n}\n\nasync function withEncryptionDebugDisabled<T>(fn: () => Promise<T>): Promise<T> {\n const previous = process.env.TENANT_DATA_ENCRYPTION_DEBUG\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = 'no'\n try {\n return await fn()\n } finally {\n if (previous === undefined) {\n delete process.env.TENANT_DATA_ENCRYPTION_DEBUG\n } else {\n process.env.TENANT_DATA_ENCRYPTION_DEBUG = previous\n }\n }\n}\n\nclass DerivedKeyKmsService implements KmsService {\n private root: Buffer\n constructor(secret: string) {\n this.root = crypto.createHash('sha256').update(normalizeKeyInput(secret)).digest()\n }\n\n isHealthy(): boolean {\n return true\n }\n\n private deriveKey(tenantId: string): string {\n const iterations = 310_000\n const keyLength = 32\n const derived = crypto.pbkdf2Sync(this.root, tenantId, iterations, keyLength, 'sha512')\n return derived.toString('base64')\n }\n\n async getTenantDek(tenantId: string): Promise<TenantDek | null> {\n if (!tenantId) return null\n return { tenantId, key: this.deriveKey(tenantId), fetchedAt: Date.now() }\n }\n\n async createTenantDek(tenantId: string): Promise<TenantDek | null> {\n return this.getTenantDek(tenantId)\n }\n}\n\nfunction fingerprintDek(dek: TenantDek | null): string | null {\n if (!dek?.key) return null\n return crypto.createHash('sha256').update(dek.key).digest('hex').slice(0, 12)\n}\n\nfunction decryptWithOldKey(\n payload: string,\n dek: TenantDek | null,\n): string | null {\n if (!dek?.key) return null\n return decryptWithAesGcm(payload, dek.key)\n}\n\nconst seedRoles: ModuleCli = {\n command: 'seed-roles',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n if (!key) continue\n const value = rest[i + 1]\n if (value) args[key] = value\n }\n const tenantId = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n if (tenantId) {\n await ensureRoles(em, { tenantId })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', tenantId)\n return\n }\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to seed.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (!id) continue\n await ensureRoles(em, { tenantId: id })\n console.log('\uD83D\uDEE1\uFE0F Roles ensured for tenant', id)\n }\n },\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenantId as string) ?? (args.tenant as string) ?? (args.tenant_id as string) ?? null\n const organizationId = (args.organizationId as string) ?? (args.orgId as string) ?? (args.org as string) ?? null\n const oldKey = (args['old-key'] as string) ?? (args.oldKey as string) ?? null\n const dryRun = Boolean(args['dry-run'] || args.dry)\n const debug = Boolean(args.debug)\n const rotate = Boolean(oldKey)\n if (rotate && !tenantId) {\n console.warn(\n '\u26A0\uFE0F Rotating with --old-key across all tenants. A single old key should normally target one tenant; consider --tenant.',\n )\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting.')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantId ?? null,\n organizationId: organizationId ?? null,\n })\n console.log('[rotate-encryption-key] key fingerprints', {\n oldKey: hashSecret(oldKey),\n currentKey: hashSecret(process.env.TENANT_DATA_ENCRYPTION_FALLBACK_KEY),\n })\n }\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n const conn: any = (em as any).getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n const meta = (em as any)?.getMetadata?.()?.get?.(User)\n const tableName = meta?.tableName || 'users'\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const isEncryptedPayload = (value: unknown): boolean => {\n if (typeof value !== 'string') return false\n const parts = value.split(':')\n return parts.length === 4 && parts[3] === 'v1'\n }\n const printedDek = new Set<string>()\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (scopeTenantId: string, scopeOrganizationId: string): Promise<number> => {\n if (debug && !printedDek.has(scopeTenantId)) {\n printedDek.add(scopeTenantId)\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(scopeTenantId) ?? Promise.resolve(null),\n encryptionService.getDek(scopeTenantId),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n tenantId: scopeTenantId,\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n }\n const rawRows = await conn.execute(\n `select id, email, email_hash from ${qualifiedTable} where tenant_id = ? and organization_id = ?`,\n [scopeTenantId, scopeOrganizationId],\n )\n const rows = Array.isArray(rawRows) ? rawRows : []\n const pending = rotate\n ? rows\n : rows.filter((row: any) => !isEncryptedPayload(row?.email))\n if (!pending.length) return 0\n console.log(\n `Found ${pending.length} auth user records to process for org=${scopeOrganizationId}${dryRun ? ' (dry-run)' : ''}.`\n )\n if (dryRun) return 0\n const ids = pending.map((row: any) => String(row.id))\n const users = rotate\n ? await em.find(\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n )\n : await findWithDecryption(\n em,\n User,\n { id: { $in: ids }, tenantId: scopeTenantId, organizationId: scopeOrganizationId },\n {},\n { tenantId: scopeTenantId, organizationId: scopeOrganizationId, encryptionService },\n )\n const usersById = new Map(users.map((user) => [String(user.id), user]))\n let updated = 0\n for (const row of pending) {\n const user = usersById.get(String(row.id))\n if (!user) continue\n const rawEmail = typeof row.email === 'string' ? row.email : String(row.email ?? '')\n if (!rawEmail) continue\n if (rotate && (!isEncryptedPayload(rawEmail) || !oldKms)) {\n continue\n }\n let plainEmail = rawEmail\n if (rotate && isEncryptedPayload(rawEmail) && oldKms) {\n if (debug) {\n console.log('[rotate-encryption-key] decrypting', {\n userId: row.id,\n tenantId: scopeTenantId,\n organizationId: scopeOrganizationId,\n })\n }\n let oldDek = oldDekCache.get(scopeTenantId) ?? null\n if (!oldDekCache.has(scopeTenantId)) {\n oldDek = await oldKms.getTenantDek(scopeTenantId)\n oldDekCache.set(scopeTenantId, oldDek)\n }\n const maybeEmail = decryptWithOldKey(rawEmail, oldDek)\n if (typeof maybeEmail !== 'string' || isEncryptedPayload(maybeEmail)) continue\n plainEmail = maybeEmail\n }\n if (!plainEmail) continue\n const encrypted = await encryptionService.encryptEntityPayload(\n 'auth:user',\n { email: plainEmail },\n scopeTenantId,\n scopeOrganizationId,\n )\n const nextEmail = encrypted.email as string | undefined\n if (nextEmail && nextEmail !== user.email) {\n user.email = nextEmail as any\n user.emailHash = (encrypted as any).emailHash ?? computeEmailHash(plainEmail)\n em.persist(user)\n updated += 1\n }\n }\n if (updated > 0) {\n await em.flush()\n }\n return updated\n }\n\n if (tenantId && organizationId) {\n const updated = await processScope(String(tenantId), String(organizationId))\n if (!updated) {\n console.log('All auth user emails already encrypted for the selected scope.')\n } else {\n console.log(`Encrypted ${updated} auth user email(s).`)\n }\n return\n }\n\n const organizations = await em.find(Organization, {})\n if (!organizations.length) {\n console.log('No organizations found; nothing to encrypt.')\n return\n }\n let total = 0\n for (const org of organizations) {\n const scopeTenantId = org.tenant?.id ? String(org.tenant.id) : org.tenant.id ? String(org.tenant.id) : null\n const scopeOrganizationId = org.id ? String(org.id) : null\n if (!scopeTenantId || !scopeOrganizationId) continue\n total += await processScope(scopeTenantId, scopeOrganizationId)\n }\n if (total > 0) {\n console.log(`Encrypted ${total} auth user email(s) across all organizations.`)\n } else {\n console.log('All auth user emails already encrypted across all organizations.')\n }\n },\n}\n\n// will be exported at the bottom with all commands\n\nconst addOrganization: ModuleCli = {\n command: 'add-org',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n const name = args.name || args.orgName\n if (!name) {\n console.error('Usage: mercato auth add-org --name <organization name>')\n return\n }\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n // Create tenant implicitly for simplicity\n const tenant = em.create(Tenant, { name: `${name} Tenant` })\n await em.persist(tenant).flush()\n const org = em.create(Organization, { name, tenant })\n await em.persist(org).flush()\n await rebuildHierarchyForTenant(em, String(tenant.id))\n console.log('Organization created with id', org.id, 'in tenant', tenant.id)\n },\n}\n\nconst SETUP_USAGE = 'Usage: mercato auth setup --orgName <name> --email <email> --password <password> [--orgSlug <slug>] [--roles superadmin,admin,employee] [--skip-password-policy] [--include-demo-users] [--with-examples] [--json]'\n\nconst setupApp: ModuleCli = {\n command: 'setup',\n async run(rest) {\n const args = parseArgs(rest)\n const orgName = typeof args.orgName === 'string'\n ? args.orgName\n : typeof args.name === 'string'\n ? args.name\n : undefined\n const email = typeof args.email === 'string' ? args.email : undefined\n const password = typeof args.password === 'string' ? args.password : undefined\n const orgSlugRaw = typeof args.orgSlug === 'string'\n ? args.orgSlug\n : typeof args.slug === 'string'\n ? args.slug\n : undefined\n const orgSlug = orgSlugRaw && orgSlugRaw.trim().length ? orgSlugRaw.trim() : undefined\n if (orgSlug !== undefined && !ORG_SLUG_PATTERN.test(orgSlug)) {\n process.stderr.write(`Invalid --orgSlug: must match ${ORG_SLUG_PATTERN.source} (lowercase, digits, dashes; 1-63 chars; cannot start or end with a dash)\\n`)\n process.exitCode = 2\n return\n }\n const rolesCsv = typeof args.roles === 'string'\n ? args.roles.trim()\n : 'superadmin,admin,employee'\n const skipPasswordPolicyRaw =\n args['skip-password-policy'] ??\n args.skipPasswordPolicy ??\n args['allow-weak-password'] ??\n args.allowWeakPassword\n const skipPasswordPolicy = typeof skipPasswordPolicyRaw === 'boolean'\n ? skipPasswordPolicyRaw\n : parseBooleanToken(typeof skipPasswordPolicyRaw === 'string' ? skipPasswordPolicyRaw : null) ?? false\n const withExamplesRaw = args['with-examples'] ?? args.withExamples\n const withExamples = typeof withExamplesRaw === 'boolean'\n ? withExamplesRaw\n : parseBooleanToken(typeof withExamplesRaw === 'string' ? withExamplesRaw : null) ?? false\n const includeDemoUsersRaw = args['include-demo-users'] ?? args.includeDemoUsers\n const includeDemoUsers = typeof includeDemoUsersRaw === 'boolean'\n ? includeDemoUsersRaw\n : parseBooleanToken(typeof includeDemoUsersRaw === 'string' ? includeDemoUsersRaw : null) ?? false\n const jsonModeRaw = args.json\n const jsonMode = typeof jsonModeRaw === 'boolean'\n ? jsonModeRaw\n : parseBooleanToken(typeof jsonModeRaw === 'string' ? jsonModeRaw : null) ?? false\n if (!orgName || !email || !password) {\n process.stderr.write(`${SETUP_USAGE}\\n`)\n process.exitCode = 2\n return\n }\n if (!skipPasswordPolicy && !ensurePasswordPolicy(password)) {\n process.exitCode = 2\n return\n }\n if (skipPasswordPolicy && !jsonMode) {\n console.warn('\u26A0\uFE0F Password policy validation skipped for setup.')\n }\n let restoreConsole: (() => void) | null = null\n if (jsonMode) {\n // Force quiet mode so module bootstrap / lifecycle hooks cannot pollute\n // stdout. The JSON contract relies on a single line, so any console.log\n // from setupInitialTenant or seedExamples would otherwise break the\n // consumer's `jq` pipe. We additionally rebind console.log/info to a\n // no-op for the duration of the call because OM_CLI_QUIET is honored\n // unevenly across module setup hooks. Rebind AFTER early-return\n // validation so the missing-args and password-policy paths can't\n // leak the rebinding into a long-lived process (e.g. test runner).\n process.env.OM_CLI_QUIET = process.env.OM_CLI_QUIET ?? '1'\n const originalLog = console.log\n const originalInfo = console.info\n console.log = () => undefined\n console.info = () => undefined\n const originalLogLevel = process.env[OM_LOG_LEVEL_ENV]\n process.env[OM_LOG_LEVEL_ENV] = 'error'\n resetLogLevelCache()\n resetServerLoggerCache()\n restoreConsole = () => {\n console.log = originalLog\n console.info = originalInfo\n if (originalLogLevel === undefined) delete process.env[OM_LOG_LEVEL_ENV]\n else process.env[OM_LOG_LEVEL_ENV] = originalLogLevel\n resetLogLevelCache()\n resetServerLoggerCache()\n }\n }\n const container = await createRequestContainer()\n const em = container.resolve<EntityManager>('em')\n const roleNames = rolesCsv\n ? parseCommaSeparatedList(rolesCsv)\n : undefined\n\n try {\n const modules = getCliModules()\n const result = await setupInitialTenant(em, {\n orgName,\n orgSlug,\n roleNames,\n primaryUser: { email, password, confirm: true },\n // Derived admin/employee demo accounts only land when the operator\n // explicitly opts in via --include-demo-users. Default-deny prevents\n // production deployments from silently seeding well-known emails.\n includeDerivedUsers: includeDemoUsers,\n // When demo users are explicitly requested, the operator has consented\n // to having admin@/employee@ accounts created with autogenerated\n // passwords (surfaced in stdout) when no env override is provided.\n allowDemoDerivedPasswords: includeDemoUsers,\n // When the caller passes an explicit slug, treat it as a \"fresh tenant\"\n // signal \u2014 silent reuse of an existing user's tenant defeats the point\n // of slugging the new tenant for downstream tooling.\n failIfUserExists: orgSlug !== undefined,\n modules,\n })\n\n if (withExamples) {\n const seedCtx = {\n em,\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n container,\n }\n for (const mod of modules) {\n if (mod.setup?.seedExamples) {\n await mod.setup.seedExamples(seedCtx as any)\n }\n }\n }\n\n if (jsonMode) {\n const adminSnapshot = pickAdminSnapshot(result.users, email)\n const adminUserId = adminSnapshot && adminSnapshot.user.id != null\n ? String(adminSnapshot.user.id)\n : null\n const payload = {\n tenantId: result.tenantId,\n organizationId: result.organizationId,\n adminUserId,\n adminEmail: email,\n reusedExistingUser: result.reusedExistingUser,\n }\n if (restoreConsole) restoreConsole()\n process.stdout.write(`${JSON.stringify(payload)}\\n`)\n return\n }\n\n if (result.reusedExistingUser) {\n console.log('\u26A0\uFE0F Existing initial user detected during setup.')\n console.log(`\u26A0\uFE0F Email: ${email}`)\n console.log('\u26A0\uFE0F Updated roles if missing and reused tenant/organization.')\n }\n\n if (env.NODE_ENV !== 'test') {\n for (const snapshot of result.users) {\n if (snapshot.created) {\n if (snapshot.generatedPassword) {\n console.log('\u26A0\uFE0F GENERATED password \u2014 copy now; it is not stored in plain text')\n console.log('\uD83C\uDF89 Created user', snapshot.user.email, 'password:', snapshot.generatedPassword)\n } else {\n console.log('\uD83C\uDF89 Created user', snapshot.user.email)\n }\n } else {\n console.log(`Updated user ${snapshot.user.email}`)\n }\n }\n }\n\n if (env.NODE_ENV !== 'test') console.log('\u2705 Setup complete:', { tenantId: result.tenantId, organizationId: result.organizationId })\n } catch (err) {\n if (restoreConsole) restoreConsole()\n if (err instanceof OrgSlugExistsError) {\n process.stderr.write(`${err.message}\\n`)\n process.exitCode = 1\n return\n }\n if (err instanceof DerivedUserPasswordRequiredError) {\n process.stderr.write(\n `Setup aborted: ${err.message}. In production, set OM_INIT_ADMIN_PASSWORD and OM_INIT_EMPLOYEE_PASSWORD before passing --include-demo-users, or omit --include-demo-users to skip derived demo accounts entirely.\\n`,\n )\n process.exitCode = 2\n return\n }\n if (err instanceof Error && err.message === 'USER_EXISTS') {\n process.stderr.write('Setup aborted: user already exists with the provided email.\\n')\n process.exitCode = 1\n return\n }\n throw err\n } finally {\n if (restoreConsole) restoreConsole()\n }\n },\n}\n\nconst ORG_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/\n\nfunction pickAdminSnapshot(\n users: Array<{ user: { id: string; email: string }; roles: string[]; created: boolean }>,\n adminEmail: string,\n): { user: { id: string; email: string } } | undefined {\n const normalizedEmail = adminEmail.toLowerCase()\n const exact = users.find(\n (snapshot) =>\n snapshot.created &&\n snapshot.roles.includes('superadmin') &&\n typeof snapshot.user.email === 'string' &&\n snapshot.user.email.toLowerCase() === normalizedEmail,\n )\n if (exact) return exact\n return users.find((snapshot) => snapshot.roles.includes('superadmin'))\n}\n\nconst listOrganizations: ModuleCli = {\n command: 'list-orgs',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const orgs = await findWithDecryption(\n em,\n Organization,\n {},\n { populate: ['tenant'] },\n { tenantId: null, organizationId: null },\n )\n\n if (orgs.length === 0) {\n console.log('No organizations found')\n return\n }\n\n console.log(`Found ${orgs.length} organization(s):`)\n console.log('')\n console.log('ID | Name | Tenant ID | Created')\n console.log('-------------------------------------|-------------------------|-------------------------------------|-------------------')\n\n for (const org of orgs) {\n const created = org.createdAt ? new Date(org.createdAt).toLocaleDateString() : 'N/A'\n const id = org.id || 'N/A'\n const tenantId = org.tenant?.id || 'N/A'\n const name = (org.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${tenantId.padEnd(35)} | ${created}`)\n }\n },\n}\n\nconst listTenants: ModuleCli = {\n command: 'list-tenants',\n async run() {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const tenants = await em.find(Tenant, {})\n\n if (tenants.length === 0) {\n console.log('No tenants found')\n return\n }\n\n console.log(`Found ${tenants.length} tenant(s):`)\n console.log('')\n console.log('ID | Name | Created')\n console.log('-------------------------------------|-------------------------|-------------------')\n\n for (const tenant of tenants) {\n const created = tenant.createdAt ? new Date(tenant.createdAt).toLocaleDateString() : 'N/A'\n const id = tenant.id || 'N/A'\n const name = (tenant.name || 'Unnamed').padEnd(23)\n console.log(`${id.padEnd(35)} | ${name} | ${created}`)\n }\n },\n}\n\nconst listUsers: ModuleCli = {\n command: 'list-users',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n // Build query with optional filters\n const where: any = {}\n if (args.organizationId || args.orgId || args.org) {\n where.organizationId = args.organizationId || args.orgId || args.org\n }\n if (args.tenantId || args.tenant) {\n where.tenantId = args.tenantId || args.tenant\n }\n\n const users = await em.find(User, where)\n\n if (users.length === 0) {\n console.log('No users found')\n return\n }\n\n console.log(`Found ${users.length} user(s):`)\n console.log('')\n console.log('ID | Email | Name | Organization ID | Tenant ID | Roles')\n console.log('-------------------------------------|-------------------------|-------------------------|---------------------|---------------------|-------------------')\n\n for (const user of users) {\n // Get user roles separately\n const userRoles = await findWithDecryption(\n em,\n UserRole,\n { user: user.id },\n { populate: ['role'] },\n { tenantId: user.tenantId ?? null, organizationId: user.organizationId ?? null },\n )\n const roles = userRoles.map((ur: any) => ur.role?.name).filter(Boolean).join(', ') || 'None'\n\n // Get organization and tenant names if IDs exist\n let orgName = 'N/A'\n let tenantName = 'N/A'\n\n if (user.organizationId) {\n const org = await em.findOne(Organization, { id: user.organizationId })\n orgName = org?.name ? `${org.name.substring(0, 19)}...` : `${user.organizationId.substring(0, 8)}...`\n }\n\n if (user.tenantId) {\n const tenant = await em.findOne(Tenant, { id: user.tenantId })\n tenantName = tenant?.name ? `${tenant.name.substring(0, 19)}...` : `${user.tenantId.substring(0, 8)}...`\n }\n\n const id = user.id || 'N/A'\n const email = (user.email || 'N/A').padEnd(23)\n const name = (user.name || 'Unnamed').padEnd(23)\n\n console.log(`${id.padEnd(35)} | ${email} | ${name} | ${orgName.padEnd(19)} | ${tenantName.padEnd(19)} | ${roles}`)\n }\n },\n}\n\nconst setPassword: ModuleCli = {\n command: 'set-password',\n async run(rest) {\n const args: Record<string, string> = {}\n for (let i = 0; i < rest.length; i += 2) {\n const k = rest[i]?.replace(/^--/, '')\n const v = rest[i + 1]\n if (k) args[k] = v\n }\n\n const email = args.email\n const password = args.password\n\n if (!email || !password) {\n console.error('Usage: mercato auth set-password --email <email> --password <newPassword>')\n return\n }\n if (!ensurePasswordPolicy(password)) return\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const user = await findOneWithDecryption(\n em,\n User,\n { $or: [{ email }, { emailHash: { $in: emailHashLookupValues(email) } }] } as any,\n undefined,\n { tenantId: null, organizationId: null },\n )\n\n if (!user) {\n console.error(`User with email \"${email}\" not found`)\n return\n }\n\n user.passwordHash = await hash(password, 10)\n await em.persist(user).flush()\n\n console.log(`\u2705 Password updated successfully for user: ${email}`)\n },\n}\n\nconst syncRoleAcls: ModuleCli = {\n command: 'sync-role-acls',\n async run(rest) {\n const args: Record<string, string> = {}\n const flags = new Set<string>()\n for (let i = 0; i < rest.length; i++) {\n const token = rest[i]\n if (!token?.startsWith('--')) continue\n const key = token.replace(/^--/, '')\n const next = rest[i + 1]\n if (next && !next.startsWith('--')) {\n args[key] = next\n i++\n } else {\n flags.add(key)\n }\n }\n const tenantArg = args.tenantId ?? args.tenant ?? args.tenant_id ?? null\n const includeSuperadminRole = !flags.has('no-superadmin')\n\n const modules = getCliModules()\n if (!modules.length) {\n console.error('\u274C No CLI modules registered. Run `yarn generate` first.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve<EntityManager>('em')\n\n const targetTenantIds: string[] = []\n if (tenantArg) {\n const normalized = normalizeTenantId(tenantArg)\n if (!normalized) {\n console.error(`\u274C Invalid --tenant value: ${tenantArg}`)\n return\n }\n const tenant = await em.findOne(Tenant, { id: normalized })\n if (!tenant) {\n console.error(`\u274C Tenant not found: ${normalized}`)\n return\n }\n targetTenantIds.push(normalized)\n } else {\n const tenants = await em.find(Tenant, {})\n if (!tenants.length) {\n console.log('No tenants found; nothing to sync.')\n return\n }\n for (const tenant of tenants) {\n const id = tenant.id ? String(tenant.id) : null\n if (id) targetTenantIds.push(id)\n }\n }\n\n for (const tenantId of targetTenantIds) {\n await ensureDefaultRoleAcls(em, tenantId, modules, { includeSuperadminRole })\n await ensureCustomRoleAcls(em, tenantId, modules)\n console.log(`\u2705 Synced role ACLs for tenant ${tenantId}`)\n }\n },\n}\n\n// Export the full CLI list\nexport default [addUser, seedRoles, syncRoleAcls, rotateEncryptionKey, addOrganization, setupApp, listOrganizations, listTenants, listUsers, setPassword]\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,7 @@
1
1
  import * as semver from "semver";
2
+ import { createLogger } from "@open-mercato/shared/lib/logger";
3
+ import { reconcileAttachmentOrganizations } from "@open-mercato/core/modules/attachments/lib/reconcileOrganization";
4
+ const logger = createLogger("configs").child({ component: "upgrade-actions" });
2
5
  function compareVersions(a, b) {
3
6
  const cleanA = semver.valid(semver.coerce(a));
4
7
  const cleanB = semver.valid(semver.coerce(b));
@@ -11,6 +14,25 @@ function compareVersions(a, b) {
11
14
  return semver.compare(cleanA, cleanB);
12
15
  }
13
16
  const upgradeActions = [
17
+ {
18
+ id: "attachments.reconcile-organization",
19
+ version: "0.6.6",
20
+ messageKey: "configs.upgrades.attachmentsOrgReconcile.message",
21
+ ctaKey: "configs.upgrades.attachmentsOrgReconcile.cta",
22
+ successKey: "configs.upgrades.attachmentsOrgReconcile.success",
23
+ loadingKey: "configs.upgrades.attachmentsOrgReconcile.loading",
24
+ async run({ container, em, tenantId }) {
25
+ const queryEngine = container.resolve("queryEngine");
26
+ const report = await reconcileAttachmentOrganizations({ em, queryEngine, tenantId });
27
+ logger.info("attachments organization reconcile completed", {
28
+ tenantId,
29
+ scanned: report.scanned,
30
+ updated: report.updated,
31
+ unresolved: report.unresolved,
32
+ skippedVirtual: report.skippedVirtual
33
+ });
34
+ }
35
+ },
14
36
  {
15
37
  id: "customers.seed-interaction-statuses",
16
38
  version: "0.6.5",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/configs/lib/upgrade-actions.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport * as semver from 'semver'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\n\nexport type UpgradeActionContext = {\n tenantId: string\n organizationId: string\n container: AppContainer\n em: EntityManager\n}\n\nexport type UpgradeActionDefinition = {\n id: string\n version: string\n messageKey: string\n ctaKey: string\n successKey: string\n loadingKey?: string\n run: (ctx: UpgradeActionContext) => Promise<void>\n}\n\n/**\n * Compare two semantic version strings.\n * Uses the semver library for robust version comparison.\n * Returns negative if a < b, positive if a > b, 0 if equal.\n * Throws an error if either version string is invalid.\n */\nexport function compareVersions(a: string, b: string): number {\n const cleanA = semver.valid(semver.coerce(a))\n const cleanB = semver.valid(semver.coerce(b))\n if (!cleanA) {\n throw new Error(`Invalid version string: \"${a}\". Expected a valid semver format (e.g., \"1.2.3\").`)\n }\n if (!cleanB) {\n throw new Error(`Invalid version string: \"${b}\". Expected a valid semver format (e.g., \"1.2.3\").`)\n }\n return semver.compare(cleanA, cleanB)\n}\n\nexport const upgradeActions: UpgradeActionDefinition[] = [\n {\n id: 'customers.seed-interaction-statuses',\n version: '0.6.5',\n messageKey: 'customers.config.upgradeActions.interactionStatuses.message',\n ctaKey: 'customers.config.upgradeActions.interactionStatuses.cta',\n successKey: 'customers.config.upgradeActions.interactionStatuses.success',\n loadingKey: 'customers.config.upgradeActions.interactionStatuses.loading',\n // Existing tenants predate the `interaction_status` dictionary, so their status\n // dropdown is empty until seeded. New tenants get it via customers `seedDefaults`.\n // Lazy-imported so configs stays decoupled from customers (the catalog of actions\n // lives here, but customers code only loads when the action actually runs).\n run: async ({ em, tenantId, organizationId }) => {\n const [{ INTERACTION_STATUS_DEFAULTS }, { ensureDictionaryEntry }] = await Promise.all([\n import('@open-mercato/core/modules/customers/cli'),\n import('@open-mercato/core/modules/customers/commands/shared'),\n ])\n for (const entry of INTERACTION_STATUS_DEFAULTS) {\n await ensureDictionaryEntry(em, {\n tenantId,\n organizationId,\n kind: 'interaction_status',\n value: entry.value,\n label: entry.label,\n color: entry.color,\n icon: entry.icon,\n })\n }\n },\n },\n]\n\nexport function actionsUpToVersion(version: string): UpgradeActionDefinition[] {\n return upgradeActions // NOSONAR \u2014 upgradeActions is populated at boot time by modules\n .filter((action) => compareVersions(action.version, version) <= 0)\n .sort((a, b) => compareVersions(a.version, b.version) || a.id.localeCompare(b.id))\n}\n\nexport function findUpgradeAction(actionId: string, maxVersion: string): UpgradeActionDefinition | undefined {\n const matches = actionsUpToVersion(maxVersion).filter((action) => action.id === actionId)\n if (!matches.length) return undefined\n return matches[matches.length - 1]\n}\n"],
5
- "mappings": "AACA,YAAY,YAAY;AA0BjB,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC,CAAC;AAC5C,QAAM,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC,CAAC;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,4BAA4B,CAAC,oDAAoD;AAAA,EACnG;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,4BAA4B,CAAC,oDAAoD;AAAA,EACnG;AACA,SAAO,OAAO,QAAQ,QAAQ,MAAM;AACtC;AAEO,MAAM,iBAA4C;AAAA,EACvD;AAAA,IACE,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,IAKZ,KAAK,OAAO,EAAE,IAAI,UAAU,eAAe,MAAM;AAC/C,YAAM,CAAC,EAAE,4BAA4B,GAAG,EAAE,sBAAsB,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,QACrF,OAAO,0CAA0C;AAAA,QACjD,OAAO,sDAAsD;AAAA,MAC/D,CAAC;AACD,iBAAW,SAAS,6BAA6B;AAC/C,cAAM,sBAAsB,IAAI;AAAA,UAC9B;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,SAA4C;AAC7E,SAAO,eACJ,OAAO,CAAC,WAAW,gBAAgB,OAAO,SAAS,OAAO,KAAK,CAAC,EAChE,KAAK,CAAC,GAAG,MAAM,gBAAgB,EAAE,SAAS,EAAE,OAAO,KAAK,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AACrF;AAEO,SAAS,kBAAkB,UAAkB,YAAyD;AAC3G,QAAM,UAAU,mBAAmB,UAAU,EAAE,OAAO,CAAC,WAAW,OAAO,OAAO,QAAQ;AACxF,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SAAO,QAAQ,QAAQ,SAAS,CAAC;AACnC;",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport * as semver from 'semver'\nimport type { AppContainer } from '@open-mercato/shared/lib/di/container'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { QueryEngine } from '@open-mercato/shared/lib/query/types'\nimport { reconcileAttachmentOrganizations } from '@open-mercato/core/modules/attachments/lib/reconcileOrganization'\n\nconst logger = createLogger('configs').child({ component: 'upgrade-actions' })\n\nexport type UpgradeActionContext = {\n tenantId: string\n organizationId: string\n container: AppContainer\n em: EntityManager\n}\n\nexport type UpgradeActionDefinition = {\n id: string\n version: string\n messageKey: string\n ctaKey: string\n successKey: string\n loadingKey?: string\n run: (ctx: UpgradeActionContext) => Promise<void>\n}\n\n/**\n * Compare two semantic version strings.\n * Uses the semver library for robust version comparison.\n * Returns negative if a < b, positive if a > b, 0 if equal.\n * Throws an error if either version string is invalid.\n */\nexport function compareVersions(a: string, b: string): number {\n const cleanA = semver.valid(semver.coerce(a))\n const cleanB = semver.valid(semver.coerce(b))\n if (!cleanA) {\n throw new Error(`Invalid version string: \"${a}\". Expected a valid semver format (e.g., \"1.2.3\").`)\n }\n if (!cleanB) {\n throw new Error(`Invalid version string: \"${b}\". Expected a valid semver format (e.g., \"1.2.3\").`)\n }\n return semver.compare(cleanA, cleanB)\n}\n\nexport const upgradeActions: UpgradeActionDefinition[] = [\n {\n id: 'attachments.reconcile-organization',\n version: '0.6.6',\n messageKey: 'configs.upgrades.attachmentsOrgReconcile.message',\n ctaKey: 'configs.upgrades.attachmentsOrgReconcile.cta',\n successKey: 'configs.upgrades.attachmentsOrgReconcile.success',\n loadingKey: 'configs.upgrades.attachmentsOrgReconcile.loading',\n async run({ container, em, tenantId }) {\n const queryEngine = container.resolve('queryEngine') as QueryEngine\n const report = await reconcileAttachmentOrganizations({ em, queryEngine, tenantId })\n logger.info('attachments organization reconcile completed', {\n tenantId,\n scanned: report.scanned,\n updated: report.updated,\n unresolved: report.unresolved,\n skippedVirtual: report.skippedVirtual,\n })\n },\n },\n {\n id: 'customers.seed-interaction-statuses',\n version: '0.6.5',\n messageKey: 'customers.config.upgradeActions.interactionStatuses.message',\n ctaKey: 'customers.config.upgradeActions.interactionStatuses.cta',\n successKey: 'customers.config.upgradeActions.interactionStatuses.success',\n loadingKey: 'customers.config.upgradeActions.interactionStatuses.loading',\n // Existing tenants predate the `interaction_status` dictionary, so their status\n // dropdown is empty until seeded. New tenants get it via customers `seedDefaults`.\n // Lazy-imported so configs stays decoupled from customers (the catalog of actions\n // lives here, but customers code only loads when the action actually runs).\n run: async ({ em, tenantId, organizationId }) => {\n const [{ INTERACTION_STATUS_DEFAULTS }, { ensureDictionaryEntry }] = await Promise.all([\n import('@open-mercato/core/modules/customers/cli'),\n import('@open-mercato/core/modules/customers/commands/shared'),\n ])\n for (const entry of INTERACTION_STATUS_DEFAULTS) {\n await ensureDictionaryEntry(em, {\n tenantId,\n organizationId,\n kind: 'interaction_status',\n value: entry.value,\n label: entry.label,\n color: entry.color,\n icon: entry.icon,\n })\n }\n },\n },\n]\n\nexport function actionsUpToVersion(version: string): UpgradeActionDefinition[] {\n return upgradeActions // NOSONAR \u2014 upgradeActions is populated at boot time by modules\n .filter((action) => compareVersions(action.version, version) <= 0)\n .sort((a, b) => compareVersions(a.version, b.version) || a.id.localeCompare(b.id))\n}\n\nexport function findUpgradeAction(actionId: string, maxVersion: string): UpgradeActionDefinition | undefined {\n const matches = actionsUpToVersion(maxVersion).filter((action) => action.id === actionId)\n if (!matches.length) return undefined\n return matches[matches.length - 1]\n}\n"],
5
+ "mappings": "AACA,YAAY,YAAY;AAExB,SAAS,oBAAoB;AAE7B,SAAS,wCAAwC;AAEjD,MAAM,SAAS,aAAa,SAAS,EAAE,MAAM,EAAE,WAAW,kBAAkB,CAAC;AAyBtE,SAAS,gBAAgB,GAAW,GAAmB;AAC5D,QAAM,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC,CAAC;AAC5C,QAAM,SAAS,OAAO,MAAM,OAAO,OAAO,CAAC,CAAC;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,4BAA4B,CAAC,oDAAoD;AAAA,EACnG;AACA,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,4BAA4B,CAAC,oDAAoD;AAAA,EACnG;AACA,SAAO,OAAO,QAAQ,QAAQ,MAAM;AACtC;AAEO,MAAM,iBAA4C;AAAA,EACvD;AAAA,IACE,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,MAAM,IAAI,EAAE,WAAW,IAAI,SAAS,GAAG;AACrC,YAAM,cAAc,UAAU,QAAQ,aAAa;AACnD,YAAM,SAAS,MAAM,iCAAiC,EAAE,IAAI,aAAa,SAAS,CAAC;AACnF,aAAO,KAAK,gDAAgD;AAAA,QAC1D;AAAA,QACA,SAAS,OAAO;AAAA,QAChB,SAAS,OAAO;AAAA,QAChB,YAAY,OAAO;AAAA,QACnB,gBAAgB,OAAO;AAAA,MACzB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA,IAKZ,KAAK,OAAO,EAAE,IAAI,UAAU,eAAe,MAAM;AAC/C,YAAM,CAAC,EAAE,4BAA4B,GAAG,EAAE,sBAAsB,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,QACrF,OAAO,0CAA0C;AAAA,QACjD,OAAO,sDAAsD;AAAA,MAC/D,CAAC;AACD,iBAAW,SAAS,6BAA6B;AAC/C,cAAM,sBAAsB,IAAI;AAAA,UAC9B;AAAA,UACA;AAAA,UACA,MAAM;AAAA,UACN,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,OAAO,MAAM;AAAA,UACb,MAAM,MAAM;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,SAA4C;AAC7E,SAAO,eACJ,OAAO,CAAC,WAAW,gBAAgB,OAAO,SAAS,OAAO,KAAK,CAAC,EAChE,KAAK,CAAC,GAAG,MAAM,gBAAgB,EAAE,SAAS,EAAE,OAAO,KAAK,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AACrF;AAEO,SAAS,kBAAkB,UAAkB,YAAyD;AAC3G,QAAM,UAAU,mBAAmB,UAAU,EAAE,OAAO,CAAC,WAAW,OAAO,OAAO,QAAQ;AACxF,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,SAAO,QAAQ,QAAQ,SAAS,CAAC;AACnC;",
6
6
  "names": []
7
7
  }
@@ -4,6 +4,7 @@ import { Role } from "@open-mercato/core/modules/auth/data/entities";
4
4
  import { loadAllWidgets } from "@open-mercato/core/modules/dashboards/lib/widgets";
5
5
  import { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from "@open-mercato/core/modules/dashboards/lib/role-widgets";
6
6
  import { seedAnalyticsData } from "./seed/analytics.js";
7
+ import { parseCommaSeparatedList } from "@open-mercato/shared/lib/string";
7
8
  function parseArgs(rest) {
8
9
  const args = {};
9
10
  for (let i = 0; i < rest.length; i += 2) {
@@ -84,7 +85,7 @@ const seedDefaults = {
84
85
  console.error("Usage: mercato dashboards seed-defaults --tenant <tenantId> [--roles superadmin,admin,employee] [--widgets id1,id2]");
85
86
  return;
86
87
  }
87
- const roleNames = roleCsv.split(",").map((name) => name.trim()).filter(Boolean);
88
+ const roleNames = parseCommaSeparatedList(roleCsv);
88
89
  if (!roleNames.length) {
89
90
  console.log("No roles provided, nothing to seed.");
90
91
  return;
@@ -95,7 +96,7 @@ const seedDefaults = {
95
96
  tenantId,
96
97
  organizationId,
97
98
  roleNames,
98
- widgetIds: widgetCsv ? widgetCsv.split(",").map((id) => id.trim()).filter(Boolean) : void 0,
99
+ widgetIds: widgetCsv ? parseCommaSeparatedList(widgetCsv) : void 0,
99
100
  logger: (message) => console.log(message)
100
101
  });
101
102
  }
@@ -111,7 +112,7 @@ const enableAnalyticsWidgets = {
111
112
  console.error("Usage: mercato dashboards enable-analytics-widgets --tenant <tenantId> [--org <orgId>] [--roles admin,employee]");
112
113
  return;
113
114
  }
114
- const roleNames = roleCsv.split(",").map((name) => name.trim()).filter(Boolean);
115
+ const roleNames = parseCommaSeparatedList(roleCsv);
115
116
  if (!roleNames.length) {
116
117
  console.log("No roles provided, nothing to update.");
117
118
  return;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/modules/dashboards/cli.ts"],
4
- "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { DashboardRoleWidgets } from '@open-mercato/core/modules/dashboards/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'\nimport { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'\nimport { seedAnalyticsData } from './seed/analytics'\n\ntype Args = Record<string, string>\n\nfunction parseArgs(rest: string[]): Args {\n const args: Args = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n const value = rest[i + 1]\n if (key) args[key] = value ?? ''\n }\n return args\n}\n\nexport async function seedDashboardDefaultsForTenant(\n em: EntityManager,\n {\n tenantId,\n organizationId = null,\n roleNames = ['superadmin', 'admin', 'employee'],\n widgetIds,\n logger,\n }: {\n tenantId: string\n organizationId?: string | null\n roleNames?: string[]\n widgetIds?: string[]\n logger?: (message: string) => void\n },\n): Promise<boolean> {\n if (!tenantId) throw new Error('tenantId is required')\n const log = logger ?? (() => {})\n\n const widgets = await loadAllWidgets()\n const widgetMap = new Map(widgets.map((widget) => [widget.metadata.id, widget]))\n const resolvedWidgetIds = widgetIds && widgetIds.length\n ? widgetIds.filter((id) => widgetMap.has(id))\n : null\n const defaultWidgetIds = widgets\n .filter((widget) => widget.metadata.defaultEnabled)\n .map((widget) => widget.metadata.id)\n const allWidgetIds = widgets.map((widget) => widget.metadata.id)\n\n if (resolvedWidgetIds && resolvedWidgetIds.length === 0) {\n log('No widgets resolved for dashboard seeding.')\n return false\n }\n\n await em.transactional(async (tem) => {\n for (const roleName of roleNames) {\n const isAdminRole = roleName === 'admin' || roleName === 'superadmin'\n const roleWidgetIds = resolvedWidgetIds ?? (isAdminRole ? allWidgetIds : defaultWidgetIds)\n if (!roleWidgetIds.length) {\n log(`No widgets resolved for role \"${roleName}\".`)\n continue\n }\n const role = await tem.findOne(Role, { name: roleName })\n if (!role) {\n log(`Skipping role \"${roleName}\" (not found)`)\n continue\n }\n const existing = await tem.findOne(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.widgetIdsJson = roleWidgetIds\n tem.persist(existing)\n log(`Updated dashboard widgets for role \"${roleName}\"`)\n } else {\n const record = tem.create(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId,\n organizationId,\n widgetIdsJson: roleWidgetIds,\n createdAt: new Date(),\n updatedAt: null,\n deletedAt: null,\n })\n tem.persist(record)\n log(`Created dashboard widgets for role \"${roleName}\"`)\n }\n }\n })\n\n return true\n}\n\nconst seedDefaults: ModuleCli = {\n command: 'seed-defaults',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || null\n const roleCsv = args.roles || 'superadmin,admin,employee'\n const widgetCsv = args.widgets || ''\n if (!tenantId) {\n console.error('Usage: mercato dashboards seed-defaults --tenant <tenantId> [--roles superadmin,admin,employee] [--widgets id1,id2]')\n return\n }\n\n const roleNames = roleCsv\n .split(',')\n .map((name) => name.trim())\n .filter(Boolean)\n\n if (!roleNames.length) {\n console.log('No roles provided, nothing to seed.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n await seedDashboardDefaultsForTenant(em as EntityManager, {\n tenantId,\n organizationId,\n roleNames,\n widgetIds: widgetCsv ? widgetCsv.split(',').map((id) => id.trim()).filter(Boolean) : undefined,\n logger: (message) => console.log(message),\n })\n },\n}\n\nconst enableAnalyticsWidgets: ModuleCli = {\n command: 'enable-analytics-widgets',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n const roleCsv = args.roles || 'admin,employee'\n if (!tenantId) {\n console.error('Usage: mercato dashboards enable-analytics-widgets --tenant <tenantId> [--org <orgId>] [--roles admin,employee]')\n return\n }\n\n const roleNames = roleCsv\n .split(',')\n .map((name) => name.trim())\n .filter(Boolean)\n\n if (!roleNames.length) {\n console.log('No roles provided, nothing to update.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const widgetIds = await resolveAnalyticsWidgetIds()\n\n const updated = await appendWidgetsToRoles(em, {\n tenantId,\n organizationId,\n roleNames,\n widgetIds,\n })\n\n if (!updated) {\n console.log('No dashboard role widgets updated.')\n }\n },\n}\n\nconst seedAnalytics: ModuleCli = {\n command: 'seed-analytics',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n const months = args.months ? parseInt(args.months, 10) : 6\n const ordersPerMonth = args.ordersPerMonth ? parseInt(args.ordersPerMonth, 10) : 50\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato dashboards seed-analytics --tenant <tenantId> --organization <organizationId> [--months 6] [--ordersPerMonth 50]')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n\n console.log(`Seeding analytics data for ${months} months with ~${ordersPerMonth} orders/month...`)\n\n try {\n const result = await em.transactional(async (tem) =>\n seedAnalyticsData(tem, { tenantId, organizationId }, { months, ordersPerMonth })\n )\n\n if (result.orders === 0) {\n console.log('Analytics data already exists. Skipping seed.')\n } else {\n console.log(`Seeded analytics data:`)\n console.log(` - Orders: ${result.orders}`)\n console.log(` - Customers: ${result.customers}`)\n console.log(` - Products: ${result.products}`)\n console.log(` - Deals: ${result.deals}`)\n }\n } catch (error) {\n console.error('Failed to seed analytics data:', error)\n }\n },\n}\n\nconst debugAnalytics: ModuleCli = {\n command: 'debug-analytics',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n\n if (!tenantId) {\n console.error('Usage: mercato dashboards debug-analytics --tenant <tenantId> [--organization <organizationId>]')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const conn = em.getConnection()\n\n console.log('Checking analytics data...\\n')\n\n const ordersResult = await conn.execute(\n `SELECT COUNT(*) as total, MIN(placed_at) as earliest, MAX(placed_at) as latest\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'`,\n [tenantId]\n )\n console.log('Orders summary:', ordersResult[0])\n\n const recentOrders = await conn.execute(\n `SELECT order_number, placed_at, status, grand_total_gross_amount::numeric as total\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'\n ORDER BY placed_at DESC LIMIT 5`,\n [tenantId]\n )\n console.log('\\nRecent orders:', recentOrders)\n\n const januaryOrders = await conn.execute(\n `SELECT COUNT(*) as count, SUM(grand_total_gross_amount::numeric) as total\n FROM sales_orders\n WHERE tenant_id = ?\n AND order_number LIKE 'SO-ANALYTICS-%'\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId]\n )\n console.log('\\nJanuary 2026 orders:', januaryOrders[0])\n\n const allOrders = await conn.execute(\n `SELECT COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ?`,\n [tenantId]\n )\n console.log('\\nTotal orders in tenant:', allOrders[0])\n\n const orgCheck = await conn.execute(\n `SELECT organization_id, COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'\n GROUP BY organization_id`,\n [tenantId]\n )\n console.log('\\nOrders by organization:', orgCheck)\n\n if (organizationId) {\n const orgOrders = await conn.execute(\n `SELECT COUNT(*) as count, SUM(grand_total_gross_amount::numeric) as total\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ?\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, organizationId]\n )\n console.log(`\\nJanuary orders for org ${organizationId}:`, orgOrders[0])\n }\n\n // Check for NULL placed_at\n const nullPlacedAt = await conn.execute(\n `SELECT COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ? AND placed_at IS NULL`,\n [tenantId]\n )\n console.log('\\nOrders with NULL placed_at:', nullPlacedAt[0])\n\n // Check non-analytics orders\n const nonAnalytics = await conn.execute(\n `SELECT order_number, placed_at, status, organization_id, grand_total_gross_amount::numeric as total\n FROM sales_orders\n WHERE tenant_id = ? AND order_number NOT LIKE 'SO-ANALYTICS-%'\n ORDER BY placed_at DESC NULLS LAST LIMIT 10`,\n [tenantId]\n )\n console.log('\\nNon-analytics orders:', nonAnalytics)\n\n // Simulate widget query\n console.log('\\n--- Simulating widget query for this_month ---')\n const widgetQuery = await conn.execute(\n `SELECT COALESCE(SUM(grand_total_gross_amount::numeric), 0) AS value\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ANY(?::uuid[])\n AND deleted_at IS NULL\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, `{${organizationId}}`]\n )\n console.log('Widget query result (revenue sum):', widgetQuery[0])\n\n const widgetCountQuery = await conn.execute(\n `SELECT COUNT(*) AS value\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ANY(?::uuid[])\n AND deleted_at IS NULL\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, `{${organizationId}}`]\n )\n console.log('Widget query result (order count):', widgetCountQuery[0])\n },\n}\n\nexport default [seedDefaults, enableAnalyticsWidgets, seedAnalytics, debugAnalytics]\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { DashboardRoleWidgets } from '@open-mercato/core/modules/dashboards/data/entities'\nimport { Role } from '@open-mercato/core/modules/auth/data/entities'\nimport { loadAllWidgets } from '@open-mercato/core/modules/dashboards/lib/widgets'\nimport { appendWidgetsToRoles, resolveAnalyticsWidgetIds } from '@open-mercato/core/modules/dashboards/lib/role-widgets'\nimport { seedAnalyticsData } from './seed/analytics'\nimport { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'\n\ntype Args = Record<string, string>\n\nfunction parseArgs(rest: string[]): Args {\n const args: Args = {}\n for (let i = 0; i < rest.length; i += 2) {\n const key = rest[i]?.replace(/^--/, '')\n const value = rest[i + 1]\n if (key) args[key] = value ?? ''\n }\n return args\n}\n\nexport async function seedDashboardDefaultsForTenant(\n em: EntityManager,\n {\n tenantId,\n organizationId = null,\n roleNames = ['superadmin', 'admin', 'employee'],\n widgetIds,\n logger,\n }: {\n tenantId: string\n organizationId?: string | null\n roleNames?: string[]\n widgetIds?: string[]\n logger?: (message: string) => void\n },\n): Promise<boolean> {\n if (!tenantId) throw new Error('tenantId is required')\n const log = logger ?? (() => {})\n\n const widgets = await loadAllWidgets()\n const widgetMap = new Map(widgets.map((widget) => [widget.metadata.id, widget]))\n const resolvedWidgetIds = widgetIds && widgetIds.length\n ? widgetIds.filter((id) => widgetMap.has(id))\n : null\n const defaultWidgetIds = widgets\n .filter((widget) => widget.metadata.defaultEnabled)\n .map((widget) => widget.metadata.id)\n const allWidgetIds = widgets.map((widget) => widget.metadata.id)\n\n if (resolvedWidgetIds && resolvedWidgetIds.length === 0) {\n log('No widgets resolved for dashboard seeding.')\n return false\n }\n\n await em.transactional(async (tem) => {\n for (const roleName of roleNames) {\n const isAdminRole = roleName === 'admin' || roleName === 'superadmin'\n const roleWidgetIds = resolvedWidgetIds ?? (isAdminRole ? allWidgetIds : defaultWidgetIds)\n if (!roleWidgetIds.length) {\n log(`No widgets resolved for role \"${roleName}\".`)\n continue\n }\n const role = await tem.findOne(Role, { name: roleName })\n if (!role) {\n log(`Skipping role \"${roleName}\" (not found)`)\n continue\n }\n const existing = await tem.findOne(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.widgetIdsJson = roleWidgetIds\n tem.persist(existing)\n log(`Updated dashboard widgets for role \"${roleName}\"`)\n } else {\n const record = tem.create(DashboardRoleWidgets, {\n roleId: String(role.id),\n tenantId,\n organizationId,\n widgetIdsJson: roleWidgetIds,\n createdAt: new Date(),\n updatedAt: null,\n deletedAt: null,\n })\n tem.persist(record)\n log(`Created dashboard widgets for role \"${roleName}\"`)\n }\n }\n })\n\n return true\n}\n\nconst seedDefaults: ModuleCli = {\n command: 'seed-defaults',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || null\n const roleCsv = args.roles || 'superadmin,admin,employee'\n const widgetCsv = args.widgets || ''\n if (!tenantId) {\n console.error('Usage: mercato dashboards seed-defaults --tenant <tenantId> [--roles superadmin,admin,employee] [--widgets id1,id2]')\n return\n }\n\n const roleNames = parseCommaSeparatedList(roleCsv)\n\n if (!roleNames.length) {\n console.log('No roles provided, nothing to seed.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n await seedDashboardDefaultsForTenant(em as EntityManager, {\n tenantId,\n organizationId,\n roleNames,\n widgetIds: widgetCsv ? parseCommaSeparatedList(widgetCsv) : undefined,\n logger: (message) => console.log(message),\n })\n },\n}\n\nconst enableAnalyticsWidgets: ModuleCli = {\n command: 'enable-analytics-widgets',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n const roleCsv = args.roles || 'admin,employee'\n if (!tenantId) {\n console.error('Usage: mercato dashboards enable-analytics-widgets --tenant <tenantId> [--org <orgId>] [--roles admin,employee]')\n return\n }\n\n const roleNames = parseCommaSeparatedList(roleCsv)\n\n if (!roleNames.length) {\n console.log('No roles provided, nothing to update.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const widgetIds = await resolveAnalyticsWidgetIds()\n\n const updated = await appendWidgetsToRoles(em, {\n tenantId,\n organizationId,\n roleNames,\n widgetIds,\n })\n\n if (!updated) {\n console.log('No dashboard role widgets updated.')\n }\n },\n}\n\nconst seedAnalytics: ModuleCli = {\n command: 'seed-analytics',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n const months = args.months ? parseInt(args.months, 10) : 6\n const ordersPerMonth = args.ordersPerMonth ? parseInt(args.ordersPerMonth, 10) : 50\n\n if (!tenantId || !organizationId) {\n console.error('Usage: mercato dashboards seed-analytics --tenant <tenantId> --organization <organizationId> [--months 6] [--ordersPerMonth 50]')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n\n console.log(`Seeding analytics data for ${months} months with ~${ordersPerMonth} orders/month...`)\n\n try {\n const result = await em.transactional(async (tem) =>\n seedAnalyticsData(tem, { tenantId, organizationId }, { months, ordersPerMonth })\n )\n\n if (result.orders === 0) {\n console.log('Analytics data already exists. Skipping seed.')\n } else {\n console.log(`Seeded analytics data:`)\n console.log(` - Orders: ${result.orders}`)\n console.log(` - Customers: ${result.customers}`)\n console.log(` - Products: ${result.products}`)\n console.log(` - Deals: ${result.deals}`)\n }\n } catch (error) {\n console.error('Failed to seed analytics data:', error)\n }\n },\n}\n\nconst debugAnalytics: ModuleCli = {\n command: 'debug-analytics',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = args.tenant || args.tenantId || null\n const organizationId = args.organization || args.organizationId || args.org || null\n\n if (!tenantId) {\n console.error('Usage: mercato dashboards debug-analytics --tenant <tenantId> [--organization <organizationId>]')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const conn = em.getConnection()\n\n console.log('Checking analytics data...\\n')\n\n const ordersResult = await conn.execute(\n `SELECT COUNT(*) as total, MIN(placed_at) as earliest, MAX(placed_at) as latest\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'`,\n [tenantId]\n )\n console.log('Orders summary:', ordersResult[0])\n\n const recentOrders = await conn.execute(\n `SELECT order_number, placed_at, status, grand_total_gross_amount::numeric as total\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'\n ORDER BY placed_at DESC LIMIT 5`,\n [tenantId]\n )\n console.log('\\nRecent orders:', recentOrders)\n\n const januaryOrders = await conn.execute(\n `SELECT COUNT(*) as count, SUM(grand_total_gross_amount::numeric) as total\n FROM sales_orders\n WHERE tenant_id = ?\n AND order_number LIKE 'SO-ANALYTICS-%'\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId]\n )\n console.log('\\nJanuary 2026 orders:', januaryOrders[0])\n\n const allOrders = await conn.execute(\n `SELECT COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ?`,\n [tenantId]\n )\n console.log('\\nTotal orders in tenant:', allOrders[0])\n\n const orgCheck = await conn.execute(\n `SELECT organization_id, COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ? AND order_number LIKE 'SO-ANALYTICS-%'\n GROUP BY organization_id`,\n [tenantId]\n )\n console.log('\\nOrders by organization:', orgCheck)\n\n if (organizationId) {\n const orgOrders = await conn.execute(\n `SELECT COUNT(*) as count, SUM(grand_total_gross_amount::numeric) as total\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ?\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, organizationId]\n )\n console.log(`\\nJanuary orders for org ${organizationId}:`, orgOrders[0])\n }\n\n // Check for NULL placed_at\n const nullPlacedAt = await conn.execute(\n `SELECT COUNT(*) as count\n FROM sales_orders\n WHERE tenant_id = ? AND placed_at IS NULL`,\n [tenantId]\n )\n console.log('\\nOrders with NULL placed_at:', nullPlacedAt[0])\n\n // Check non-analytics orders\n const nonAnalytics = await conn.execute(\n `SELECT order_number, placed_at, status, organization_id, grand_total_gross_amount::numeric as total\n FROM sales_orders\n WHERE tenant_id = ? AND order_number NOT LIKE 'SO-ANALYTICS-%'\n ORDER BY placed_at DESC NULLS LAST LIMIT 10`,\n [tenantId]\n )\n console.log('\\nNon-analytics orders:', nonAnalytics)\n\n // Simulate widget query\n console.log('\\n--- Simulating widget query for this_month ---')\n const widgetQuery = await conn.execute(\n `SELECT COALESCE(SUM(grand_total_gross_amount::numeric), 0) AS value\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ANY(?::uuid[])\n AND deleted_at IS NULL\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, `{${organizationId}}`]\n )\n console.log('Widget query result (revenue sum):', widgetQuery[0])\n\n const widgetCountQuery = await conn.execute(\n `SELECT COUNT(*) AS value\n FROM sales_orders\n WHERE tenant_id = ?\n AND organization_id = ANY(?::uuid[])\n AND deleted_at IS NULL\n AND placed_at >= '2026-01-01'\n AND placed_at <= '2026-01-31 23:59:59'`,\n [tenantId, `{${organizationId}}`]\n )\n console.log('Widget query result (order count):', widgetCountQuery[0])\n },\n}\n\nexport default [seedDefaults, enableAnalyticsWidgets, seedAnalytics, debugAnalytics]\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -34,7 +34,8 @@ function isValidCachedScope(value) {
34
34
  const record = value;
35
35
  const idOk = (v) => v === null || typeof v === "string";
36
36
  const arrOk = (v) => v === null || Array.isArray(v) && v.every((entry) => typeof entry === "string");
37
- return idOk(record.selectedId) && idOk(record.tenantId) && arrOk(record.filterIds) && arrOk(record.allowedIds);
37
+ const flagOk = record.selectionRejected === void 0 || typeof record.selectionRejected === "boolean";
38
+ return idOk(record.selectedId) && idOk(record.tenantId) && arrOk(record.filterIds) && arrOk(record.allowedIds) && flagOk;
38
39
  }
39
40
  function resolveCacheFromContainer(container) {
40
41
  if (!container) return null;
@@ -197,9 +198,10 @@ async function resolveOrganizationScope({
197
198
  const noOrgSelection = normalizedSelectedId === null && !explicitAllOrgsChoice;
198
199
  const widenToAllOrgs = explicitAllOrgsChoice && hasUnrestrictedAccess || effectiveSuperAdmin && noOrgSelection;
199
200
  const initialSelected = normalizedSelectedId ?? (widenToAllOrgs ? null : accountOrgId ?? null);
201
+ const selectionResolvesToOrg = (id) => orgDescendants.has(id);
200
202
  let effectiveSelected = null;
201
203
  if (initialSelected) {
202
- if (allowedSet === null || allowedSet.has(initialSelected)) {
204
+ if ((allowedSet === null || allowedSet.has(initialSelected)) && selectionResolvesToOrg(initialSelected)) {
203
205
  effectiveSelected = initialSelected;
204
206
  }
205
207
  }
@@ -212,6 +214,9 @@ async function resolveOrganizationScope({
212
214
  filterSet = null;
213
215
  } else if (auth.orgId) {
214
216
  filterSet = loadFallbackSet();
217
+ if (!effectiveSelected && fallbackOrgId && filterSet && filterSet.size > 0) {
218
+ effectiveSelected = fallbackOrgId;
219
+ }
215
220
  }
216
221
  if ((!filterSet || filterSet.size === 0) && fallbackOrgId && !widenToAllOrgs) {
217
222
  const computed = loadFallbackSet();
@@ -222,11 +227,13 @@ async function resolveOrganizationScope({
222
227
  }
223
228
  }
224
229
  }
230
+ const selectionRejected = normalizedSelectedId !== null && effectiveSelected !== normalizedSelectedId;
225
231
  return {
226
232
  selectedId: effectiveSelected,
227
233
  filterIds: filterSet ? Array.from(filterSet) : null,
228
234
  allowedIds: allowedSet ? Array.from(allowedSet) : null,
229
- tenantId
235
+ tenantId,
236
+ ...selectionRejected ? { selectionRejected: true } : {}
230
237
  };
231
238
  }
232
239
  async function resolveOrganizationScopeForRequest({