@open-mercato/shared 0.5.1-develop.2683.4878a05b8e → 0.5.1-develop.2694.732417c5ec
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 +2 -2
- package/build.mjs +13 -102
- package/dist/lib/api/crud.js +1 -1
- package/dist/lib/api/crud.js.map +2 -2
- package/dist/lib/auth/server.js +1 -1
- package/dist/lib/auth/server.js.map +2 -2
- package/dist/lib/data/engine.js +68 -27
- package/dist/lib/data/engine.js.map +2 -2
- package/dist/lib/db/mikro.js +18 -22
- package/dist/lib/db/mikro.js.map +2 -2
- package/dist/lib/indexers/error-log.js +10 -12
- package/dist/lib/indexers/error-log.js.map +2 -2
- package/dist/lib/indexers/status-log.js +14 -16
- package/dist/lib/indexers/status-log.js.map +2 -2
- package/dist/lib/query/engine.js +220 -228
- package/dist/lib/query/engine.js.map +3 -3
- package/dist/lib/query/join-utils.js +28 -23
- package/dist/lib/query/join-utils.js.map +2 -2
- package/dist/lib/version.js +1 -1
- package/dist/lib/version.js.map +1 -1
- package/jest.config.cjs +4 -2
- package/package.json +1 -1
- package/src/lib/api/__tests__/crud.test.ts +5 -3
- package/src/lib/api/crud.ts +1 -1
- package/src/lib/auth/__tests__/server.apiKeyCache.test.ts +10 -4
- package/src/lib/auth/server.ts +1 -1
- package/src/lib/bootstrap/types.ts +2 -2
- package/src/lib/crud/__tests__/crud-factory.test.ts +27 -17
- package/src/lib/data/engine.ts +95 -47
- package/src/lib/db/mikro.ts +26 -25
- package/src/lib/indexers/error-log.ts +23 -23
- package/src/lib/indexers/status-log.ts +36 -33
- package/src/lib/query/__tests__/engine.scope-and-or.test.ts +253 -114
- package/src/lib/query/__tests__/engine.test.ts +206 -139
- package/src/lib/query/engine.ts +306 -263
- package/src/lib/query/join-utils.ts +38 -30
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
shared built successfully
|
|
1
|
+
[build:shared] found 201 entry points
|
|
2
|
+
[build:shared] built successfully
|
package/build.mjs
CHANGED
|
@@ -1,116 +1,27 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { glob } from 'glob'
|
|
3
|
-
import { readFileSync, writeFileSync, existsSync } from 'node:fs'
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
4
2
|
import { dirname, join } from 'node:path'
|
|
5
3
|
import { fileURLToPath } from 'node:url'
|
|
4
|
+
import { buildPackage } from '../../scripts/build-package.mjs'
|
|
6
5
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
// Read the package version at build time for injection
|
|
10
|
-
const packageJson = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf-8'))
|
|
6
|
+
const packageDir = dirname(fileURLToPath(import.meta.url))
|
|
7
|
+
const packageJson = JSON.parse(readFileSync(join(packageDir, 'package.json'), 'utf-8'))
|
|
11
8
|
const packageVersion = packageJson.version
|
|
12
9
|
|
|
13
|
-
//
|
|
10
|
+
// Inject build-time version into lib/version.ts without touching the source file.
|
|
14
11
|
const injectVersion = {
|
|
15
12
|
name: 'inject-version',
|
|
16
13
|
setup(build) {
|
|
17
|
-
build.onLoad({ filter: /lib\/version\.ts$/ }, async () => {
|
|
18
|
-
|
|
19
|
-
contents: `// Build-time generated version
|
|
14
|
+
build.onLoad({ filter: /lib\/version\.ts$/ }, async () => ({
|
|
15
|
+
contents: `// Build-time generated version
|
|
20
16
|
export const APP_VERSION = '${packageVersion}'
|
|
21
17
|
export const appVersion = APP_VERSION
|
|
22
18
|
`,
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
19
|
+
loader: 'ts',
|
|
20
|
+
}))
|
|
21
|
+
},
|
|
27
22
|
}
|
|
28
23
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
absolute: true,
|
|
24
|
+
await buildPackage(packageDir, {
|
|
25
|
+
name: 'shared',
|
|
26
|
+
extraPlugins: [injectVersion],
|
|
33
27
|
})
|
|
34
|
-
|
|
35
|
-
if (entryPoints.length === 0) {
|
|
36
|
-
console.error('No entry points found!')
|
|
37
|
-
process.exit(1)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
console.log(`Found ${entryPoints.length} entry points`)
|
|
41
|
-
|
|
42
|
-
// Plugin to add .js extension to relative imports
|
|
43
|
-
const addJsExtension = {
|
|
44
|
-
name: 'add-js-extension',
|
|
45
|
-
setup(build) {
|
|
46
|
-
build.onEnd(async (result) => {
|
|
47
|
-
if (result.errors.length > 0) return
|
|
48
|
-
const outputFiles = await glob('dist/**/*.js', { cwd: __dirname, absolute: true })
|
|
49
|
-
for (const file of outputFiles) {
|
|
50
|
-
const fileDir = dirname(file)
|
|
51
|
-
let content = readFileSync(file, 'utf-8')
|
|
52
|
-
// Add .js to relative imports that don't have an extension
|
|
53
|
-
content = content.replace(
|
|
54
|
-
/from\s+["'](\.[^"']+)["']/g,
|
|
55
|
-
(match, path) => {
|
|
56
|
-
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
57
|
-
// Check if it's a directory with index.js
|
|
58
|
-
const resolvedPath = join(fileDir, path)
|
|
59
|
-
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
60
|
-
return `from "${path}/index.js"`
|
|
61
|
-
}
|
|
62
|
-
return `from "${path}.js"`
|
|
63
|
-
}
|
|
64
|
-
)
|
|
65
|
-
content = content.replace(
|
|
66
|
-
/import\s*\(\s*["'](\.[^"']+)["']\s*\)/g,
|
|
67
|
-
(match, path) => {
|
|
68
|
-
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
69
|
-
// Check if it's a directory with index.js
|
|
70
|
-
const resolvedPath = join(fileDir, path)
|
|
71
|
-
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
72
|
-
return `import("${path}/index.js")`
|
|
73
|
-
}
|
|
74
|
-
return `import("${path}.js")`
|
|
75
|
-
}
|
|
76
|
-
)
|
|
77
|
-
// Handle side-effect imports: import "./path" (no from clause)
|
|
78
|
-
content = content.replace(
|
|
79
|
-
/import\s+["'](\.[^"']+)["'];/g,
|
|
80
|
-
(match, path) => {
|
|
81
|
-
if (path.endsWith('.js') || path.endsWith('.json')) return match
|
|
82
|
-
// Check if it's a directory with index.js
|
|
83
|
-
const resolvedPath = join(fileDir, path)
|
|
84
|
-
if (existsSync(resolvedPath) && existsSync(join(resolvedPath, 'index.js'))) {
|
|
85
|
-
return `import "${path}/index.js";`
|
|
86
|
-
}
|
|
87
|
-
return `import "${path}.js";`
|
|
88
|
-
}
|
|
89
|
-
)
|
|
90
|
-
writeFileSync(file, content)
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const outdir = join(__dirname, 'dist')
|
|
97
|
-
|
|
98
|
-
const result = await esbuild.build({
|
|
99
|
-
entryPoints,
|
|
100
|
-
outdir,
|
|
101
|
-
outbase: join(__dirname, 'src'),
|
|
102
|
-
format: 'esm',
|
|
103
|
-
platform: 'node',
|
|
104
|
-
target: 'node18',
|
|
105
|
-
sourcemap: true,
|
|
106
|
-
jsx: 'automatic',
|
|
107
|
-
write: true,
|
|
108
|
-
plugins: [injectVersion, addJsExtension],
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
if (result.errors.length > 0) {
|
|
112
|
-
console.error('Build errors:', result.errors)
|
|
113
|
-
process.exit(1)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
console.log('shared built successfully')
|
package/dist/lib/api/crud.js
CHANGED
|
@@ -36,7 +36,7 @@ async function findOneScoped(em, entity, id, scope) {
|
|
|
36
36
|
async function softDelete(em, entity) {
|
|
37
37
|
;
|
|
38
38
|
entity.deletedAt = /* @__PURE__ */ new Date();
|
|
39
|
-
await em.
|
|
39
|
+
await em.persist(entity).flush();
|
|
40
40
|
}
|
|
41
41
|
export {
|
|
42
42
|
buildScopedWhere,
|
package/dist/lib/api/crud.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/api/crud.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\n\ntype Scope = { organizationId?: string | null; organizationIds?: string[] | null; tenantId?: string | null }\n\nexport function buildScopedWhere(\n base: Record<string, any>,\n scope: Scope & { orgField?: string | null; tenantField?: string | null; softDeleteField?: string | null }\n): Record<string, any> {\n const where: any = { ...base }\n const orgField = scope.orgField === null ? null : (scope.orgField as string) || 'organizationId'\n const tenantField = scope.tenantField === null ? null : (scope.tenantField as string) || 'tenantId'\n const softField = scope.softDeleteField === null ? null : (scope.softDeleteField as string) || 'deletedAt'\n\n if (orgField) {\n if (scope.organizationIds !== undefined) {\n const ids = (scope.organizationIds ?? [])\n .map((id) => (typeof id === 'string' ? id.trim() : id))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (ids.length === 0) {\n where[orgField] = { $in: [] }\n } else if (ids.length === 1) {\n where[orgField] = ids[0]\n } else {\n where[orgField] = { $in: ids }\n }\n } else if (scope.organizationId !== undefined) {\n where[orgField] = scope.organizationId\n }\n }\n\n if (tenantField && scope.tenantId !== undefined) where[tenantField] = scope.tenantId\n if (softField) where[softField] = null\n return where\n}\n\nexport function extractScopeFromAuth(auth: { orgId?: string | null; tenantId?: string | null } | null | undefined): { organizationId?: string | null; tenantId?: string | null } {\n if (!auth) return {}\n return { organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n}\n\nexport async function findOneScoped<T extends { id: string }>(\n em: EntityManager,\n entity: { new (): T },\n id: string,\n scope: Scope & { orgField?: keyof T; tenantField?: keyof T }\n): Promise<T | null> {\n const orgField = (scope.orgField as string) || 'organizationId'\n const tenantField = (scope.tenantField as string) || 'tenantId'\n const where: any = { id }\n if (scope.organizationId != null) where[orgField] = scope.organizationId\n if (scope.tenantId != null) where[tenantField] = scope.tenantId\n return em.getRepository(entity).findOne(where as any)\n}\n\nexport async function softDelete<T extends { deletedAt?: Date | null }>(\n em: EntityManager,\n entity: T\n): Promise<void> {\n ;(entity as any).deletedAt = new Date()\n await em.
|
|
5
|
-
"mappings": "AAIO,SAAS,iBACd,MACA,OACqB;AACrB,QAAM,QAAa,EAAE,GAAG,KAAK;AAC7B,QAAM,WAAW,MAAM,aAAa,OAAO,OAAQ,MAAM,YAAuB;AAChF,QAAM,cAAc,MAAM,gBAAgB,OAAO,OAAQ,MAAM,eAA0B;AACzF,QAAM,YAAY,MAAM,oBAAoB,OAAO,OAAQ,MAAM,mBAA8B;AAE/F,MAAI,UAAU;AACZ,QAAI,MAAM,oBAAoB,QAAW;AACvC,YAAM,OAAO,MAAM,mBAAmB,CAAC,GACpC,IAAI,CAAC,OAAQ,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI,EAAG,EACrD,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACvE,UAAI,IAAI,WAAW,GAAG;AACpB,cAAM,QAAQ,IAAI,EAAE,KAAK,CAAC,EAAE;AAAA,MAC9B,WAAW,IAAI,WAAW,GAAG;AAC3B,cAAM,QAAQ,IAAI,IAAI,CAAC;AAAA,MACzB,OAAO;AACL,cAAM,QAAQ,IAAI,EAAE,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF,WAAW,MAAM,mBAAmB,QAAW;AAC7C,YAAM,QAAQ,IAAI,MAAM;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,eAAe,MAAM,aAAa,OAAW,OAAM,WAAW,IAAI,MAAM;AAC5E,MAAI,UAAW,OAAM,SAAS,IAAI;AAClC,SAAO;AACT;AAEO,SAAS,qBAAqB,MAA4I;AAC/K,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,SAAO,EAAE,gBAAgB,KAAK,SAAS,MAAM,UAAU,KAAK,YAAY,KAAK;AAC/E;AAEA,eAAsB,cACpB,IACA,QACA,IACA,OACmB;AACnB,QAAM,WAAY,MAAM,YAAuB;AAC/C,QAAM,cAAe,MAAM,eAA0B;AACrD,QAAM,QAAa,EAAE,GAAG;AACxB,MAAI,MAAM,kBAAkB,KAAM,OAAM,QAAQ,IAAI,MAAM;AAC1D,MAAI,MAAM,YAAY,KAAM,OAAM,WAAW,IAAI,MAAM;AACvD,SAAO,GAAG,cAAc,MAAM,EAAE,QAAQ,KAAY;AACtD;AAEA,eAAsB,WACpB,IACA,QACe;AACf;AAAC,EAAC,OAAe,YAAY,oBAAI,KAAK;AACtC,QAAM,GAAG,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/core'\n\ntype Scope = { organizationId?: string | null; organizationIds?: string[] | null; tenantId?: string | null }\n\nexport function buildScopedWhere(\n base: Record<string, any>,\n scope: Scope & { orgField?: string | null; tenantField?: string | null; softDeleteField?: string | null }\n): Record<string, any> {\n const where: any = { ...base }\n const orgField = scope.orgField === null ? null : (scope.orgField as string) || 'organizationId'\n const tenantField = scope.tenantField === null ? null : (scope.tenantField as string) || 'tenantId'\n const softField = scope.softDeleteField === null ? null : (scope.softDeleteField as string) || 'deletedAt'\n\n if (orgField) {\n if (scope.organizationIds !== undefined) {\n const ids = (scope.organizationIds ?? [])\n .map((id) => (typeof id === 'string' ? id.trim() : id))\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (ids.length === 0) {\n where[orgField] = { $in: [] }\n } else if (ids.length === 1) {\n where[orgField] = ids[0]\n } else {\n where[orgField] = { $in: ids }\n }\n } else if (scope.organizationId !== undefined) {\n where[orgField] = scope.organizationId\n }\n }\n\n if (tenantField && scope.tenantId !== undefined) where[tenantField] = scope.tenantId\n if (softField) where[softField] = null\n return where\n}\n\nexport function extractScopeFromAuth(auth: { orgId?: string | null; tenantId?: string | null } | null | undefined): { organizationId?: string | null; tenantId?: string | null } {\n if (!auth) return {}\n return { organizationId: auth.orgId ?? null, tenantId: auth.tenantId ?? null }\n}\n\nexport async function findOneScoped<T extends { id: string }>(\n em: EntityManager,\n entity: { new (): T },\n id: string,\n scope: Scope & { orgField?: keyof T; tenantField?: keyof T }\n): Promise<T | null> {\n const orgField = (scope.orgField as string) || 'organizationId'\n const tenantField = (scope.tenantField as string) || 'tenantId'\n const where: any = { id }\n if (scope.organizationId != null) where[orgField] = scope.organizationId\n if (scope.tenantId != null) where[tenantField] = scope.tenantId\n return em.getRepository(entity).findOne(where as any)\n}\n\nexport async function softDelete<T extends { deletedAt?: Date | null }>(\n em: EntityManager,\n entity: T\n): Promise<void> {\n ;(entity as any).deletedAt = new Date()\n await em.persist(entity).flush()\n}\n"],
|
|
5
|
+
"mappings": "AAIO,SAAS,iBACd,MACA,OACqB;AACrB,QAAM,QAAa,EAAE,GAAG,KAAK;AAC7B,QAAM,WAAW,MAAM,aAAa,OAAO,OAAQ,MAAM,YAAuB;AAChF,QAAM,cAAc,MAAM,gBAAgB,OAAO,OAAQ,MAAM,eAA0B;AACzF,QAAM,YAAY,MAAM,oBAAoB,OAAO,OAAQ,MAAM,mBAA8B;AAE/F,MAAI,UAAU;AACZ,QAAI,MAAM,oBAAoB,QAAW;AACvC,YAAM,OAAO,MAAM,mBAAmB,CAAC,GACpC,IAAI,CAAC,OAAQ,OAAO,OAAO,WAAW,GAAG,KAAK,IAAI,EAAG,EACrD,OAAO,CAAC,OAAqB,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACvE,UAAI,IAAI,WAAW,GAAG;AACpB,cAAM,QAAQ,IAAI,EAAE,KAAK,CAAC,EAAE;AAAA,MAC9B,WAAW,IAAI,WAAW,GAAG;AAC3B,cAAM,QAAQ,IAAI,IAAI,CAAC;AAAA,MACzB,OAAO;AACL,cAAM,QAAQ,IAAI,EAAE,KAAK,IAAI;AAAA,MAC/B;AAAA,IACF,WAAW,MAAM,mBAAmB,QAAW;AAC7C,YAAM,QAAQ,IAAI,MAAM;AAAA,IAC1B;AAAA,EACF;AAEA,MAAI,eAAe,MAAM,aAAa,OAAW,OAAM,WAAW,IAAI,MAAM;AAC5E,MAAI,UAAW,OAAM,SAAS,IAAI;AAClC,SAAO;AACT;AAEO,SAAS,qBAAqB,MAA4I;AAC/K,MAAI,CAAC,KAAM,QAAO,CAAC;AACnB,SAAO,EAAE,gBAAgB,KAAK,SAAS,MAAM,UAAU,KAAK,YAAY,KAAK;AAC/E;AAEA,eAAsB,cACpB,IACA,QACA,IACA,OACmB;AACnB,QAAM,WAAY,MAAM,YAAuB;AAC/C,QAAM,cAAe,MAAM,eAA0B;AACrD,QAAM,QAAa,EAAE,GAAG;AACxB,MAAI,MAAM,kBAAkB,KAAM,OAAM,QAAQ,IAAI,MAAM;AAC1D,MAAI,MAAM,YAAY,KAAM,OAAM,WAAW,IAAI,MAAM;AACvD,SAAO,GAAG,cAAc,MAAM,EAAE,QAAQ,KAAY;AACtD;AAEA,eAAsB,WACpB,IACA,QACe;AACf;AAAC,EAAC,OAAe,YAAY,oBAAI,KAAK;AACtC,QAAM,GAAG,QAAQ,MAAM,EAAE,MAAM;AACjC;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/auth/server.js
CHANGED
|
@@ -102,7 +102,7 @@ async function resolveApiKeyAuth(secret) {
|
|
|
102
102
|
if (cache.shouldWriteLastUsed(record.id)) {
|
|
103
103
|
try {
|
|
104
104
|
record.lastUsedAt = /* @__PURE__ */ new Date();
|
|
105
|
-
await em.
|
|
105
|
+
await em.persist(record).flush();
|
|
106
106
|
} catch {
|
|
107
107
|
}
|
|
108
108
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/lib/auth/server.ts"],
|
|
4
|
-
"sourcesContent": ["import { cookies } from 'next/headers.js'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { verifyJwt } from './jwt'\nimport { getSharedApiKeyAuthCache } from './apiKeyAuthCache'\n\nconst TENANT_COOKIE_NAME = 'om_selected_tenant'\nconst ORGANIZATION_COOKIE_NAME = 'om_selected_org'\nconst ALL_ORGANIZATIONS_COOKIE_VALUE = '__all__'\nconst SUPERADMIN_ROLE = 'superadmin'\n\nexport type AuthContext = {\n sub: string\n sid?: string | null\n tenantId: string | null\n orgId: string | null\n email?: string\n roles?: string[]\n isApiKey?: boolean\n userId?: string\n keyId?: string\n keyName?: string\n [k: string]: unknown\n} | null\n\ntype CookieOverride = { applied: boolean; value: string | null }\ntype AuthResolutionStatus = 'authenticated' | 'missing' | 'invalid'\ntype AuthResolution = {\n auth: AuthContext\n status: AuthResolutionStatus\n}\n\nfunction decodeCookieValue(raw: string | undefined): string | null {\n if (raw === undefined) return null\n try {\n const decoded = decodeURIComponent(raw)\n return decoded ?? null\n } catch {\n return raw ?? null\n }\n}\n\nfunction readCookieFromHeader(header: string | null | undefined, name: string): string | undefined {\n if (!header) return undefined\n const parts = header.split(';')\n for (const part of parts) {\n const trimmed = part.trim()\n if (trimmed.startsWith(`${name}=`)) {\n return trimmed.slice(name.length + 1)\n }\n }\n return undefined\n}\n\nfunction resolveTenantOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded) return { applied: true, value: null }\n const trimmed = decoded.trim()\n if (!trimmed) return { applied: true, value: null }\n return { applied: true, value: trimmed }\n}\n\nfunction resolveOrganizationOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded || decoded === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n const trimmed = decoded.trim()\n if (!trimmed || trimmed === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n return { applied: true, value: trimmed }\n}\n\nfunction isSuperAdminAuth(auth: AuthContext | null | undefined): boolean {\n if (!auth) return false\n return (auth as Record<string, unknown>).isSuperAdmin === true\n}\n\nfunction applySuperAdminScope(\n auth: AuthContext,\n tenantCookie: string | undefined,\n orgCookie: string | undefined\n): AuthContext {\n if (!auth || !isSuperAdminAuth(auth)) return auth\n\n const tenantOverride = resolveTenantOverride(tenantCookie)\n const orgOverride = resolveOrganizationOverride(orgCookie)\n if (!tenantOverride.applied && !orgOverride.applied) return auth\n\n type MutableAuthContext = Exclude<AuthContext, null> & {\n actorTenantId?: string | null\n actorOrgId?: string | null\n }\n const baseAuth = auth as Exclude<AuthContext, null>\n const next: MutableAuthContext = { ...baseAuth }\n if (tenantOverride.applied) {\n if (!('actorTenantId' in next)) next.actorTenantId = auth?.tenantId ?? null\n next.tenantId = tenantOverride.value\n }\n if (orgOverride.applied) {\n if (!('actorOrgId' in next)) next.actorOrgId = auth?.orgId ?? null\n next.orgId = orgOverride.value\n }\n next.isSuperAdmin = true\n const existingRoles = Array.isArray(next.roles) ? next.roles : []\n if (!existingRoles.some((role) => typeof role === 'string' && role.trim().toLowerCase() === SUPERADMIN_ROLE)) {\n next.roles = [...existingRoles, 'superadmin']\n }\n return next\n}\n\nasync function resolveApiKeyAuth(secret: string): Promise<AuthContext> {\n if (!secret) return null\n const cache = getSharedApiKeyAuthCache()\n const cached = cache.get(secret)\n if (cached !== undefined) return cached as AuthContext\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')\n const { Role, RoleAcl, User } = await import('@open-mercato/core/modules/auth/data/entities')\n const { Organization, Tenant } = await import('@open-mercato/core/modules/directory/data/entities')\n\n const record = await findApiKeyBySecret(em, secret)\n if (!record) {\n cache.setMiss(secret)\n return null\n }\n\n const roleIds = Array.isArray(record.rolesJson)\n ? record.rolesJson.filter((value): value is string => typeof value === 'string' && value.length > 0)\n : []\n const roles = roleIds.length\n ? await em.find(Role, { id: { $in: roleIds }, deletedAt: null })\n : []\n const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)\n\n let keyIsSuperAdmin = false\n if (roleIds.length) {\n const superAcl = await em.findOne(\n RoleAcl,\n { role: { $in: roleIds } as any, isSuperAdmin: true, deletedAt: null } as any,\n )\n keyIsSuperAdmin = !!(superAcl && (superAcl as { isSuperAdmin?: boolean }).isSuperAdmin)\n }\n\n if (cache.shouldWriteLastUsed(record.id)) {\n try {\n record.lastUsedAt = new Date()\n await em.persistAndFlush(record)\n } catch {\n // best-effort update; ignore write failures\n }\n }\n\n // For session keys, use sessionUserId; for regular keys, use createdBy\n const actualUserId = record.sessionUserId ?? record.createdBy ?? null\n\n if (actualUserId) {\n const user = await em.findOne(User, { id: actualUserId, deletedAt: null })\n if (!user) {\n cache.setMiss(secret)\n return null\n }\n if ((user.tenantId ?? null) !== (record.tenantId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n if ((user.organizationId ?? null) !== (record.organizationId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n } else {\n if (record.tenantId) {\n const tenant = await em.findOne(Tenant, { id: record.tenantId, deletedAt: null, isActive: true })\n if (!tenant) {\n cache.setMiss(secret)\n return null\n }\n }\n if (record.organizationId) {\n const organization = await em.findOne(Organization, { id: record.organizationId, deletedAt: null, isActive: true })\n if (!organization) {\n cache.setMiss(secret)\n return null\n }\n if (record.tenantId && String(organization.tenant.id) !== record.tenantId) {\n cache.setMiss(secret)\n return null\n }\n }\n }\n\n const auth: Exclude<AuthContext, null> = {\n sub: `api_key:${record.id}`,\n tenantId: record.tenantId ?? null,\n orgId: record.organizationId ?? null,\n roles: roleNames,\n isApiKey: true,\n isSuperAdmin: keyIsSuperAdmin,\n keyId: record.id,\n keyName: record.name,\n ...(actualUserId ? { userId: actualUserId } : {}),\n }\n cache.setSuccess(secret, auth, record.expiresAt ? record.expiresAt.getTime() : null)\n return auth\n } catch {\n return null\n }\n}\n\nfunction extractApiKey(req: Request): string | null {\n const header = (req.headers.get('x-api-key') || '').trim()\n if (header) return header\n const authHeader = (req.headers.get('authorization') || '').trim()\n if (authHeader.toLowerCase().startsWith('apikey ')) {\n return authHeader.slice(7).trim()\n }\n return null\n}\n\nasync function resolveCanonicalInteractiveAuthContext(auth: AuthContext): Promise<AuthContext> {\n if (!auth || auth.isApiKey) return auth\n if (typeof auth.sub !== 'string' || auth.sub.trim().length === 0) return null\n\n try {\n const [{ createRequestContainer }, { resolveCanonicalStaffAuthContext }] = await Promise.all([\n import('@open-mercato/shared/lib/di/container'),\n import('@open-mercato/core/modules/auth/lib/sessionIntegrity'),\n ])\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return await resolveCanonicalStaffAuthContext(em, auth)\n } catch {\n return null\n }\n}\n\nexport async function resolveAuthFromCookiesDetailed(): Promise<AuthResolution> {\n const cookieStore = await cookies()\n const token = cookieStore.get('auth_token')?.value\n if (!token) return { auth: null, status: 'missing' }\n try {\n const payload = verifyJwt(token) as AuthContext\n if (!payload) return { auth: null, status: 'invalid' }\n if (payload.type === 'customer') return { auth: null, status: 'invalid' }\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (!canonicalAuth) return { auth: null, status: 'invalid' }\n const tenantCookie = cookieStore.get(TENANT_COOKIE_NAME)?.value\n const orgCookie = cookieStore.get(ORGANIZATION_COOKIE_NAME)?.value\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n } catch {\n return { auth: null, status: 'invalid' }\n }\n}\n\nexport async function getAuthFromCookies(): Promise<AuthContext> {\n return (await resolveAuthFromCookiesDetailed()).auth\n}\n\nexport async function resolveAuthFromRequestDetailed(req: Request): Promise<AuthResolution> {\n const cookieHeader = req.headers.get('cookie') || ''\n const tenantCookie = readCookieFromHeader(cookieHeader, TENANT_COOKIE_NAME)\n const orgCookie = readCookieFromHeader(cookieHeader, ORGANIZATION_COOKIE_NAME)\n const authHeader = (req.headers.get('authorization') || '').trim()\n let token: string | undefined\n let hadInvalidInteractiveToken = false\n if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()\n if (!token) {\n const match = cookieHeader.match(/(?:^|;\\s*)auth_token=([^;]+)/)\n if (match) token = decodeURIComponent(match[1])\n }\n if (token) {\n try {\n const payload = verifyJwt(token) as AuthContext\n if (payload && payload.type === 'customer') return { auth: null, status: 'invalid' }\n if (payload) {\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (canonicalAuth) {\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n }\n hadInvalidInteractiveToken = true\n }\n } catch {\n hadInvalidInteractiveToken = true\n }\n }\n\n const apiKey = extractApiKey(req)\n if (!apiKey) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n const apiAuth = await resolveApiKeyAuth(apiKey)\n if (!apiAuth) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n return {\n auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n}\n\nexport async function getAuthFromRequest(req: Request): Promise<AuthContext> {\n return (await resolveAuthFromRequestDetailed(req)).auth\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { cookies } from 'next/headers.js'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { verifyJwt } from './jwt'\nimport { getSharedApiKeyAuthCache } from './apiKeyAuthCache'\n\nconst TENANT_COOKIE_NAME = 'om_selected_tenant'\nconst ORGANIZATION_COOKIE_NAME = 'om_selected_org'\nconst ALL_ORGANIZATIONS_COOKIE_VALUE = '__all__'\nconst SUPERADMIN_ROLE = 'superadmin'\n\nexport type AuthContext = {\n sub: string\n sid?: string | null\n tenantId: string | null\n orgId: string | null\n email?: string\n roles?: string[]\n isApiKey?: boolean\n userId?: string\n keyId?: string\n keyName?: string\n [k: string]: unknown\n} | null\n\ntype CookieOverride = { applied: boolean; value: string | null }\ntype AuthResolutionStatus = 'authenticated' | 'missing' | 'invalid'\ntype AuthResolution = {\n auth: AuthContext\n status: AuthResolutionStatus\n}\n\nfunction decodeCookieValue(raw: string | undefined): string | null {\n if (raw === undefined) return null\n try {\n const decoded = decodeURIComponent(raw)\n return decoded ?? null\n } catch {\n return raw ?? null\n }\n}\n\nfunction readCookieFromHeader(header: string | null | undefined, name: string): string | undefined {\n if (!header) return undefined\n const parts = header.split(';')\n for (const part of parts) {\n const trimmed = part.trim()\n if (trimmed.startsWith(`${name}=`)) {\n return trimmed.slice(name.length + 1)\n }\n }\n return undefined\n}\n\nfunction resolveTenantOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded) return { applied: true, value: null }\n const trimmed = decoded.trim()\n if (!trimmed) return { applied: true, value: null }\n return { applied: true, value: trimmed }\n}\n\nfunction resolveOrganizationOverride(raw: string | undefined): CookieOverride {\n if (raw === undefined) return { applied: false, value: null }\n const decoded = decodeCookieValue(raw)\n if (!decoded || decoded === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n const trimmed = decoded.trim()\n if (!trimmed || trimmed === ALL_ORGANIZATIONS_COOKIE_VALUE) {\n return { applied: true, value: null }\n }\n return { applied: true, value: trimmed }\n}\n\nfunction isSuperAdminAuth(auth: AuthContext | null | undefined): boolean {\n if (!auth) return false\n return (auth as Record<string, unknown>).isSuperAdmin === true\n}\n\nfunction applySuperAdminScope(\n auth: AuthContext,\n tenantCookie: string | undefined,\n orgCookie: string | undefined\n): AuthContext {\n if (!auth || !isSuperAdminAuth(auth)) return auth\n\n const tenantOverride = resolveTenantOverride(tenantCookie)\n const orgOverride = resolveOrganizationOverride(orgCookie)\n if (!tenantOverride.applied && !orgOverride.applied) return auth\n\n type MutableAuthContext = Exclude<AuthContext, null> & {\n actorTenantId?: string | null\n actorOrgId?: string | null\n }\n const baseAuth = auth as Exclude<AuthContext, null>\n const next: MutableAuthContext = { ...baseAuth }\n if (tenantOverride.applied) {\n if (!('actorTenantId' in next)) next.actorTenantId = auth?.tenantId ?? null\n next.tenantId = tenantOverride.value\n }\n if (orgOverride.applied) {\n if (!('actorOrgId' in next)) next.actorOrgId = auth?.orgId ?? null\n next.orgId = orgOverride.value\n }\n next.isSuperAdmin = true\n const existingRoles = Array.isArray(next.roles) ? next.roles : []\n if (!existingRoles.some((role) => typeof role === 'string' && role.trim().toLowerCase() === SUPERADMIN_ROLE)) {\n next.roles = [...existingRoles, 'superadmin']\n }\n return next\n}\n\nasync function resolveApiKeyAuth(secret: string): Promise<AuthContext> {\n if (!secret) return null\n const cache = getSharedApiKeyAuthCache()\n const cached = cache.get(secret)\n if (cached !== undefined) return cached as AuthContext\n try {\n const { createRequestContainer } = await import('@open-mercato/shared/lib/di/container')\n const container = await createRequestContainer()\n const em = (container.resolve('em') as EntityManager)\n const { findApiKeyBySecret } = await import('@open-mercato/core/modules/api_keys/services/apiKeyService')\n const { Role, RoleAcl, User } = await import('@open-mercato/core/modules/auth/data/entities')\n const { Organization, Tenant } = await import('@open-mercato/core/modules/directory/data/entities')\n\n const record = await findApiKeyBySecret(em, secret)\n if (!record) {\n cache.setMiss(secret)\n return null\n }\n\n const roleIds = Array.isArray(record.rolesJson)\n ? record.rolesJson.filter((value): value is string => typeof value === 'string' && value.length > 0)\n : []\n const roles = roleIds.length\n ? await em.find(Role, { id: { $in: roleIds }, deletedAt: null })\n : []\n const roleNames = roles.map((role) => role.name).filter((name): name is string => typeof name === 'string' && name.length > 0)\n\n let keyIsSuperAdmin = false\n if (roleIds.length) {\n const superAcl = await em.findOne(\n RoleAcl,\n { role: { $in: roleIds } as any, isSuperAdmin: true, deletedAt: null } as any,\n )\n keyIsSuperAdmin = !!(superAcl && (superAcl as { isSuperAdmin?: boolean }).isSuperAdmin)\n }\n\n if (cache.shouldWriteLastUsed(record.id)) {\n try {\n record.lastUsedAt = new Date()\n await em.persist(record).flush()\n } catch {\n // best-effort update; ignore write failures\n }\n }\n\n // For session keys, use sessionUserId; for regular keys, use createdBy\n const actualUserId = record.sessionUserId ?? record.createdBy ?? null\n\n if (actualUserId) {\n const user = await em.findOne(User, { id: actualUserId, deletedAt: null })\n if (!user) {\n cache.setMiss(secret)\n return null\n }\n if ((user.tenantId ?? null) !== (record.tenantId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n if ((user.organizationId ?? null) !== (record.organizationId ?? null)) {\n cache.setMiss(secret)\n return null\n }\n } else {\n if (record.tenantId) {\n const tenant = await em.findOne(Tenant, { id: record.tenantId, deletedAt: null, isActive: true })\n if (!tenant) {\n cache.setMiss(secret)\n return null\n }\n }\n if (record.organizationId) {\n const organization = await em.findOne(Organization, { id: record.organizationId, deletedAt: null, isActive: true })\n if (!organization) {\n cache.setMiss(secret)\n return null\n }\n if (record.tenantId && String(organization.tenant.id) !== record.tenantId) {\n cache.setMiss(secret)\n return null\n }\n }\n }\n\n const auth: Exclude<AuthContext, null> = {\n sub: `api_key:${record.id}`,\n tenantId: record.tenantId ?? null,\n orgId: record.organizationId ?? null,\n roles: roleNames,\n isApiKey: true,\n isSuperAdmin: keyIsSuperAdmin,\n keyId: record.id,\n keyName: record.name,\n ...(actualUserId ? { userId: actualUserId } : {}),\n }\n cache.setSuccess(secret, auth, record.expiresAt ? record.expiresAt.getTime() : null)\n return auth\n } catch {\n return null\n }\n}\n\nfunction extractApiKey(req: Request): string | null {\n const header = (req.headers.get('x-api-key') || '').trim()\n if (header) return header\n const authHeader = (req.headers.get('authorization') || '').trim()\n if (authHeader.toLowerCase().startsWith('apikey ')) {\n return authHeader.slice(7).trim()\n }\n return null\n}\n\nasync function resolveCanonicalInteractiveAuthContext(auth: AuthContext): Promise<AuthContext> {\n if (!auth || auth.isApiKey) return auth\n if (typeof auth.sub !== 'string' || auth.sub.trim().length === 0) return null\n\n try {\n const [{ createRequestContainer }, { resolveCanonicalStaffAuthContext }] = await Promise.all([\n import('@open-mercato/shared/lib/di/container'),\n import('@open-mercato/core/modules/auth/lib/sessionIntegrity'),\n ])\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return await resolveCanonicalStaffAuthContext(em, auth)\n } catch {\n return null\n }\n}\n\nexport async function resolveAuthFromCookiesDetailed(): Promise<AuthResolution> {\n const cookieStore = await cookies()\n const token = cookieStore.get('auth_token')?.value\n if (!token) return { auth: null, status: 'missing' }\n try {\n const payload = verifyJwt(token) as AuthContext\n if (!payload) return { auth: null, status: 'invalid' }\n if (payload.type === 'customer') return { auth: null, status: 'invalid' }\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (!canonicalAuth) return { auth: null, status: 'invalid' }\n const tenantCookie = cookieStore.get(TENANT_COOKIE_NAME)?.value\n const orgCookie = cookieStore.get(ORGANIZATION_COOKIE_NAME)?.value\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n } catch {\n return { auth: null, status: 'invalid' }\n }\n}\n\nexport async function getAuthFromCookies(): Promise<AuthContext> {\n return (await resolveAuthFromCookiesDetailed()).auth\n}\n\nexport async function resolveAuthFromRequestDetailed(req: Request): Promise<AuthResolution> {\n const cookieHeader = req.headers.get('cookie') || ''\n const tenantCookie = readCookieFromHeader(cookieHeader, TENANT_COOKIE_NAME)\n const orgCookie = readCookieFromHeader(cookieHeader, ORGANIZATION_COOKIE_NAME)\n const authHeader = (req.headers.get('authorization') || '').trim()\n let token: string | undefined\n let hadInvalidInteractiveToken = false\n if (authHeader.toLowerCase().startsWith('bearer ')) token = authHeader.slice(7).trim()\n if (!token) {\n const match = cookieHeader.match(/(?:^|;\\s*)auth_token=([^;]+)/)\n if (match) token = decodeURIComponent(match[1])\n }\n if (token) {\n try {\n const payload = verifyJwt(token) as AuthContext\n if (payload && payload.type === 'customer') return { auth: null, status: 'invalid' }\n if (payload) {\n const canonicalAuth = await resolveCanonicalInteractiveAuthContext(payload)\n if (canonicalAuth) {\n return {\n auth: applySuperAdminScope(canonicalAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n }\n hadInvalidInteractiveToken = true\n }\n } catch {\n hadInvalidInteractiveToken = true\n }\n }\n\n const apiKey = extractApiKey(req)\n if (!apiKey) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n const apiAuth = await resolveApiKeyAuth(apiKey)\n if (!apiAuth) {\n return { auth: null, status: hadInvalidInteractiveToken ? 'invalid' : 'missing' }\n }\n return {\n auth: applySuperAdminScope(apiAuth, tenantCookie, orgCookie),\n status: 'authenticated',\n }\n}\n\nexport async function getAuthFromRequest(req: Request): Promise<AuthContext> {\n return (await resolveAuthFromRequestDetailed(req)).auth\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/lib/data/engine.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { sql } from "kysely";
|
|
1
2
|
import { setRecordCustomFields } from "@open-mercato/core/modules/entities/lib/helpers";
|
|
2
3
|
import { validateCustomFieldValuesServer } from "@open-mercato/core/modules/entities/lib/validation";
|
|
3
4
|
import { sanitizeCustomFieldHtmlRichTextValuesServer } from "@open-mercato/core/modules/entities/lib/htmlRichTextSanitizer";
|
|
@@ -93,9 +94,12 @@ class DefaultDataEngine {
|
|
|
93
94
|
return false;
|
|
94
95
|
}
|
|
95
96
|
}
|
|
97
|
+
getKysely() {
|
|
98
|
+
return this.em.getKysely();
|
|
99
|
+
}
|
|
96
100
|
async ensureStorageTableExists() {
|
|
97
|
-
const
|
|
98
|
-
const exists = await
|
|
101
|
+
const db = this.getKysely();
|
|
102
|
+
const exists = await db.selectFrom("information_schema.tables").select(sql`1`.as("present")).where("table_name", "=", "custom_entities_storage").executeTakeFirst();
|
|
99
103
|
if (!exists) {
|
|
100
104
|
throw new Error("custom_entities_storage table is missing. Run migrations (yarn db:migrate).");
|
|
101
105
|
}
|
|
@@ -128,7 +132,7 @@ class DefaultDataEngine {
|
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
async createCustomEntityRecord(opts) {
|
|
131
|
-
const
|
|
135
|
+
const db = this.getKysely();
|
|
132
136
|
await this.ensureStorageTableExists();
|
|
133
137
|
const sanitizedValues = await sanitizeCustomFieldHtmlRichTextValuesServer(this.em, {
|
|
134
138
|
entityId: opts.entityId,
|
|
@@ -153,23 +157,32 @@ class DefaultDataEngine {
|
|
|
153
157
|
const orgId = opts.organizationId ?? null;
|
|
154
158
|
const tenantId = opts.tenantId ?? null;
|
|
155
159
|
const doc = { id, ...this.normalizeDocValues(sanitizedValues || {}) };
|
|
160
|
+
const now = sql`now()`;
|
|
156
161
|
const payload = {
|
|
157
162
|
entity_type: opts.entityId,
|
|
158
163
|
entity_id: id,
|
|
159
164
|
organization_id: orgId,
|
|
160
165
|
tenant_id: tenantId,
|
|
161
|
-
doc
|
|
162
|
-
updated_at:
|
|
163
|
-
created_at:
|
|
166
|
+
doc: sql`${JSON.stringify(doc)}::jsonb`,
|
|
167
|
+
updated_at: now,
|
|
168
|
+
created_at: now,
|
|
164
169
|
deleted_at: null
|
|
165
170
|
};
|
|
166
171
|
try {
|
|
167
|
-
await
|
|
172
|
+
await db.insertInto("custom_entities_storage").values(payload).onConflict((oc) => oc.columns(["entity_type", "entity_id", "organization_id"]).doUpdateSet({
|
|
173
|
+
doc: sql`${JSON.stringify(doc)}::jsonb`,
|
|
174
|
+
updated_at: sql`now()`,
|
|
175
|
+
deleted_at: null
|
|
176
|
+
})).execute();
|
|
168
177
|
} catch {
|
|
169
178
|
try {
|
|
170
|
-
const updated = await
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
const updated = await db.updateTable("custom_entities_storage").set({
|
|
180
|
+
doc: sql`${JSON.stringify(doc)}::jsonb`,
|
|
181
|
+
updated_at: sql`now()`,
|
|
182
|
+
deleted_at: null
|
|
183
|
+
}).where("entity_type", "=", opts.entityId).where("entity_id", "=", id).where("organization_id", orgId === null ? "is" : "=", orgId).executeTakeFirst();
|
|
184
|
+
if (!updated || Number(updated.numUpdatedRows ?? 0) === 0) {
|
|
185
|
+
await db.insertInto("custom_entities_storage").values(payload).execute();
|
|
173
186
|
}
|
|
174
187
|
} catch (err) {
|
|
175
188
|
throw err;
|
|
@@ -189,7 +202,7 @@ class DefaultDataEngine {
|
|
|
189
202
|
return { id };
|
|
190
203
|
}
|
|
191
204
|
async updateCustomEntityRecord(opts) {
|
|
192
|
-
const
|
|
205
|
+
const db = this.getKysely();
|
|
193
206
|
const sanitizedValues = await sanitizeCustomFieldHtmlRichTextValuesServer(this.em, {
|
|
194
207
|
entityId: opts.entityId,
|
|
195
208
|
organizationId: opts.organizationId ?? null,
|
|
@@ -201,22 +214,36 @@ class DefaultDataEngine {
|
|
|
201
214
|
const orgId = opts.organizationId ?? null;
|
|
202
215
|
const tenantId = opts.tenantId ?? null;
|
|
203
216
|
await this.ensureStorageTableExists();
|
|
204
|
-
const
|
|
217
|
+
const applyScope = (q) => {
|
|
218
|
+
let chain = q.where("entity_type", "=", opts.entityId);
|
|
219
|
+
chain = chain.where("entity_id", "=", id);
|
|
220
|
+
chain = orgId === null ? chain.where("organization_id", "is", null) : chain.where("organization_id", "=", orgId);
|
|
221
|
+
return chain;
|
|
222
|
+
};
|
|
223
|
+
const row = await applyScope(
|
|
224
|
+
db.selectFrom("custom_entities_storage").select(["doc"])
|
|
225
|
+
).executeTakeFirst();
|
|
205
226
|
const prevDoc = row?.doc || { id };
|
|
206
227
|
const nextDoc = { ...prevDoc, ...this.normalizeDocValues(sanitizedValues || {}), id };
|
|
207
228
|
try {
|
|
208
|
-
const updated = await
|
|
209
|
-
|
|
210
|
-
|
|
229
|
+
const updated = await applyScope(
|
|
230
|
+
db.updateTable("custom_entities_storage").set({
|
|
231
|
+
doc: sql`${JSON.stringify(nextDoc)}::jsonb`,
|
|
232
|
+
updated_at: sql`now()`,
|
|
233
|
+
deleted_at: null
|
|
234
|
+
})
|
|
235
|
+
).executeTakeFirst();
|
|
236
|
+
if (!updated || Number(updated.numUpdatedRows ?? 0) === 0) {
|
|
237
|
+
await db.insertInto("custom_entities_storage").values({
|
|
211
238
|
entity_type: opts.entityId,
|
|
212
239
|
entity_id: id,
|
|
213
240
|
organization_id: orgId,
|
|
214
241
|
tenant_id: tenantId,
|
|
215
|
-
doc: nextDoc
|
|
216
|
-
created_at:
|
|
217
|
-
updated_at:
|
|
242
|
+
doc: sql`${JSON.stringify(nextDoc)}::jsonb`,
|
|
243
|
+
created_at: sql`now()`,
|
|
244
|
+
updated_at: sql`now()`,
|
|
218
245
|
deleted_at: null
|
|
219
|
-
});
|
|
246
|
+
}).execute();
|
|
220
247
|
}
|
|
221
248
|
} catch (err) {
|
|
222
249
|
throw err;
|
|
@@ -234,14 +261,25 @@ class DefaultDataEngine {
|
|
|
234
261
|
}
|
|
235
262
|
}
|
|
236
263
|
async deleteCustomEntityRecord(opts) {
|
|
237
|
-
const
|
|
264
|
+
const db = this.getKysely();
|
|
238
265
|
const id = String(opts.recordId);
|
|
239
266
|
const orgId = opts.organizationId ?? null;
|
|
240
267
|
const soft = opts.soft !== false;
|
|
268
|
+
const applyScope = (q) => {
|
|
269
|
+
let chain = q.where("entity_type", "=", opts.entityId);
|
|
270
|
+
chain = chain.where("entity_id", "=", id);
|
|
271
|
+
chain = orgId === null ? chain.where("organization_id", "is", null) : chain.where("organization_id", "=", orgId);
|
|
272
|
+
return chain;
|
|
273
|
+
};
|
|
241
274
|
if (soft) {
|
|
242
|
-
await
|
|
275
|
+
await applyScope(
|
|
276
|
+
db.updateTable("custom_entities_storage").set({
|
|
277
|
+
deleted_at: sql`now()`,
|
|
278
|
+
updated_at: sql`now()`
|
|
279
|
+
})
|
|
280
|
+
).execute();
|
|
243
281
|
} else {
|
|
244
|
-
await
|
|
282
|
+
await applyScope(db.deleteFrom("custom_entities_storage")).execute();
|
|
245
283
|
}
|
|
246
284
|
try {
|
|
247
285
|
const { CustomFieldValue } = await import("@open-mercato/core/modules/entities/data/entities");
|
|
@@ -257,7 +295,10 @@ class DefaultDataEngine {
|
|
|
257
295
|
record.deletedAt = now;
|
|
258
296
|
return true;
|
|
259
297
|
});
|
|
260
|
-
if (mutated.length)
|
|
298
|
+
if (mutated.length) {
|
|
299
|
+
for (const record of values) this.em.persist(record);
|
|
300
|
+
await this.em.flush();
|
|
301
|
+
}
|
|
261
302
|
} catch {
|
|
262
303
|
}
|
|
263
304
|
}
|
|
@@ -266,14 +307,14 @@ class DefaultDataEngine {
|
|
|
266
307
|
opts.entity,
|
|
267
308
|
opts.data
|
|
268
309
|
);
|
|
269
|
-
await this.em.
|
|
310
|
+
await this.em.persist(entity).flush();
|
|
270
311
|
return entity;
|
|
271
312
|
}
|
|
272
313
|
async updateOrmEntity(opts) {
|
|
273
314
|
const current = await this.em.findOne(opts.entity, opts.where);
|
|
274
315
|
if (!current) return null;
|
|
275
316
|
await opts.apply(current);
|
|
276
|
-
await this.em.
|
|
317
|
+
await this.em.persist(current).flush();
|
|
277
318
|
return current;
|
|
278
319
|
}
|
|
279
320
|
async deleteOrmEntity(opts) {
|
|
@@ -284,10 +325,10 @@ class DefaultDataEngine {
|
|
|
284
325
|
if (typeof current === "object" && current !== null) {
|
|
285
326
|
;
|
|
286
327
|
current[field] = /* @__PURE__ */ new Date();
|
|
287
|
-
await this.em.
|
|
328
|
+
await this.em.persist(current).flush();
|
|
288
329
|
}
|
|
289
330
|
} else {
|
|
290
|
-
await this.em.
|
|
331
|
+
await this.em.remove(current).flush();
|
|
291
332
|
}
|
|
292
333
|
return current;
|
|
293
334
|
}
|