@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/modules/api_keys/cli.js +2 -1
- package/dist/modules/api_keys/cli.js.map +2 -2
- package/dist/modules/attachments/api/file/[id]/route.js +9 -7
- package/dist/modules/attachments/api/file/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +9 -7
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +21 -17
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/lib/reconcileOrganization.js +110 -0
- package/dist/modules/attachments/lib/reconcileOrganization.js.map +7 -0
- package/dist/modules/attachments/lib/requestScope.js +10 -0
- package/dist/modules/attachments/lib/requestScope.js.map +7 -0
- package/dist/modules/auth/api/users/route.js +6 -2
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/cli.js +3 -2
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/configs/lib/upgrade-actions.js +22 -0
- package/dist/modules/configs/lib/upgrade-actions.js.map +2 -2
- package/dist/modules/dashboards/cli.js +4 -3
- package/dist/modules/dashboards/cli.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +10 -3
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +5 -4
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/cli.js +2 -1
- package/dist/modules/entities/cli.js.map +2 -2
- package/package.json +7 -7
- package/src/modules/api_keys/cli.ts +2 -3
- package/src/modules/attachments/api/file/[id]/route.ts +11 -7
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +11 -7
- package/src/modules/attachments/api/route.ts +26 -21
- package/src/modules/attachments/lib/reconcileOrganization.ts +188 -0
- package/src/modules/attachments/lib/requestScope.ts +27 -0
- package/src/modules/auth/api/users/route.ts +11 -2
- package/src/modules/auth/cli.ts +3 -2
- package/src/modules/configs/i18n/de.json +5 -1
- package/src/modules/configs/i18n/en.json +5 -1
- package/src/modules/configs/i18n/es.json +5 -1
- package/src/modules/configs/i18n/pl.json +5 -1
- package/src/modules/configs/lib/upgrade-actions.ts +24 -0
- package/src/modules/dashboards/cli.ts +4 -9
- package/src/modules/directory/utils/organizationScope.ts +36 -2
- package/src/modules/entities/api/records.ts +5 -7
- package/src/modules/entities/cli.ts +2 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/entities/cli.ts"],
|
|
4
|
-
"sourcesContent": ["import { getCliModules, getDefaultEncryptionMaps, type Module, type ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport { CustomEntity, CustomFieldDef, EncryptionMap } from './data/entities'\nimport {\n installCustomEntitiesFromModules,\n getAggregatedCustomEntityConfigs,\n} from './lib/install-from-ce'\nimport readline from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { createKmsService, type KmsService, type TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport {\n decryptWithAesGcm,\n decryptWithAesGcmStrict,\n TenantDataEncryptionError,\n TenantDataEncryptionErrorCode,\n} from '@open-mercato/shared/lib/encryption/aes'\nimport {\n TenantDataEncryptionService,\n parseDecryptedFieldValue,\n} from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '../directory/data/entities'\nimport crypto from 'node:crypto'\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\nconst seedDefs: ModuleCli = {\n command: 'install',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const force = Boolean(args.force)\n const includeGlobal = args['no-global'] ? false : true\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Sync'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Reinstall: remove existing definitions for target scope and re-seed from modules\nconst reinstallDefs: ModuleCli = {\n command: 'reinstall',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const includeGlobal = globalOnly ? true : (args['no-global'] ? false : true)\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const aggregates = getAggregatedCustomEntityConfigs()\n const relevant = aggregates.filter((entry) => (globalOnly ? entry.spec?.global === true : true))\n if (!relevant.length) {\n console.log('No custom entities or fields discovered. Nothing to reinstall.')\n return\n }\n const entityIds = Array.from(new Set(relevant.map((entry) => entry.entityId)))\n if (!entityIds.length) {\n console.log('No entity ids discovered. Nothing to reinstall.')\n return\n }\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n if (dry) {\n console.log('Dry-run: would remove existing custom entity definitions before reinstall.')\n } else {\n const fieldWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) fieldWhere.tenantId = null\n else fieldWhere.tenantId = { $in: tenantIds }\n }\n const removedFields = await em.nativeDelete(CustomFieldDef, fieldWhere)\n\n const entityWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) entityWhere.tenantId = null\n else entityWhere.tenantId = { $in: tenantIds }\n }\n const removedEntities = await em.nativeDelete(CustomEntity, entityWhere)\n\n if (cache && entityIds.length) {\n try {\n await cache.deleteByTags(entityIds.map((id) => `custom-entity:${id}`))\n } catch {}\n }\n console.log(`Cleared definitions: fields=${removedFields}, entities=${removedEntities}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force: true,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Reinstall'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Interactive: add a single custom field definition\nconst addField: ModuleCli = {\n command: 'add-field',\n async run(rest) {\n const args = parseArgs(rest)\n const rl = readline.createInterface({ input, output })\n const ask = async (q: string, d?: string) => {\n const a = (await rl.question(d ? `${q} [${d}]: ` : `${q}: `)).trim()\n return a || (d ?? '')\n }\n const askBool = async (q: string, d = false) => {\n const a = (await ask(q, d ? 'y' : 'n')).toLowerCase()\n return parseBooleanToken(a) === true\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const entityId = (args.entity as string) || (args.e as string) || await ask('Entity ID (e.g., example:todo)')\n const isGlobal = args.global ? true : await askBool('Global (no organization)?', false)\n const orgId = isGlobal ? null : ((args.org as string) || (args.organizationId as string) || await ask('Organization ID'))\n const tenantId = isGlobal ? null : ((args.tenant as string) || (args.tenantId as string) || await ask('Tenant ID'))\n const key = (args.key as string) || await ask('Field key (snake_case)')\n let kind = (args.kind as string) || await ask(\"Kind (text|multiline|integer|float|boolean|select|currency|relation|attachment)\", 'text')\n kind = kind.toLowerCase()\n if (!['text','multiline','integer','float','boolean','select','currency','relation','attachment'].includes(kind)) throw new Error('Invalid kind')\n const label = (args.label as string) || (await ask('Label', key))\n const description = (args.description as string) || ''\n const required = args.required !== undefined ? Boolean(args.required) : await askBool('Required?', false)\n const multi = args.multi !== undefined ? Boolean(args.multi) : await askBool('Allow multiple?', false)\n let options: string[] | undefined\n if (kind === 'select') {\n const raw = (args.options as string) || await ask('Options (comma-separated)', 'low,medium,high')\n options = raw.split(',').map((s) => s.trim()).filter(Boolean)\n }\n let defaultValue: any = undefined\n const defRaw = (args.default as string) ?? (args.defaultValue as string)\n const needDefault = defRaw !== undefined ? defRaw : await ask('Default value (leave empty for none)', '')\n if (needDefault !== '') {\n switch (kind) {\n case 'integer': defaultValue = Number(needDefault); break\n case 'float': defaultValue = Number(needDefault); break\n case 'boolean': defaultValue = parseBooleanToken(String(needDefault)) === true; break\n default: defaultValue = String(needDefault)\n }\n }\n const filterable = args.filterable !== undefined ? Boolean(args.filterable) : await askBool('Filterable?', true)\n const listVisible = args.listVisible !== undefined ? Boolean(args.listVisible) : await askBool('Visible in list?', true)\n const formEditable = args.formEditable !== undefined ? Boolean(args.formEditable) : await askBool('Editable in forms?', true)\n const indexed = args.indexed !== undefined ? Boolean(args.indexed) : await askBool('Indexed?', false)\n\n const where = { entityId, organizationId: orgId, tenantId: tenantId, key }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: any = {}\n if (options) configJson.options = options\n if (defaultValue !== undefined) configJson.defaultValue = defaultValue\n if (required !== undefined) configJson.required = required\n if (multi !== undefined) configJson.multi = multi\n if (filterable !== undefined) configJson.filterable = filterable\n if (indexed !== undefined) configJson.indexed = indexed\n if (listVisible !== undefined) configJson.listVisible = listVisible\n if (formEditable !== undefined) configJson.formEditable = formEditable\n if (label !== undefined) configJson.label = label\n if (description !== undefined) configJson.description = description\n\n if (!existing) {\n await em.persist(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n })).flush()\n console.log(`Created custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n } else {\n existing.kind = kind as any\n existing.configJson = configJson\n existing.isActive = true\n await em.flush()\n console.log(`Updated custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n }\n } catch (e: any) {\n console.error('Failed:', e?.message || e)\n } finally {\n await rl.close()\n }\n },\n}\n\nfunction resolveEncryptionMapModules(): Module[] {\n const cliModules = getCliModules()\n if (cliModules.length > 0) return cliModules\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of getDefaultEncryptionMaps(resolveEncryptionMapModules())) {\n const existing = await em.findOne(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n existing.updatedAt = new Date()\n logger(`\uD83D\uDD12 Updated encryption map for ${spec.entityId} \u2728`)\n await em.persist(existing).flush()\n continue\n }\n const map = em.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fieldsJson: spec.fields,\n isActive: true,\n })\n await em.persist(map).flush()\n logger(`Created encryption map for ${spec.entityId}`)\n }\n}\n\nconst seedEncryptionMaps: ModuleCli = {\n command: 'seed-encryption',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenant as string) || (args.tenantId as string)\n const organizationId = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n\n if (!tenantId) {\n console.error('tenant id is required (use --tenant <uuid>)')\n return\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.warn('TENANT_DATA_ENCRYPTION is disabled; skipping encryption map seeding.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const logger = (msg: string) => console.log(msg)\n await upsertEncryptionMaps(em, tenantId, organizationId, logger)\n console.log('\u2705 Encryption maps seeded')\n },\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\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\nfunction resolveProperty(meta: any, field: string): { columnName: string | null; prop: any | null } {\n if (!meta?.properties) return { columnName: null, prop: null }\n const candidates = [\n field,\n field.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),\n field.replace(/([A-Z])/g, '_$1').toLowerCase(),\n ]\n for (const candidate of candidates) {\n const prop = meta.properties[candidate]\n const fieldName =\n prop?.fieldName ??\n (Array.isArray(prop?.fieldNames) && prop.fieldNames.length ? prop.fieldNames[0] : undefined)\n if (typeof fieldName === 'string' && fieldName.length) return { columnName: fieldName, prop }\n if (prop?.name) return { columnName: prop.name, prop }\n }\n return { columnName: null, prop: null }\n}\n\nfunction buildEntityMetaRegistry(em: any): Map<string, any> {\n const registry = em?.getMetadata?.()\n const allMetaRaw = typeof registry?.getAll === 'function' ? registry.getAll() : []\n const allMeta = Array.isArray(allMetaRaw) ? allMetaRaw : Object.values(allMetaRaw ?? {})\n const metaByEntityId = new Map<string, any>()\n for (const meta of allMeta) {\n const resolved = resolveEntityIdFromMetadata(meta)\n if (resolved) metaByEntityId.set(resolved, meta)\n }\n return metaByEntityId\n}\n\ninterface EncryptionMapMeta {\n entityId: string\n meta: any\n fields: Array<{ field: string; hashField?: string | null }>\n tenantId: string\n}\n\nfunction resolveMapMeta(\n map: EncryptionMap,\n metaByEntityId: Map<string, any>,\n warn: (msg: string) => void = () => {},\n): EncryptionMapMeta | null {\n const entityId = String(map.entityId)\n const meta = metaByEntityId.get(entityId)\n if (!meta) {\n warn(`Skipping ${entityId}: metadata not found.`)\n return null\n }\n const fields = Array.isArray(map.fieldsJson) ? map.fieldsJson : []\n if (!fields.length) return null\n const tenantId = map.tenantId ? String(map.tenantId) : null\n if (!tenantId) return null\n return { entityId, meta, fields, tenantId }\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId 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 && !tenantIdArg) {\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\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantIdArg ?? null,\n organizationId: organizationIdArg ?? null,\n })\n if (tenantIdArg) {\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(tenantIdArg) ?? Promise.resolve(null),\n encryptionService.getDek(tenantIdArg),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n } else {\n console.log('[rotate-encryption-key] dek fingerprints skipped (no tenantId)')\n }\n }\n\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\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const where: any = { deletedAt: null }\n if (tenantIdArg) where.tenantId = tenantIdArg\n if (organizationIdArg) where.organizationId = organizationIdArg\n const maps = await em.find(EncryptionMap, where)\n if (!maps.length) {\n console.log('No encryption maps found for the selected scope.')\n return\n }\n\n const formatValueForColumn = (prop: any, value: unknown): unknown => {\n if (value === null || value === undefined) return value\n const types = Array.isArray(prop?.columnTypes) ? prop.columnTypes : []\n const type = String(prop?.type ?? '').toLowerCase()\n const isJson = types.some((entry: string) => entry.toLowerCase().includes('json')) || type === 'json' || type === 'jsonb'\n if (!isJson) return value\n return JSON.stringify(value)\n }\n\n const resolveScopes = async (tenantId: string, organizationId: string | null) => {\n if (organizationId) return [{ tenantId, organizationId }]\n const orgs = await em.find(Organization, { tenant: tenantId })\n const scopes = orgs.map((org: Organization) => ({\n tenantId,\n organizationId: String(org.id),\n }))\n scopes.push({ tenantId, organizationId: null })\n return scopes\n }\n\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (\n entityId: string,\n meta: any,\n fields: Array<{ field: string; hashField?: string | null }>,\n scope: { tenantId: string; organizationId: string | null },\n ): Promise<number> => {\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const columns = new Set<string>()\n columns.add(pk)\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n if (resolved?.columnName) columns.add(resolved.columnName)\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n if (resolvedHash?.columnName) columns.add(resolvedHash.columnName)\n }\n }\n const columnList = Array.from(columns)\n if (!columnList.length) return 0\n const tableName = meta?.tableName\n if (!tableName) return 0\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const selectSql = `select ${columnList.map((c) => `\"${c}\"`).join(', ')} from ${qualifiedTable} where tenant_id = ? and organization_id is not distinct from ?`\n const rows = await conn.execute(selectSql, [scope.tenantId, scope.organizationId])\n const list = Array.isArray(rows) ? rows : []\n if (!list.length) return 0\n let updated = 0\n for (const row of list) {\n const payload: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rotate && !isEncryptedPayload(rawValue)) {\n continue\n }\n payload[rule.field] = rawValue\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n if (hashCol) payload[rule.hashField] = row[hashCol]\n }\n }\n if (rotate && !Object.keys(payload).length) {\n continue\n }\n if (rotate && oldKms) {\n let oldDek = oldDekCache.get(scope.tenantId) ?? null\n if (!oldDekCache.has(scope.tenantId)) {\n oldDek = await oldKms.getTenantDek(scope.tenantId)\n oldDekCache.set(scope.tenantId, oldDek)\n }\n for (const rule of fields) {\n const value = payload[rule.field]\n if (typeof value !== 'string' || !isEncryptedPayload(value)) continue\n const decrypted = decryptWithOldKey(value, oldDek)\n if (decrypted === null) continue\n payload[rule.field] = parseDecryptedFieldValue(decrypted)\n }\n }\n const encrypted = await encryptionService.encryptEntityPayload(\n entityId,\n payload,\n scope.tenantId,\n scope.organizationId,\n )\n const updates: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const nextValue = (encrypted as any)[rule.field]\n if (nextValue !== undefined && nextValue !== row[col]) {\n if (!rotate && isEncryptedPayload(row[col])) continue\n updates[col] = formatValueForColumn(resolved?.prop, nextValue)\n }\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n const hashValue = (encrypted as any)[rule.hashField]\n if (hashCol && hashValue !== undefined && hashValue !== row[hashCol]) {\n updates[hashCol] = formatValueForColumn(resolvedHash?.prop, hashValue)\n }\n }\n }\n if (!Object.keys(updates).length) continue\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `update ${qualifiedTable} set ${setSql} where \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n updated += 1\n }\n return updated\n }\n\n let total = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const scopes = await resolveScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n for (const scope of scopes) {\n const updated = await processScope(entityId, meta, fields, scope)\n if (updated > 0) {\n console.log(\n `${dryRun ? '[dry-run] ' : ''}Encrypted ${updated} record(s) for ${entityId} org=${scope.organizationId ?? 'null'}`\n )\n }\n total += updated\n }\n }\n\n if (total > 0) {\n console.log(`Encrypted ${total} record(s) across mapped entities.`)\n } else {\n console.log('All mapped entity fields already encrypted for the selected scope.')\n }\n },\n}\n\nconst decryptDatabase: ModuleCli = {\n command: 'decrypt-database',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const entityIdArg = (args.entity as string) || null\n const checkMode = Boolean(args.check)\n const dryRun = Boolean(args['dry-run'] || args.dry) || checkMode\n const deactivateMaps = Boolean(args['deactivate-maps'])\n const confirm = (args.confirm as string) || null\n const batchSize = Math.max(1, parseInt(String(args['batch-size'] || args.batchSize || '500'), 10) || 500)\n const sleepMs = Math.max(0, parseInt(String(args['sleep-ms'] || args.sleepMs || '0'), 10) || 0)\n const debug = Boolean(args.debug)\n\n if (!tenantIdArg) {\n console.error('--tenant <uuid> is required.')\n return\n }\n\n if (!checkMode) {\n if (!confirm) {\n console.error('--confirm <tenantUuid> is required (safety gate). Pass the exact tenant UUID to confirm the operation.')\n return\n }\n if (confirm !== tenantIdArg) {\n console.error(`--confirm value \"${confirm}\" does not match --tenant \"${tenantIdArg}\". Aborting.`)\n return\n }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw database connection; aborting.')\n return\n }\n\n if (!checkMode && !isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting. Data may already be decrypted.')\n return\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const resolveDecryptScopes = async (\n tenantId: string,\n organizationId: string | null,\n ): Promise<Array<{ tenantId: string; organizationId: string | null }>> => {\n if (organizationId) return [{ tenantId, organizationId }]\n const rows = await conn.execute(\n `SELECT DISTINCT organization_id FROM encryption_maps WHERE tenant_id = ? AND deleted_at IS NULL`,\n [tenantId],\n )\n const orgIds = new Set<string | null>()\n for (const row of Array.isArray(rows) ? rows : []) {\n orgIds.add(row.organization_id ?? null)\n }\n orgIds.add(null)\n return Array.from(orgIds).map((orgId) => ({ tenantId, organizationId: orgId }))\n }\n\n const mapWhere: any = { tenantId: tenantIdArg, deletedAt: null, isActive: true }\n if (organizationIdArg) mapWhere.organizationId = organizationIdArg\n if (entityIdArg) mapWhere.entityId = entityIdArg\n const maps = await em.find(EncryptionMap, mapWhere)\n\n if (!maps.length) {\n console.log('No active encryption maps found for the selected scope.')\n return\n }\n\n const kms = createKmsService()\n const dekCache = new Map<string, TenantDek | null>()\n const getDek = async (tenantId: string): Promise<TenantDek | null> => {\n if (dekCache.has(tenantId)) return dekCache.get(tenantId) ?? null\n const dek = await kms.getTenantDek(tenantId)\n dekCache.set(tenantId, dek)\n return dek\n }\n\n if (checkMode) {\n const envValue = process.env.TENANT_DATA_ENCRYPTION ?? '(not set)'\n console.log(`TENANT_DATA_ENCRYPTION = ${envValue}`)\n console.log(`Active EncryptionMap records for scope: ${maps.length}`)\n let encryptedCandidatesSampled = 0\n let malformedPayloadCountSampled = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId).catch(() => null)\n if (!dek) continue\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) continue\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n for (const scope of scopes) {\n const sampleRows = await conn.execute(\n `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ? LIMIT 100`,\n [scope.tenantId, scope.organizationId],\n ).catch(() => [])\n for (const row of Array.isArray(sampleRows) ? sampleRows : []) {\n let rowHasEncrypted = false\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n decryptWithAesGcmStrict(String(rawValue), dek.key)\n rowHasEncrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError && e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n malformedPayloadCountSampled++\n }\n }\n }\n if (rowHasEncrypted) encryptedCandidatesSampled++\n }\n }\n }\n console.log(`estimated encrypted candidates (sampled): ${encryptedCandidatesSampled}`)\n if (malformedPayloadCountSampled > 0) {\n console.warn(`\u26A0 malformed payloads (sampled): ${malformedPayloadCountSampled} \u2014 may indicate corruption`)\n } else {\n console.log(`malformed payloads (sampled): ${malformedPayloadCountSampled}`)\n }\n console.log('not a proof of absence \u2014 run full command + rerun --check to confirm')\n return\n }\n\n let totalRowsFetched = 0\n let totalRowsUpdated = 0\n let totalHashFieldsCleared = 0\n const totalHashFieldsSkipped = new Set<string>()\n let totalMalformedPayloadCount = 0\n const malformedByLocation = new Map<string, number>()\n let totalEntitiesProcessed = 0\n\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId)\n if (!dek) {\n console.warn(`No DEK available for tenant ${tenantId}; skipping ${entityId}.`)\n continue\n }\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) {\n console.warn(`Skipping ${entityId}: table name not found.`)\n continue\n }\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n totalEntitiesProcessed++\n\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n\n for (const scope of scopes) {\n let lastId: string | null = null\n let scopeMalformedCount = 0\n\n while (true) {\n let selectSql = `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ?`\n const selectParams: unknown[] = [scope.tenantId, scope.organizationId]\n if (lastId !== null) {\n selectSql += ` AND \"${pk}\" > ?`\n selectParams.push(lastId)\n }\n selectSql += ` ORDER BY \"${pk}\" LIMIT ?`\n selectParams.push(batchSize)\n\n const batchRows = await conn.execute(selectSql, selectParams)\n const batch = Array.isArray(batchRows) ? batchRows : []\n if (!batch.length) break\n\n lastId = String(batch[batch.length - 1]![pk])\n totalRowsFetched += batch.length\n\n const batchStart = Date.now()\n await conn.execute('BEGIN')\n let batchCommitted = false\n try {\n for (const row of batch) {\n const updates: Record<string, unknown> = {}\n let rowDecrypted = false\n\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n const decrypted = decryptWithAesGcmStrict(String(rawValue), dek.key)\n let valueToWrite: string\n try {\n const parsed = JSON.parse(decrypted)\n valueToWrite = typeof parsed === 'string' ? parsed : decrypted\n } catch {\n valueToWrite = decrypted\n }\n updates[col] = valueToWrite\n rowDecrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError) {\n if (e.code === TenantDataEncryptionErrorCode.AUTH_FAILED) {\n // Value is plaintext \u2014 skip silently\n } else if (e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n scopeMalformedCount++\n const locationKey = `${tableName}:${col}`\n malformedByLocation.set(locationKey, (malformedByLocation.get(locationKey) ?? 0) + 1)\n console.warn(`\u26A0 MALFORMED_PAYLOAD for ${entityId} field \"${col}\" row ${row[pk]}; skipping field.`)\n } else {\n throw e\n }\n } else {\n throw e\n }\n }\n }\n\n if (rowDecrypted) {\n for (const fieldRule of fields) {\n if (!fieldRule.hashField) continue\n const resolvedHash = resolveProperty(meta, fieldRule.hashField)\n const hashCol = resolvedHash?.columnName\n if (!hashCol) {\n const skippedKey = `${tableName}:${fieldRule.hashField}`\n if (!totalHashFieldsSkipped.has(skippedKey)) {\n console.warn(`\u26A0 Hash column \"${fieldRule.hashField}\" not found in metadata for ${entityId}; skipping.`)\n totalHashFieldsSkipped.add(skippedKey)\n }\n continue\n }\n updates[hashCol] = null\n totalHashFieldsCleared++\n }\n }\n\n if (Object.keys(updates).length > 0) {\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `UPDATE ${qualifiedTable} SET ${setSql} WHERE \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n totalRowsUpdated++\n }\n }\n await conn.execute('COMMIT')\n batchCommitted = true\n } catch (fatalErr: any) {\n if (!batchCommitted) {\n try { await conn.execute('ROLLBACK') } catch {}\n }\n console.error(`Fatal error during batch processing for ${entityId}: ${(fatalErr as Error)?.message || String(fatalErr)}`)\n throw fatalErr\n }\n\n const batchDurationMs = Date.now() - batchStart\n if (debug) {\n console.log(\n `[debug] Batch ${entityId} org=${scope.organizationId ?? 'null'}: ${batch.length} rows in ${batchDurationMs}ms, scopeMalformed=${scopeMalformedCount}`,\n )\n if (batchDurationMs > 30_000) {\n console.warn('\u26A0 Batch took >30s; consider reducing --batch-size.')\n }\n }\n if (sleepMs > 0) {\n await new Promise<void>((r) => setTimeout(r, sleepMs))\n }\n }\n\n totalMalformedPayloadCount += scopeMalformedCount\n }\n }\n\n if (deactivateMaps && !dryRun) {\n let deactivateSql = `UPDATE encryption_maps SET is_active = false, deleted_at = now() WHERE tenant_id = ? AND deleted_at IS NULL`\n const deactivateParams: unknown[] = [tenantIdArg]\n if (organizationIdArg) {\n deactivateSql += ` AND (organization_id = ? OR organization_id IS NULL)`\n deactivateParams.push(organizationIdArg)\n }\n if (entityIdArg) {\n deactivateSql += ` AND entity_id = ?`\n deactivateParams.push(entityIdArg)\n }\n await conn.execute(deactivateSql, deactivateParams)\n console.warn('\u26A0 Restart all application replicas \u2014 in-process map caches may still be active.')\n if (isTenantDataEncryptionEnabled()) {\n console.warn('\u26A0 Env TENANT_DATA_ENCRYPTION is still true \u2014 new writes will be re-encrypted until env is updated and replicas restarted.')\n }\n }\n\n if (deactivateMaps && dryRun) {\n console.log(`[dry-run] Would deactivate ${maps.length} EncryptionMap record(s).`)\n }\n\n const prefix = dryRun ? '[dry-run] ' : ''\n console.log(`\\n${prefix}Decryption summary:`)\n console.log(` Rows fetched: ${totalRowsFetched}`)\n console.log(` Rows updated: ${totalRowsUpdated}`)\n console.log(` Entities processed: ${totalEntitiesProcessed}`)\n console.log(` Hash fields cleared: ${totalHashFieldsCleared}`)\n if (totalHashFieldsSkipped.size > 0) {\n console.log(` Hash fields skipped (missing columns): ${Array.from(totalHashFieldsSkipped).join(', ')}`)\n }\n if (totalMalformedPayloadCount > 0) {\n console.warn(\n ` \u26A0 ${totalMalformedPayloadCount} field value(s) returned MALFORMED_PAYLOAD and were skipped; these may be corrupted ciphertexts. Investigate before assuming decryption is complete.`,\n )\n if (debug && malformedByLocation.size > 0) {\n const top = Array.from(malformedByLocation.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n console.log(' Top malformed locations:')\n for (const [loc, count] of top) {\n console.log(` ${loc}: ${count}`)\n }\n }\n }\n\n if (!dryRun) {\n console.log(`\\n\u2705 Decryption complete. Required next steps:`)\n console.log(` 1. Set TENANT_DATA_ENCRYPTION=false in your environment / secrets`)\n console.log(` 2. Restart all application replicas`)\n console.log(` 3. Run: mercato query_index reindex --tenant ${tenantIdArg} \u2190 run after env flip + restart; search/filter degraded until this completes`)\n console.log(` 4. Run: mercato entities decrypt-database --tenant ${tenantIdArg} --check to confirm no encrypted values remain`)\n console.log(` NOTE: if the run was long and concurrent inserts occurred, run again before step 4 \u2014 it is idempotent.`)\n }\n },\n}\n\n// Keep default export stable (install first for help listing)\nexport default [seedDefs, reinstallDefs, addField, seedEncryptionMaps, rotateEncryptionKey, decryptDatabase]\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { getCliModules, getDefaultEncryptionMaps, type Module, type ModuleCli } from '@open-mercato/shared/modules/registry'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { CacheStrategy } from '@open-mercato/cache/types'\nimport { CustomEntity, CustomFieldDef, EncryptionMap } from './data/entities'\nimport {\n installCustomEntitiesFromModules,\n getAggregatedCustomEntityConfigs,\n} from './lib/install-from-ce'\nimport readline from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\nimport { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'\nimport { createKmsService, type KmsService, type TenantDek } from '@open-mercato/shared/lib/encryption/kms'\nimport {\n decryptWithAesGcm,\n decryptWithAesGcmStrict,\n TenantDataEncryptionError,\n TenantDataEncryptionErrorCode,\n} from '@open-mercato/shared/lib/encryption/aes'\nimport {\n TenantDataEncryptionService,\n parseDecryptedFieldValue,\n} from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\nimport { resolveEntityIdFromMetadata } from '@open-mercato/shared/lib/encryption/entityIds'\nimport { Organization } from '../directory/data/entities'\nimport crypto from 'node:crypto'\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\nconst seedDefs: ModuleCli = {\n command: 'install',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const force = Boolean(args.force)\n const includeGlobal = args['no-global'] ? false : true\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Sync'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Reinstall: remove existing definitions for target scope and re-seed from modules\nconst reinstallDefs: ModuleCli = {\n command: 'reinstall',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string)\n const globalOnly = Boolean(args.global)\n const dry = Boolean(args['dry-run'] || args.dry)\n const includeGlobal = globalOnly ? true : (args['no-global'] ? false : true)\n\n if (globalOnly && includeGlobal === false) {\n console.error('Cannot combine --global with --no-global.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n let cache: CacheStrategy | null = null\n try { cache = resolve('cache') as CacheStrategy } catch {}\n\n const tenantIds = tenantIdArg\n ? [tenantIdArg]\n : (globalOnly ? [] : undefined)\n\n const aggregates = getAggregatedCustomEntityConfigs()\n const relevant = aggregates.filter((entry) => (globalOnly ? entry.spec?.global === true : true))\n if (!relevant.length) {\n console.log('No custom entities or fields discovered. Nothing to reinstall.')\n return\n }\n const entityIds = Array.from(new Set(relevant.map((entry) => entry.entityId)))\n if (!entityIds.length) {\n console.log('No entity ids discovered. Nothing to reinstall.')\n return\n }\n\n const logger = (message: string) => {\n const prefix = dry ? '[dry-run] ' : ''\n console.log(`${prefix}${message}`)\n }\n\n if (dry) {\n console.log('Dry-run: would remove existing custom entity definitions before reinstall.')\n } else {\n const fieldWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) fieldWhere.tenantId = null\n else fieldWhere.tenantId = { $in: tenantIds }\n }\n const removedFields = await em.nativeDelete(CustomFieldDef, fieldWhere)\n\n const entityWhere: any = { entityId: { $in: entityIds } }\n if (tenantIds !== undefined) {\n if (tenantIds.length === 0) entityWhere.tenantId = null\n else entityWhere.tenantId = { $in: tenantIds }\n }\n const removedEntities = await em.nativeDelete(CustomEntity, entityWhere)\n\n if (cache && entityIds.length) {\n try {\n await cache.deleteByTags(entityIds.map((id) => `custom-entity:${id}`))\n } catch {}\n }\n console.log(`Cleared definitions: fields=${removedFields}, entities=${removedEntities}`)\n }\n\n const result = await installCustomEntitiesFromModules(em, cache, {\n tenantIds,\n includeGlobal,\n dryRun: dry,\n force: true,\n logger,\n })\n const label = dry ? 'Dry-run' : 'Reinstall'\n console.log(`\u2705 ${label} complete: processed=${result.processed}, updated=${result.synchronized}, fieldsChanged=${result.fieldChanges}, skipped=${result.skipped}`)\n },\n}\n\n// Interactive: add a single custom field definition\nconst addField: ModuleCli = {\n command: 'add-field',\n async run(rest) {\n const args = parseArgs(rest)\n const rl = readline.createInterface({ input, output })\n const ask = async (q: string, d?: string) => {\n const a = (await rl.question(d ? `${q} [${d}]: ` : `${q}: `)).trim()\n return a || (d ?? '')\n }\n const askBool = async (q: string, d = false) => {\n const a = (await ask(q, d ? 'y' : 'n')).toLowerCase()\n return parseBooleanToken(a) === true\n }\n\n try {\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n\n const entityId = (args.entity as string) || (args.e as string) || await ask('Entity ID (e.g., example:todo)')\n const isGlobal = args.global ? true : await askBool('Global (no organization)?', false)\n const orgId = isGlobal ? null : ((args.org as string) || (args.organizationId as string) || await ask('Organization ID'))\n const tenantId = isGlobal ? null : ((args.tenant as string) || (args.tenantId as string) || await ask('Tenant ID'))\n const key = (args.key as string) || await ask('Field key (snake_case)')\n let kind = (args.kind as string) || await ask(\"Kind (text|multiline|integer|float|boolean|select|currency|relation|attachment)\", 'text')\n kind = kind.toLowerCase()\n if (!['text','multiline','integer','float','boolean','select','currency','relation','attachment'].includes(kind)) throw new Error('Invalid kind')\n const label = (args.label as string) || (await ask('Label', key))\n const description = (args.description as string) || ''\n const required = args.required !== undefined ? Boolean(args.required) : await askBool('Required?', false)\n const multi = args.multi !== undefined ? Boolean(args.multi) : await askBool('Allow multiple?', false)\n let options: string[] | undefined\n if (kind === 'select') {\n const raw = (args.options as string) || await ask('Options (comma-separated)', 'low,medium,high')\n options = parseCommaSeparatedList(raw)\n }\n let defaultValue: any = undefined\n const defRaw = (args.default as string) ?? (args.defaultValue as string)\n const needDefault = defRaw !== undefined ? defRaw : await ask('Default value (leave empty for none)', '')\n if (needDefault !== '') {\n switch (kind) {\n case 'integer': defaultValue = Number(needDefault); break\n case 'float': defaultValue = Number(needDefault); break\n case 'boolean': defaultValue = parseBooleanToken(String(needDefault)) === true; break\n default: defaultValue = String(needDefault)\n }\n }\n const filterable = args.filterable !== undefined ? Boolean(args.filterable) : await askBool('Filterable?', true)\n const listVisible = args.listVisible !== undefined ? Boolean(args.listVisible) : await askBool('Visible in list?', true)\n const formEditable = args.formEditable !== undefined ? Boolean(args.formEditable) : await askBool('Editable in forms?', true)\n const indexed = args.indexed !== undefined ? Boolean(args.indexed) : await askBool('Indexed?', false)\n\n const where = { entityId, organizationId: orgId, tenantId: tenantId, key }\n const existing = await em.findOne(CustomFieldDef, where)\n const configJson: any = {}\n if (options) configJson.options = options\n if (defaultValue !== undefined) configJson.defaultValue = defaultValue\n if (required !== undefined) configJson.required = required\n if (multi !== undefined) configJson.multi = multi\n if (filterable !== undefined) configJson.filterable = filterable\n if (indexed !== undefined) configJson.indexed = indexed\n if (listVisible !== undefined) configJson.listVisible = listVisible\n if (formEditable !== undefined) configJson.formEditable = formEditable\n if (label !== undefined) configJson.label = label\n if (description !== undefined) configJson.description = description\n\n if (!existing) {\n await em.persist(em.create(CustomFieldDef, {\n entityId,\n organizationId: orgId,\n tenantId: tenantId,\n key,\n kind,\n configJson,\n isActive: true,\n })).flush()\n console.log(`Created custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n } else {\n existing.kind = kind as any\n existing.configJson = configJson\n existing.isActive = true\n await em.flush()\n console.log(`Updated custom field: ${entityId}.${key} (${kind})${orgId == null ? ' [global]' : ` [org=${orgId}, tenant=${tenantId}]`}`)\n }\n } catch (e: any) {\n console.error('Failed:', e?.message || e)\n } finally {\n await rl.close()\n }\n },\n}\n\nfunction resolveEncryptionMapModules(): Module[] {\n const cliModules = getCliModules()\n if (cliModules.length > 0) return cliModules\n try {\n const { getModules } = require('@open-mercato/shared/lib/modules/registry')\n return getModules()\n } catch {\n return []\n }\n}\n\nasync function upsertEncryptionMaps(em: any, tenantId: string, organizationId: string | null, logger: (msg: string) => void) {\n for (const spec of getDefaultEncryptionMaps(resolveEncryptionMapModules())) {\n const existing = await em.findOne(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (existing) {\n existing.fieldsJson = spec.fields\n existing.isActive = true\n existing.updatedAt = new Date()\n logger(`\uD83D\uDD12 Updated encryption map for ${spec.entityId} \u2728`)\n await em.persist(existing).flush()\n continue\n }\n const map = em.create(EncryptionMap, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fieldsJson: spec.fields,\n isActive: true,\n })\n await em.persist(map).flush()\n logger(`Created encryption map for ${spec.entityId}`)\n }\n}\n\nconst seedEncryptionMaps: ModuleCli = {\n command: 'seed-encryption',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantId = (args.tenant as string) || (args.tenantId as string)\n const organizationId = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n\n if (!tenantId) {\n console.error('tenant id is required (use --tenant <uuid>)')\n return\n }\n if (!isTenantDataEncryptionEnabled()) {\n console.warn('TENANT_DATA_ENCRYPTION is disabled; skipping encryption map seeding.')\n return\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const logger = (msg: string) => console.log(msg)\n await upsertEncryptionMaps(em, tenantId, organizationId, logger)\n console.log('\u2705 Encryption maps seeded')\n },\n}\n\nfunction normalizeKeyInput(value: string): string {\n return value.trim().replace(/(?:^['\"]|['\"]$)/g, '')\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\nfunction resolveProperty(meta: any, field: string): { columnName: string | null; prop: any | null } {\n if (!meta?.properties) return { columnName: null, prop: null }\n const candidates = [\n field,\n field.replace(/_([a-z])/g, (_, c) => c.toUpperCase()),\n field.replace(/([A-Z])/g, '_$1').toLowerCase(),\n ]\n for (const candidate of candidates) {\n const prop = meta.properties[candidate]\n const fieldName =\n prop?.fieldName ??\n (Array.isArray(prop?.fieldNames) && prop.fieldNames.length ? prop.fieldNames[0] : undefined)\n if (typeof fieldName === 'string' && fieldName.length) return { columnName: fieldName, prop }\n if (prop?.name) return { columnName: prop.name, prop }\n }\n return { columnName: null, prop: null }\n}\n\nfunction buildEntityMetaRegistry(em: any): Map<string, any> {\n const registry = em?.getMetadata?.()\n const allMetaRaw = typeof registry?.getAll === 'function' ? registry.getAll() : []\n const allMeta = Array.isArray(allMetaRaw) ? allMetaRaw : Object.values(allMetaRaw ?? {})\n const metaByEntityId = new Map<string, any>()\n for (const meta of allMeta) {\n const resolved = resolveEntityIdFromMetadata(meta)\n if (resolved) metaByEntityId.set(resolved, meta)\n }\n return metaByEntityId\n}\n\ninterface EncryptionMapMeta {\n entityId: string\n meta: any\n fields: Array<{ field: string; hashField?: string | null }>\n tenantId: string\n}\n\nfunction resolveMapMeta(\n map: EncryptionMap,\n metaByEntityId: Map<string, any>,\n warn: (msg: string) => void = () => {},\n): EncryptionMapMeta | null {\n const entityId = String(map.entityId)\n const meta = metaByEntityId.get(entityId)\n if (!meta) {\n warn(`Skipping ${entityId}: metadata not found.`)\n return null\n }\n const fields = Array.isArray(map.fieldsJson) ? map.fieldsJson : []\n if (!fields.length) return null\n const tenantId = map.tenantId ? String(map.tenantId) : null\n if (!tenantId) return null\n return { entityId, meta, fields, tenantId }\n}\n\nconst rotateEncryptionKey: ModuleCli = {\n command: 'rotate-encryption-key',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId 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 && !tenantIdArg) {\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\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw connection; aborting.')\n return\n }\n\n const encryptionService = new TenantDataEncryptionService(em as any, { kms: createKmsService() })\n const oldKms = rotate && oldKey ? new DerivedKeyKmsService(oldKey) : null\n if (!encryptionService.isEnabled()) {\n console.error('Encryption service is not enabled (KMS unhealthy or no DEK). Aborting.')\n return\n }\n\n if (debug) {\n console.log('[rotate-encryption-key]', {\n hasOldKey: Boolean(oldKey),\n rotate,\n tenantId: tenantIdArg ?? null,\n organizationId: organizationIdArg ?? null,\n })\n if (tenantIdArg) {\n const [oldDek, newDek] = await Promise.all([\n oldKms?.getTenantDek(tenantIdArg) ?? Promise.resolve(null),\n encryptionService.getDek(tenantIdArg),\n ])\n console.log('[rotate-encryption-key] dek fingerprints', {\n oldKey: fingerprintDek(oldDek),\n currentKey: fingerprintDek(newDek),\n })\n } else {\n console.log('[rotate-encryption-key] dek fingerprints skipped (no tenantId)')\n }\n }\n\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\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const where: any = { deletedAt: null }\n if (tenantIdArg) where.tenantId = tenantIdArg\n if (organizationIdArg) where.organizationId = organizationIdArg\n const maps = await em.find(EncryptionMap, where)\n if (!maps.length) {\n console.log('No encryption maps found for the selected scope.')\n return\n }\n\n const formatValueForColumn = (prop: any, value: unknown): unknown => {\n if (value === null || value === undefined) return value\n const types = Array.isArray(prop?.columnTypes) ? prop.columnTypes : []\n const type = String(prop?.type ?? '').toLowerCase()\n const isJson = types.some((entry: string) => entry.toLowerCase().includes('json')) || type === 'json' || type === 'jsonb'\n if (!isJson) return value\n return JSON.stringify(value)\n }\n\n const resolveScopes = async (tenantId: string, organizationId: string | null) => {\n if (organizationId) return [{ tenantId, organizationId }]\n const orgs = await em.find(Organization, { tenant: tenantId })\n const scopes = orgs.map((org: Organization) => ({\n tenantId,\n organizationId: String(org.id),\n }))\n scopes.push({ tenantId, organizationId: null })\n return scopes\n }\n\n const oldDekCache = new Map<string, TenantDek | null>()\n const processScope = async (\n entityId: string,\n meta: any,\n fields: Array<{ field: string; hashField?: string | null }>,\n scope: { tenantId: string; organizationId: string | null },\n ): Promise<number> => {\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const columns = new Set<string>()\n columns.add(pk)\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n if (resolved?.columnName) columns.add(resolved.columnName)\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n if (resolvedHash?.columnName) columns.add(resolvedHash.columnName)\n }\n }\n const columnList = Array.from(columns)\n if (!columnList.length) return 0\n const tableName = meta?.tableName\n if (!tableName) return 0\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const selectSql = `select ${columnList.map((c) => `\"${c}\"`).join(', ')} from ${qualifiedTable} where tenant_id = ? and organization_id is not distinct from ?`\n const rows = await conn.execute(selectSql, [scope.tenantId, scope.organizationId])\n const list = Array.isArray(rows) ? rows : []\n if (!list.length) return 0\n let updated = 0\n for (const row of list) {\n const payload: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rotate && !isEncryptedPayload(rawValue)) {\n continue\n }\n payload[rule.field] = rawValue\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n if (hashCol) payload[rule.hashField] = row[hashCol]\n }\n }\n if (rotate && !Object.keys(payload).length) {\n continue\n }\n if (rotate && oldKms) {\n let oldDek = oldDekCache.get(scope.tenantId) ?? null\n if (!oldDekCache.has(scope.tenantId)) {\n oldDek = await oldKms.getTenantDek(scope.tenantId)\n oldDekCache.set(scope.tenantId, oldDek)\n }\n for (const rule of fields) {\n const value = payload[rule.field]\n if (typeof value !== 'string' || !isEncryptedPayload(value)) continue\n const decrypted = decryptWithOldKey(value, oldDek)\n if (decrypted === null) continue\n payload[rule.field] = parseDecryptedFieldValue(decrypted)\n }\n }\n const encrypted = await encryptionService.encryptEntityPayload(\n entityId,\n payload,\n scope.tenantId,\n scope.organizationId,\n )\n const updates: Record<string, unknown> = {}\n for (const rule of fields) {\n const resolved = resolveProperty(meta, rule.field)\n const col = resolved?.columnName\n if (!col) continue\n const nextValue = (encrypted as any)[rule.field]\n if (nextValue !== undefined && nextValue !== row[col]) {\n if (!rotate && isEncryptedPayload(row[col])) continue\n updates[col] = formatValueForColumn(resolved?.prop, nextValue)\n }\n if (rule.hashField) {\n const resolvedHash = resolveProperty(meta, rule.hashField)\n const hashCol = resolvedHash?.columnName\n const hashValue = (encrypted as any)[rule.hashField]\n if (hashCol && hashValue !== undefined && hashValue !== row[hashCol]) {\n updates[hashCol] = formatValueForColumn(resolvedHash?.prop, hashValue)\n }\n }\n }\n if (!Object.keys(updates).length) continue\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `update ${qualifiedTable} set ${setSql} where \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n updated += 1\n }\n return updated\n }\n\n let total = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const scopes = await resolveScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n for (const scope of scopes) {\n const updated = await processScope(entityId, meta, fields, scope)\n if (updated > 0) {\n console.log(\n `${dryRun ? '[dry-run] ' : ''}Encrypted ${updated} record(s) for ${entityId} org=${scope.organizationId ?? 'null'}`\n )\n }\n total += updated\n }\n }\n\n if (total > 0) {\n console.log(`Encrypted ${total} record(s) across mapped entities.`)\n } else {\n console.log('All mapped entity fields already encrypted for the selected scope.')\n }\n },\n}\n\nconst decryptDatabase: ModuleCli = {\n command: 'decrypt-database',\n async run(rest) {\n const args = parseArgs(rest)\n const tenantIdArg = (args.tenant as string) || (args.tenantId as string) || null\n const organizationIdArg = (args.org as string) || (args.organization as string) || (args.organizationId as string) || null\n const entityIdArg = (args.entity as string) || null\n const checkMode = Boolean(args.check)\n const dryRun = Boolean(args['dry-run'] || args.dry) || checkMode\n const deactivateMaps = Boolean(args['deactivate-maps'])\n const confirm = (args.confirm as string) || null\n const batchSize = Math.max(1, parseInt(String(args['batch-size'] || args.batchSize || '500'), 10) || 500)\n const sleepMs = Math.max(0, parseInt(String(args['sleep-ms'] || args.sleepMs || '0'), 10) || 0)\n const debug = Boolean(args.debug)\n\n if (!tenantIdArg) {\n console.error('--tenant <uuid> is required.')\n return\n }\n\n if (!checkMode) {\n if (!confirm) {\n console.error('--confirm <tenantUuid> is required (safety gate). Pass the exact tenant UUID to confirm the operation.')\n return\n }\n if (confirm !== tenantIdArg) {\n console.error(`--confirm value \"${confirm}\" does not match --tenant \"${tenantIdArg}\". Aborting.`)\n return\n }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const conn: any = em?.getConnection?.()\n if (!conn || typeof conn.execute !== 'function') {\n console.error('Unable to access raw database connection; aborting.')\n return\n }\n\n if (!checkMode && !isTenantDataEncryptionEnabled()) {\n console.error('TENANT_DATA_ENCRYPTION is disabled; aborting. Data may already be decrypted.')\n return\n }\n\n const metaByEntityId = buildEntityMetaRegistry(em)\n\n const resolveDecryptScopes = async (\n tenantId: string,\n organizationId: string | null,\n ): Promise<Array<{ tenantId: string; organizationId: string | null }>> => {\n if (organizationId) return [{ tenantId, organizationId }]\n const rows = await conn.execute(\n `SELECT DISTINCT organization_id FROM encryption_maps WHERE tenant_id = ? AND deleted_at IS NULL`,\n [tenantId],\n )\n const orgIds = new Set<string | null>()\n for (const row of Array.isArray(rows) ? rows : []) {\n orgIds.add(row.organization_id ?? null)\n }\n orgIds.add(null)\n return Array.from(orgIds).map((orgId) => ({ tenantId, organizationId: orgId }))\n }\n\n const mapWhere: any = { tenantId: tenantIdArg, deletedAt: null, isActive: true }\n if (organizationIdArg) mapWhere.organizationId = organizationIdArg\n if (entityIdArg) mapWhere.entityId = entityIdArg\n const maps = await em.find(EncryptionMap, mapWhere)\n\n if (!maps.length) {\n console.log('No active encryption maps found for the selected scope.')\n return\n }\n\n const kms = createKmsService()\n const dekCache = new Map<string, TenantDek | null>()\n const getDek = async (tenantId: string): Promise<TenantDek | null> => {\n if (dekCache.has(tenantId)) return dekCache.get(tenantId) ?? null\n const dek = await kms.getTenantDek(tenantId)\n dekCache.set(tenantId, dek)\n return dek\n }\n\n if (checkMode) {\n const envValue = process.env.TENANT_DATA_ENCRYPTION ?? '(not set)'\n console.log(`TENANT_DATA_ENCRYPTION = ${envValue}`)\n console.log(`Active EncryptionMap records for scope: ${maps.length}`)\n let encryptedCandidatesSampled = 0\n let malformedPayloadCountSampled = 0\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId).catch(() => null)\n if (!dek) continue\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) continue\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n for (const scope of scopes) {\n const sampleRows = await conn.execute(\n `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ? LIMIT 100`,\n [scope.tenantId, scope.organizationId],\n ).catch(() => [])\n for (const row of Array.isArray(sampleRows) ? sampleRows : []) {\n let rowHasEncrypted = false\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n decryptWithAesGcmStrict(String(rawValue), dek.key)\n rowHasEncrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError && e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n malformedPayloadCountSampled++\n }\n }\n }\n if (rowHasEncrypted) encryptedCandidatesSampled++\n }\n }\n }\n console.log(`estimated encrypted candidates (sampled): ${encryptedCandidatesSampled}`)\n if (malformedPayloadCountSampled > 0) {\n console.warn(`\u26A0 malformed payloads (sampled): ${malformedPayloadCountSampled} \u2014 may indicate corruption`)\n } else {\n console.log(`malformed payloads (sampled): ${malformedPayloadCountSampled}`)\n }\n console.log('not a proof of absence \u2014 run full command + rerun --check to confirm')\n return\n }\n\n let totalRowsFetched = 0\n let totalRowsUpdated = 0\n let totalHashFieldsCleared = 0\n const totalHashFieldsSkipped = new Set<string>()\n let totalMalformedPayloadCount = 0\n const malformedByLocation = new Map<string, number>()\n let totalEntitiesProcessed = 0\n\n for (const map of maps) {\n const mapMeta = resolveMapMeta(map, metaByEntityId, console.warn)\n if (!mapMeta) continue\n const { entityId, meta, fields, tenantId } = mapMeta\n const dek = await getDek(tenantId)\n if (!dek) {\n console.warn(`No DEK available for tenant ${tenantId}; skipping ${entityId}.`)\n continue\n }\n const pk = Array.isArray(meta?.primaryKeys) && meta.primaryKeys.length ? meta.primaryKeys[0] : 'id'\n const tableName = meta?.tableName\n if (!tableName) {\n console.warn(`Skipping ${entityId}: table name not found.`)\n continue\n }\n const schema = meta?.schema\n const qualifiedTable = schema ? `\"${schema}\".\"${tableName}\"` : `\"${tableName}\"`\n const fieldCols = fields.flatMap((f: any) => {\n const r = resolveProperty(meta, f.field)\n return r.columnName ? [r.columnName] : []\n })\n const colList = Array.from(new Set([pk, ...fieldCols]))\n totalEntitiesProcessed++\n\n const scopes = await resolveDecryptScopes(tenantId, map.organizationId ? String(map.organizationId) : null)\n\n for (const scope of scopes) {\n let lastId: string | null = null\n let scopeMalformedCount = 0\n\n while (true) {\n let selectSql = `SELECT ${colList.map((c: string) => `\"${c}\"`).join(', ')} FROM ${qualifiedTable} WHERE tenant_id = ? AND organization_id IS NOT DISTINCT FROM ?`\n const selectParams: unknown[] = [scope.tenantId, scope.organizationId]\n if (lastId !== null) {\n selectSql += ` AND \"${pk}\" > ?`\n selectParams.push(lastId)\n }\n selectSql += ` ORDER BY \"${pk}\" LIMIT ?`\n selectParams.push(batchSize)\n\n const batchRows = await conn.execute(selectSql, selectParams)\n const batch = Array.isArray(batchRows) ? batchRows : []\n if (!batch.length) break\n\n lastId = String(batch[batch.length - 1]![pk])\n totalRowsFetched += batch.length\n\n const batchStart = Date.now()\n await conn.execute('BEGIN')\n let batchCommitted = false\n try {\n for (const row of batch) {\n const updates: Record<string, unknown> = {}\n let rowDecrypted = false\n\n for (const fieldRule of fields) {\n const resolved = resolveProperty(meta, fieldRule.field)\n const col = resolved?.columnName\n if (!col) continue\n const rawValue = row[col]\n if (rawValue === null || rawValue === undefined) continue\n try {\n const decrypted = decryptWithAesGcmStrict(String(rawValue), dek.key)\n let valueToWrite: string\n try {\n const parsed = JSON.parse(decrypted)\n valueToWrite = typeof parsed === 'string' ? parsed : decrypted\n } catch {\n valueToWrite = decrypted\n }\n updates[col] = valueToWrite\n rowDecrypted = true\n } catch (e: any) {\n if (e instanceof TenantDataEncryptionError) {\n if (e.code === TenantDataEncryptionErrorCode.AUTH_FAILED) {\n // Value is plaintext \u2014 skip silently\n } else if (e.code === TenantDataEncryptionErrorCode.MALFORMED_PAYLOAD) {\n scopeMalformedCount++\n const locationKey = `${tableName}:${col}`\n malformedByLocation.set(locationKey, (malformedByLocation.get(locationKey) ?? 0) + 1)\n console.warn(`\u26A0 MALFORMED_PAYLOAD for ${entityId} field \"${col}\" row ${row[pk]}; skipping field.`)\n } else {\n throw e\n }\n } else {\n throw e\n }\n }\n }\n\n if (rowDecrypted) {\n for (const fieldRule of fields) {\n if (!fieldRule.hashField) continue\n const resolvedHash = resolveProperty(meta, fieldRule.hashField)\n const hashCol = resolvedHash?.columnName\n if (!hashCol) {\n const skippedKey = `${tableName}:${fieldRule.hashField}`\n if (!totalHashFieldsSkipped.has(skippedKey)) {\n console.warn(`\u26A0 Hash column \"${fieldRule.hashField}\" not found in metadata for ${entityId}; skipping.`)\n totalHashFieldsSkipped.add(skippedKey)\n }\n continue\n }\n updates[hashCol] = null\n totalHashFieldsCleared++\n }\n }\n\n if (Object.keys(updates).length > 0) {\n if (!dryRun) {\n const setSql = Object.keys(updates).map((col) => `\"${col}\" = ?`).join(', ')\n await conn.execute(\n `UPDATE ${qualifiedTable} SET ${setSql} WHERE \"${pk}\" = ?`,\n [...Object.values(updates), row[pk]],\n )\n }\n totalRowsUpdated++\n }\n }\n await conn.execute('COMMIT')\n batchCommitted = true\n } catch (fatalErr: any) {\n if (!batchCommitted) {\n try { await conn.execute('ROLLBACK') } catch {}\n }\n console.error(`Fatal error during batch processing for ${entityId}: ${(fatalErr as Error)?.message || String(fatalErr)}`)\n throw fatalErr\n }\n\n const batchDurationMs = Date.now() - batchStart\n if (debug) {\n console.log(\n `[debug] Batch ${entityId} org=${scope.organizationId ?? 'null'}: ${batch.length} rows in ${batchDurationMs}ms, scopeMalformed=${scopeMalformedCount}`,\n )\n if (batchDurationMs > 30_000) {\n console.warn('\u26A0 Batch took >30s; consider reducing --batch-size.')\n }\n }\n if (sleepMs > 0) {\n await new Promise<void>((r) => setTimeout(r, sleepMs))\n }\n }\n\n totalMalformedPayloadCount += scopeMalformedCount\n }\n }\n\n if (deactivateMaps && !dryRun) {\n let deactivateSql = `UPDATE encryption_maps SET is_active = false, deleted_at = now() WHERE tenant_id = ? AND deleted_at IS NULL`\n const deactivateParams: unknown[] = [tenantIdArg]\n if (organizationIdArg) {\n deactivateSql += ` AND (organization_id = ? OR organization_id IS NULL)`\n deactivateParams.push(organizationIdArg)\n }\n if (entityIdArg) {\n deactivateSql += ` AND entity_id = ?`\n deactivateParams.push(entityIdArg)\n }\n await conn.execute(deactivateSql, deactivateParams)\n console.warn('\u26A0 Restart all application replicas \u2014 in-process map caches may still be active.')\n if (isTenantDataEncryptionEnabled()) {\n console.warn('\u26A0 Env TENANT_DATA_ENCRYPTION is still true \u2014 new writes will be re-encrypted until env is updated and replicas restarted.')\n }\n }\n\n if (deactivateMaps && dryRun) {\n console.log(`[dry-run] Would deactivate ${maps.length} EncryptionMap record(s).`)\n }\n\n const prefix = dryRun ? '[dry-run] ' : ''\n console.log(`\\n${prefix}Decryption summary:`)\n console.log(` Rows fetched: ${totalRowsFetched}`)\n console.log(` Rows updated: ${totalRowsUpdated}`)\n console.log(` Entities processed: ${totalEntitiesProcessed}`)\n console.log(` Hash fields cleared: ${totalHashFieldsCleared}`)\n if (totalHashFieldsSkipped.size > 0) {\n console.log(` Hash fields skipped (missing columns): ${Array.from(totalHashFieldsSkipped).join(', ')}`)\n }\n if (totalMalformedPayloadCount > 0) {\n console.warn(\n ` \u26A0 ${totalMalformedPayloadCount} field value(s) returned MALFORMED_PAYLOAD and were skipped; these may be corrupted ciphertexts. Investigate before assuming decryption is complete.`,\n )\n if (debug && malformedByLocation.size > 0) {\n const top = Array.from(malformedByLocation.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 10)\n console.log(' Top malformed locations:')\n for (const [loc, count] of top) {\n console.log(` ${loc}: ${count}`)\n }\n }\n }\n\n if (!dryRun) {\n console.log(`\\n\u2705 Decryption complete. Required next steps:`)\n console.log(` 1. Set TENANT_DATA_ENCRYPTION=false in your environment / secrets`)\n console.log(` 2. Restart all application replicas`)\n console.log(` 3. Run: mercato query_index reindex --tenant ${tenantIdArg} \u2190 run after env flip + restart; search/filter degraded until this completes`)\n console.log(` 4. Run: mercato entities decrypt-database --tenant ${tenantIdArg} --check to confirm no encrypted values remain`)\n console.log(` NOTE: if the run was long and concurrent inserts occurred, run again before step 4 \u2014 it is idempotent.`)\n }\n },\n}\n\n// Keep default export stable (install first for help listing)\nexport default [seedDefs, reinstallDefs, addField, seedEncryptionMaps, rotateEncryptionKey, decryptDatabase]\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-mercato/core",
|
|
3
|
-
"version": "0.6.6-develop.
|
|
3
|
+
"version": "0.6.6-develop.6540.1.ae856df1ec",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -253,16 +253,16 @@
|
|
|
253
253
|
"zod": "^4.4.3"
|
|
254
254
|
},
|
|
255
255
|
"peerDependencies": {
|
|
256
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
257
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
258
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
256
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.6540.1.ae856df1ec",
|
|
257
|
+
"@open-mercato/shared": "0.6.6-develop.6540.1.ae856df1ec",
|
|
258
|
+
"@open-mercato/ui": "0.6.6-develop.6540.1.ae856df1ec",
|
|
259
259
|
"react": "^19.0.0",
|
|
260
260
|
"react-dom": "^19.0.0"
|
|
261
261
|
},
|
|
262
262
|
"devDependencies": {
|
|
263
|
-
"@open-mercato/ai-assistant": "0.6.6-develop.
|
|
264
|
-
"@open-mercato/shared": "0.6.6-develop.
|
|
265
|
-
"@open-mercato/ui": "0.6.6-develop.
|
|
263
|
+
"@open-mercato/ai-assistant": "0.6.6-develop.6540.1.ae856df1ec",
|
|
264
|
+
"@open-mercato/shared": "0.6.6-develop.6540.1.ae856df1ec",
|
|
265
|
+
"@open-mercato/ui": "0.6.6-develop.6540.1.ae856df1ec",
|
|
266
266
|
"@testing-library/dom": "^10.4.1",
|
|
267
267
|
"@testing-library/jest-dom": "^6.9.1",
|
|
268
268
|
"@testing-library/react": "^16.3.1",
|
|
@@ -3,6 +3,7 @@ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
|
3
3
|
import { createApiKey } from './services/apiKeyService'
|
|
4
4
|
import { Organization } from '@open-mercato/core/modules/directory/data/entities'
|
|
5
5
|
import { Role } from '@open-mercato/core/modules/auth/data/entities'
|
|
6
|
+
import { parseCommaSeparatedList } from '@open-mercato/shared/lib/string'
|
|
6
7
|
|
|
7
8
|
function parseArgs(rest: string[]): Record<string, string> {
|
|
8
9
|
const args: Record<string, string> = {}
|
|
@@ -47,9 +48,7 @@ const addApiKey: ModuleCli = {
|
|
|
47
48
|
return
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
const roleInputs = rolesCsv
|
|
51
|
-
? rolesCsv.split(',').map((value) => value.trim()).filter(Boolean)
|
|
52
|
-
: []
|
|
51
|
+
const roleInputs = parseCommaSeparatedList(rolesCsv)
|
|
53
52
|
const roleIds: string[] = []
|
|
54
53
|
if (roleInputs.length) {
|
|
55
54
|
for (const token of roleInputs) {
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
canRenderInlineAttachment,
|
|
16
16
|
} from "@open-mercato/core/modules/attachments/lib/security";
|
|
17
17
|
import { StorageDriverFactory } from '../../../lib/drivers';
|
|
18
|
+
import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope';
|
|
18
19
|
|
|
19
20
|
export const metadata = {
|
|
20
21
|
GET: { requireAuth: false },
|
|
@@ -32,16 +33,19 @@ export async function GET(
|
|
|
32
33
|
);
|
|
33
34
|
}
|
|
34
35
|
const auth = await getAuthFromRequest(req);
|
|
35
|
-
const
|
|
36
|
-
const em = resolve("em") as EntityManager;
|
|
36
|
+
const container = await createRequestContainer();
|
|
37
|
+
const em = container.resolve("em") as EntityManager;
|
|
37
38
|
const storageDriverFactory =
|
|
38
|
-
(resolve("storageDriverFactory") as StorageDriverFactory | null) ??
|
|
39
|
+
(container.resolve("storageDriverFactory") as StorageDriverFactory | null) ??
|
|
39
40
|
new StorageDriverFactory(em);
|
|
40
41
|
|
|
42
|
+
const scopedAuth = auth
|
|
43
|
+
? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
|
|
44
|
+
: auth;
|
|
41
45
|
const findFilter: Record<string, unknown> = { id };
|
|
42
|
-
if (
|
|
43
|
-
if (
|
|
44
|
-
if (
|
|
46
|
+
if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
|
|
47
|
+
if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId;
|
|
48
|
+
if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId;
|
|
45
49
|
}
|
|
46
50
|
const attachment = await em.findOne(Attachment, findFilter);
|
|
47
51
|
if (!attachment) {
|
|
@@ -60,7 +64,7 @@ export async function GET(
|
|
|
60
64
|
);
|
|
61
65
|
}
|
|
62
66
|
|
|
63
|
-
const access = checkAttachmentAccess(
|
|
67
|
+
const access = checkAttachmentAccess(scopedAuth, attachment, partition);
|
|
64
68
|
if (!access.ok) {
|
|
65
69
|
const message = access.status === 401 ? "Unauthorized" : "Forbidden";
|
|
66
70
|
return NextResponse.json({ error: message }, { status: access.status });
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
validateImageMagicBytes,
|
|
21
21
|
} from '@open-mercato/core/modules/attachments/lib/imageSafety'
|
|
22
22
|
import { StorageDriverFactory } from '../../../../lib/drivers'
|
|
23
|
+
import { resolveAttachmentOrganizationId } from '@open-mercato/core/modules/attachments/lib/requestScope'
|
|
23
24
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
24
25
|
|
|
25
26
|
const logger = createLogger('attachments').child({ component: 'image' })
|
|
@@ -51,15 +52,18 @@ export async function GET(
|
|
|
51
52
|
}
|
|
52
53
|
const { width, height, cropType } = parsedQuery.data
|
|
53
54
|
|
|
54
|
-
const
|
|
55
|
-
const em = resolve('em') as EntityManager
|
|
55
|
+
const container = await createRequestContainer()
|
|
56
|
+
const em = container.resolve('em') as EntityManager
|
|
56
57
|
const storageDriverFactory =
|
|
57
|
-
(resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
58
|
+
(container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
58
59
|
|
|
60
|
+
const scopedAuth = auth
|
|
61
|
+
? { ...auth, orgId: await resolveAttachmentOrganizationId(container, auth, req) }
|
|
62
|
+
: auth
|
|
59
63
|
const findFilter: Record<string, unknown> = { id }
|
|
60
|
-
if (
|
|
61
|
-
if (
|
|
62
|
-
if (
|
|
64
|
+
if (scopedAuth && !isSuperAdminAuth(scopedAuth)) {
|
|
65
|
+
if (scopedAuth.tenantId) findFilter.tenantId = scopedAuth.tenantId
|
|
66
|
+
if (scopedAuth.orgId) findFilter.organizationId = scopedAuth.orgId
|
|
63
67
|
}
|
|
64
68
|
const attachment = await em.findOne(Attachment, findFilter)
|
|
65
69
|
if (!attachment) {
|
|
@@ -72,7 +76,7 @@ export async function GET(
|
|
|
72
76
|
if (!partition) {
|
|
73
77
|
return NextResponse.json({ error: 'Partition misconfigured' }, { status: 500 })
|
|
74
78
|
}
|
|
75
|
-
const access = checkAttachmentAccess(
|
|
79
|
+
const access = checkAttachmentAccess(scopedAuth, attachment, partition)
|
|
76
80
|
if (!access.ok) {
|
|
77
81
|
const message = access.status === 401 ? 'Unauthorized' : 'Forbidden'
|
|
78
82
|
return NextResponse.json({ error: message }, { status: access.status })
|
|
@@ -13,6 +13,7 @@ import { StorageDriverFactory } from '../lib/drivers'
|
|
|
13
13
|
import { OcrService, shouldUseLlmOcr } from '../lib/ocrService'
|
|
14
14
|
import { clearAttachmentThumbnailCache } from '../lib/thumbnailCache'
|
|
15
15
|
import { assertAttachmentScopeInvariant } from '../lib/access'
|
|
16
|
+
import { resolveAttachmentOrganizationId } from '../lib/requestScope'
|
|
16
17
|
import {
|
|
17
18
|
mergeAttachmentMetadata,
|
|
18
19
|
normalizeAttachmentAssignments,
|
|
@@ -198,10 +199,11 @@ export async function GET(req: Request) {
|
|
|
198
199
|
}
|
|
199
200
|
const { entityId, recordId, page, pageSize } = parsedQuery.data
|
|
200
201
|
|
|
201
|
-
const
|
|
202
|
-
const em = resolve('em') as EntityManager
|
|
202
|
+
const container = await createRequestContainer()
|
|
203
|
+
const em = container.resolve('em') as EntityManager
|
|
204
|
+
const orgId = await resolveAttachmentOrganizationId(container, auth, req)
|
|
203
205
|
const filter: Record<string, unknown> = { entityId, recordId, tenantId: auth.tenantId! }
|
|
204
|
-
if (
|
|
206
|
+
if (orgId) filter.organizationId = orgId
|
|
205
207
|
const orderBy: Record<string, 'ASC' | 'DESC'> = { createdAt: 'DESC' }
|
|
206
208
|
const usePaging = typeof page === 'number' && typeof pageSize === 'number'
|
|
207
209
|
const total = usePaging ? await em.count(Attachment, filter) : null
|
|
@@ -224,7 +226,7 @@ export async function GET(req: Request) {
|
|
|
224
226
|
},
|
|
225
227
|
{
|
|
226
228
|
tenantId: auth.tenantId ?? null,
|
|
227
|
-
organizationId:
|
|
229
|
+
organizationId: orgId ?? null,
|
|
228
230
|
},
|
|
229
231
|
)
|
|
230
232
|
return NextResponse.json({
|
|
@@ -275,7 +277,6 @@ export async function POST(req: Request) {
|
|
|
275
277
|
}, { status: 400 })
|
|
276
278
|
}
|
|
277
279
|
const tenantId = auth.tenantId
|
|
278
|
-
const orgId = auth.orgId
|
|
279
280
|
|
|
280
281
|
const contentType = req.headers.get('content-type') || ''
|
|
281
282
|
if (!contentType.toLowerCase().includes('multipart/form-data')) {
|
|
@@ -301,11 +302,13 @@ export async function POST(req: Request) {
|
|
|
301
302
|
const tags = parseFormTags(form.get('tags'))
|
|
302
303
|
const assignmentsFromForm = parseFormAssignments(form.get('assignments'))
|
|
303
304
|
|
|
304
|
-
const
|
|
305
|
-
const em = resolve('em') as EntityManager
|
|
306
|
-
const dataEngine = resolve('dataEngine')
|
|
305
|
+
const container = await createRequestContainer()
|
|
306
|
+
const em = container.resolve('em') as EntityManager
|
|
307
|
+
const dataEngine = container.resolve('dataEngine')
|
|
307
308
|
const storageDriverFactory =
|
|
308
|
-
(resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
309
|
+
(container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
310
|
+
const orgId = await resolveAttachmentOrganizationId(container, auth, req)
|
|
311
|
+
if (!orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
|
309
312
|
await ensureDefaultPartitions(em)
|
|
310
313
|
// Optional per-field validations
|
|
311
314
|
let partitionFromField: string | null = null
|
|
@@ -436,12 +439,12 @@ export async function POST(req: Request) {
|
|
|
436
439
|
}
|
|
437
440
|
const metadata = mergeAttachmentMetadata(null, { assignments, tags })
|
|
438
441
|
const attachmentId = randomUUID()
|
|
439
|
-
assertAttachmentScopeInvariant({ tenantId: auth.tenantId, organizationId:
|
|
442
|
+
assertAttachmentScopeInvariant({ tenantId: auth.tenantId, organizationId: orgId })
|
|
440
443
|
const att = em.create(Attachment, {
|
|
441
444
|
id: attachmentId,
|
|
442
445
|
entityId,
|
|
443
446
|
recordId,
|
|
444
|
-
organizationId:
|
|
447
|
+
organizationId: orgId,
|
|
445
448
|
tenantId: auth.tenantId!,
|
|
446
449
|
fileName: safeName,
|
|
447
450
|
mimeType: fileMimeType,
|
|
@@ -545,17 +548,19 @@ export async function DELETE(req: Request) {
|
|
|
545
548
|
const url = new URL(req.url)
|
|
546
549
|
const id = url.searchParams.get('id') || ''
|
|
547
550
|
if (!id) return NextResponse.json({ error: 'Attachment id is required' }, { status: 400 })
|
|
548
|
-
const
|
|
549
|
-
const em = resolve('em') as EntityManager
|
|
550
|
-
const dataEngine = resolve('dataEngine')
|
|
551
|
+
const container = await createRequestContainer()
|
|
552
|
+
const em = container.resolve('em') as EntityManager
|
|
553
|
+
const dataEngine = container.resolve('dataEngine')
|
|
551
554
|
const storageDriverFactory =
|
|
552
|
-
(resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
553
|
-
//
|
|
554
|
-
//
|
|
555
|
-
//
|
|
556
|
-
//
|
|
555
|
+
(container.resolve('storageDriverFactory') as StorageDriverFactory | null) ?? new StorageDriverFactory(em)
|
|
556
|
+
// Resolve the currently selected organization (#3765) so a multi-org admin who
|
|
557
|
+
// switched the header org deletes within that org, not their pinned home org.
|
|
558
|
+
// A superadmin browsing with "All organizations" selected has no concrete org,
|
|
559
|
+
// so resolution returns null and the delete falls back to a tenant-only scope
|
|
560
|
+
// (#3764) — letting them remove any attachment in the tenant.
|
|
561
|
+
const orgId = await resolveAttachmentOrganizationId(container, auth, req)
|
|
557
562
|
const deleteFilter: Record<string, unknown> = { id, tenantId: auth.tenantId! }
|
|
558
|
-
if (
|
|
563
|
+
if (orgId) deleteFilter.organizationId = orgId
|
|
559
564
|
const record = await em.findOne(Attachment, deleteFilter)
|
|
560
565
|
if (!record) return NextResponse.json({ error: 'Attachment not found' }, { status: 404 })
|
|
561
566
|
await em.remove(record).flush()
|
|
@@ -565,7 +570,7 @@ export async function DELETE(req: Request) {
|
|
|
565
570
|
if (record.storagePath) {
|
|
566
571
|
const delDriver = await storageDriverFactory.resolveForPartition(record.partitionCode, {
|
|
567
572
|
tenantId: record.tenantId ?? auth.tenantId!,
|
|
568
|
-
organizationId: record.organizationId ??
|
|
573
|
+
organizationId: record.organizationId ?? orgId ?? '',
|
|
569
574
|
})
|
|
570
575
|
await delDriver.delete(record.partitionCode, record.storagePath)
|
|
571
576
|
}
|