@things-factory/shell 10.0.0-y.0 → 10.0.0-zeta.10
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/client/entries/public/home.js +1 -1
- package/dist-server/bootstrap-hooks.d.ts +51 -0
- package/dist-server/bootstrap-hooks.js +39 -0
- package/dist-server/bootstrap-hooks.js.map +1 -0
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/initializers/database.js +4 -1
- package/dist-server/initializers/database.js.map +1 -1
- package/dist-server/server-dev.js +13 -10
- package/dist-server/server-dev.js.map +1 -1
- package/dist-server/server.js +12 -8
- package/dist-server/server.js.map +1 -1
- package/dist-server/service/domain/domain-mutation.js +3 -2
- package/dist-server/service/domain/domain-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/dist-server/typeorm/get-data-encryption-key.js +11 -1
- package/dist-server/typeorm/get-data-encryption-key.js.map +1 -1
- package/dist-server/utils/apply-domain-filter.d.ts +17 -0
- package/dist-server/utils/apply-domain-filter.js +30 -0
- package/dist-server/utils/apply-domain-filter.js.map +1 -0
- package/dist-server/utils/condition-builder.js +15 -7
- package/dist-server/utils/condition-builder.js.map +1 -1
- package/dist-server/utils/get-domain.js +5 -2
- package/dist-server/utils/get-domain.js.map +1 -1
- package/dist-server/utils/get-query-builder-from-list-params.d.ts +39 -9
- package/dist-server/utils/get-query-builder-from-list-params.js +83 -40
- package/dist-server/utils/get-query-builder-from-list-params.js.map +1 -1
- package/dist-server/utils/list-params-converter.js +8 -6
- package/dist-server/utils/list-params-converter.js.map +1 -1
- package/dist-server/utils/list-query-builder.js +2 -14
- package/dist-server/utils/list-query-builder.js.map +1 -1
- package/jest.config.js +5 -0
- package/package.json +6 -6
- package/tests/get-query-builder-features.test.ts +229 -0
- package/tests/list-param-operators.test.ts +87 -0
- package/tests/manifest-contract.test.ts +38 -0
- package/client/store.ts +0 -15
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.dataEncryptionKey = void 0;
|
|
4
4
|
const env_1 = require("@things-factory/env");
|
|
5
|
+
/* shell 이 dev 편의를 위해 제공하는 기본 키. 프로덕션에서는 앱이 자기 config 로
|
|
6
|
+
반드시 override 하는 것이 정석/권장이다. */
|
|
7
|
+
const DEFAULT_DATA_ENCRYPTION_KEY = 'V6g5oHJZb7KcYzIyL6cM95XvIDouon5b';
|
|
5
8
|
var _DATA_ENCRYPTION_KEY = env_1.config.get('dataEncryptionKey');
|
|
6
9
|
if (!_DATA_ENCRYPTION_KEY) {
|
|
7
10
|
if (process.env.NODE_ENV == 'production') {
|
|
8
11
|
throw new TypeError('dataEncryptionKey not configured.');
|
|
9
12
|
}
|
|
10
13
|
else {
|
|
11
|
-
_DATA_ENCRYPTION_KEY =
|
|
14
|
+
_DATA_ENCRYPTION_KEY = DEFAULT_DATA_ENCRYPTION_KEY;
|
|
12
15
|
}
|
|
13
16
|
}
|
|
17
|
+
/* 프로덕션인데 앱이 override 하지 않아 레포에 공개된 기본 키가 그대로 쓰이는 경우:
|
|
18
|
+
암호화가 사실상 무의미하므로 강한 보안 경고를 남긴다(부팅·데이터는 건드리지 않음). */
|
|
19
|
+
if (process.env.NODE_ENV == 'production' && _DATA_ENCRYPTION_KEY == DEFAULT_DATA_ENCRYPTION_KEY) {
|
|
20
|
+
env_1.logger.warn('[SECURITY] dataEncryptionKey is using the built-in default committed in the repository, ' +
|
|
21
|
+
'which provides NO real protection for encrypted data. ' +
|
|
22
|
+
'Override `dataEncryptionKey` in your application config for production.');
|
|
23
|
+
}
|
|
14
24
|
exports.dataEncryptionKey = _DATA_ENCRYPTION_KEY;
|
|
15
25
|
//# sourceMappingURL=get-data-encryption-key.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-data-encryption-key.js","sourceRoot":"","sources":["../../server/typeorm/get-data-encryption-key.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"get-data-encryption-key.js","sourceRoot":"","sources":["../../server/typeorm/get-data-encryption-key.ts"],"names":[],"mappings":";;;AAAA,6CAAoD;AAEpD;iCACiC;AACjC,MAAM,2BAA2B,GAAG,kCAAkC,CAAA;AAEtE,IAAI,oBAAoB,GAAG,YAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAA;AAE1D,IAAI,CAAC,oBAAoB,EAAE,CAAC;IAC1B,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,YAAY,EAAE,CAAC;QACzC,MAAM,IAAI,SAAS,CAAC,mCAAmC,CAAC,CAAA;IAC1D,CAAC;SAAM,CAAC;QACN,oBAAoB,GAAG,2BAA2B,CAAA;IACpD,CAAC;AACH,CAAC;AAED;qDACqD;AACrD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,YAAY,IAAI,oBAAoB,IAAI,2BAA2B,EAAE,CAAC;IAChG,YAAM,CAAC,IAAI,CACT,0FAA0F;QACxF,wDAAwD;QACxD,yEAAyE,CAC5E,CAAA;AACH,CAAC;AAEY,QAAA,iBAAiB,GAAG,oBAAoB,CAAA","sourcesContent":["import { config, logger } from '@things-factory/env'\n\n/* shell 이 dev 편의를 위해 제공하는 기본 키. 프로덕션에서는 앱이 자기 config 로\n 반드시 override 하는 것이 정석/권장이다. */\nconst DEFAULT_DATA_ENCRYPTION_KEY = 'V6g5oHJZb7KcYzIyL6cM95XvIDouon5b'\n\nvar _DATA_ENCRYPTION_KEY = config.get('dataEncryptionKey')\n\nif (!_DATA_ENCRYPTION_KEY) {\n if (process.env.NODE_ENV == 'production') {\n throw new TypeError('dataEncryptionKey not configured.')\n } else {\n _DATA_ENCRYPTION_KEY = DEFAULT_DATA_ENCRYPTION_KEY\n }\n}\n\n/* 프로덕션인데 앱이 override 하지 않아 레포에 공개된 기본 키가 그대로 쓰이는 경우:\n 암호화가 사실상 무의미하므로 강한 보안 경고를 남긴다(부팅·데이터는 건드리지 않음). */\nif (process.env.NODE_ENV == 'production' && _DATA_ENCRYPTION_KEY == DEFAULT_DATA_ENCRYPTION_KEY) {\n logger.warn(\n '[SECURITY] dataEncryptionKey is using the built-in default committed in the repository, ' +\n 'which provides NO real protection for encrypted data. ' +\n 'Override `dataEncryptionKey` in your application config for production.'\n )\n}\n\nexport const dataEncryptionKey = _DATA_ENCRYPTION_KEY\n"]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { WhereExpressionBuilder } from 'typeorm';
|
|
2
|
+
import { InheritedValueType } from '../service/common-types/list-param.js';
|
|
3
|
+
/**
|
|
4
|
+
* 멀티테넌트 도메인 상속 필터를 쿼리에 적용한다.
|
|
5
|
+
*
|
|
6
|
+
* `getQueryBuilderFromListParams` 와 (deprecated) `buildQuery` 양쪽에 동일하게 중복돼 있던
|
|
7
|
+
* 도메인 필터 로직의 단일 소스. 도메인 필터는 테넌트 격리(보안)에 직결되므로 한 곳에서만 정의한다.
|
|
8
|
+
*
|
|
9
|
+
* @param qb 조건을 추가할 대상(WhereExpressionBuilder / SelectQueryBuilder)
|
|
10
|
+
* @param alias 엔티티 alias
|
|
11
|
+
* @param domain 현재 도메인 (id, parentId)
|
|
12
|
+
* @param inherited 상속 방식: None(자기 도메인) / Include(자기+부모) / Only(부모만)
|
|
13
|
+
*/
|
|
14
|
+
export declare function applyDomainInheritanceFilter(qb: WhereExpressionBuilder, alias: string, domain: {
|
|
15
|
+
id: string;
|
|
16
|
+
parentId?: string | null;
|
|
17
|
+
}, inherited?: InheritedValueType): void;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyDomainInheritanceFilter = applyDomainInheritanceFilter;
|
|
4
|
+
const list_param_js_1 = require("../service/common-types/list-param.js");
|
|
5
|
+
/**
|
|
6
|
+
* 멀티테넌트 도메인 상속 필터를 쿼리에 적용한다.
|
|
7
|
+
*
|
|
8
|
+
* `getQueryBuilderFromListParams` 와 (deprecated) `buildQuery` 양쪽에 동일하게 중복돼 있던
|
|
9
|
+
* 도메인 필터 로직의 단일 소스. 도메인 필터는 테넌트 격리(보안)에 직결되므로 한 곳에서만 정의한다.
|
|
10
|
+
*
|
|
11
|
+
* @param qb 조건을 추가할 대상(WhereExpressionBuilder / SelectQueryBuilder)
|
|
12
|
+
* @param alias 엔티티 alias
|
|
13
|
+
* @param domain 현재 도메인 (id, parentId)
|
|
14
|
+
* @param inherited 상속 방식: None(자기 도메인) / Include(자기+부모) / Only(부모만)
|
|
15
|
+
*/
|
|
16
|
+
function applyDomainInheritanceFilter(qb, alias, domain, inherited = list_param_js_1.InheritedValueType.None) {
|
|
17
|
+
if (!inherited || inherited === list_param_js_1.InheritedValueType.None) {
|
|
18
|
+
qb.andWhere(`${alias}.domain = :domain`, { domain: domain.id });
|
|
19
|
+
}
|
|
20
|
+
else if (inherited === list_param_js_1.InheritedValueType.Include) {
|
|
21
|
+
qb.andWhere(`${alias}.domain IN (:...domains)`, { domains: [domain.id, domain.parentId].filter(Boolean) });
|
|
22
|
+
}
|
|
23
|
+
else if (inherited === list_param_js_1.InheritedValueType.Only) {
|
|
24
|
+
qb.andWhere(`${alias}.domain = :domain`, { domain: domain.parentId || 'Impossible' });
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
qb.andWhere(`${alias}.domain = :domain`, { domain: 'Impossible' });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=apply-domain-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply-domain-filter.js","sourceRoot":"","sources":["../../server/utils/apply-domain-filter.ts"],"names":[],"mappings":";;AAeA,oEAeC;AA5BD,yEAA0E;AAE1E;;;;;;;;;;GAUG;AACH,SAAgB,4BAA4B,CAC1C,EAA0B,EAC1B,KAAa,EACb,MAAgD,EAChD,YAAgC,kCAAkB,CAAC,IAAI;IAEvD,IAAI,CAAC,SAAS,IAAI,SAAS,KAAK,kCAAkB,CAAC,IAAI,EAAE,CAAC;QACxD,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAA;IACjE,CAAC;SAAM,IAAI,SAAS,KAAK,kCAAkB,CAAC,OAAO,EAAE,CAAC;QACpD,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,0BAA0B,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAC5G,CAAC;SAAM,IAAI,SAAS,KAAK,kCAAkB,CAAC,IAAI,EAAE,CAAC;QACjD,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,mBAAmB,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,QAAQ,IAAI,YAAY,EAAE,CAAC,CAAA;IACvF,CAAC;SAAM,CAAC;QACN,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,mBAAmB,EAAE,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAA;IACpE,CAAC;AACH,CAAC","sourcesContent":["import { WhereExpressionBuilder } from 'typeorm'\n\nimport { InheritedValueType } from '../service/common-types/list-param.js'\n\n/**\n * 멀티테넌트 도메인 상속 필터를 쿼리에 적용한다.\n *\n * `getQueryBuilderFromListParams` 와 (deprecated) `buildQuery` 양쪽에 동일하게 중복돼 있던\n * 도메인 필터 로직의 단일 소스. 도메인 필터는 테넌트 격리(보안)에 직결되므로 한 곳에서만 정의한다.\n *\n * @param qb 조건을 추가할 대상(WhereExpressionBuilder / SelectQueryBuilder)\n * @param alias 엔티티 alias\n * @param domain 현재 도메인 (id, parentId)\n * @param inherited 상속 방식: None(자기 도메인) / Include(자기+부모) / Only(부모만)\n */\nexport function applyDomainInheritanceFilter(\n qb: WhereExpressionBuilder,\n alias: string,\n domain: { id: string; parentId?: string | null },\n inherited: InheritedValueType = InheritedValueType.None\n): void {\n if (!inherited || inherited === InheritedValueType.None) {\n qb.andWhere(`${alias}.domain = :domain`, { domain: domain.id })\n } else if (inherited === InheritedValueType.Include) {\n qb.andWhere(`${alias}.domain IN (:...domains)`, { domains: [domain.id, domain.parentId].filter(Boolean) })\n } else if (inherited === InheritedValueType.Only) {\n qb.andWhere(`${alias}.domain = :domain`, { domain: domain.parentId || 'Impossible' })\n } else {\n qb.andWhere(`${alias}.domain = :domain`, { domain: 'Impossible' })\n }\n}\n"]}
|
|
@@ -95,33 +95,41 @@ const buildCondition = function (alias, fieldName, operator, value, relation, se
|
|
|
95
95
|
return {
|
|
96
96
|
clause: `${alias}.${fieldName} IS NOT NULL`
|
|
97
97
|
};
|
|
98
|
+
/* boolean 비교는 파라미터 바인딩으로 (IS TRUE/FALSE 는 MSSQL/Oracle 미지원 → 이식성 확보) */
|
|
98
99
|
case 'is_false':
|
|
99
100
|
return {
|
|
100
|
-
clause: `${alias}.${fieldName}
|
|
101
|
+
clause: `${alias}.${fieldName} = :args${seq}`,
|
|
102
|
+
parameters: { [`args${seq}`]: false }
|
|
101
103
|
};
|
|
102
104
|
case 'is_true':
|
|
103
105
|
return {
|
|
104
|
-
clause: `${alias}.${fieldName}
|
|
106
|
+
clause: `${alias}.${fieldName} = :args${seq}`,
|
|
107
|
+
parameters: { [`args${seq}`]: true }
|
|
105
108
|
};
|
|
106
109
|
case 'is_not_false':
|
|
110
|
+
/* NOT FALSE = true 또는 NULL */
|
|
107
111
|
return {
|
|
108
|
-
clause:
|
|
112
|
+
clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,
|
|
113
|
+
parameters: { [`args${seq}`]: true }
|
|
109
114
|
};
|
|
110
115
|
case 'is_not_true':
|
|
116
|
+
/* NOT TRUE = false 또는 NULL */
|
|
111
117
|
return {
|
|
112
|
-
clause:
|
|
118
|
+
clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,
|
|
119
|
+
parameters: { [`args${seq}`]: false }
|
|
113
120
|
};
|
|
114
121
|
case 'is_present':
|
|
115
122
|
return {
|
|
116
|
-
clause: `${alias}.${fieldName} IS
|
|
123
|
+
clause: `${alias}.${fieldName} IS NOT NULL`
|
|
117
124
|
};
|
|
118
125
|
case 'is_blank':
|
|
119
126
|
return {
|
|
120
|
-
clause: `${alias}.${fieldName} IS
|
|
127
|
+
clause: `${alias}.${fieldName} IS NULL`
|
|
121
128
|
};
|
|
122
129
|
case 'is_empty_num_id':
|
|
123
130
|
return {
|
|
124
|
-
clause:
|
|
131
|
+
clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,
|
|
132
|
+
parameters: { [`args${seq}`]: 0 }
|
|
125
133
|
};
|
|
126
134
|
case 'between':
|
|
127
135
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"condition-builder.js","sourceRoot":"","sources":["../../server/utils/condition-builder.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"condition-builder.js","sourceRoot":"","sources":["../../server/utils/condition-builder.ts"],"names":[],"mappings":"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","sourcesContent":["import _ from 'lodash'\n\n/**\n * Generates and returns a condition clause based on the provided parameters.\n *\n * @param alias {string} Alias or table name of the entity.\n * @param fieldName {string} Field or column name.\n * @param operator {string} Comparison operator.\n * @param value {any} Value or an array of values to compare.\n * @param relation {boolean | string} Indicates if it's a related field or the name of the relation (optional).\n * @param seq {number} Integer representing the order of condition generation (internal use).\n * @returns {Object} An object containing the generated condition clause and parameters.\n */\nexport const buildCondition = function (alias: string, fieldName: string, operator: string, value: any, relation: boolean | string, seq: number) {\n seq++\n\n fieldName = _.snakeCase(fieldName)\n const values = value instanceof Array ? value : [value]\n\n switch (operator) {\n case 'eq':\n return {\n clause: `${alias}.${fieldName} = :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'like':\n return {\n clause: `${alias}.${fieldName} LIKE :args${seq}`,\n parameters: { [`args${seq}`]: `${value}` }\n }\n\n case 'search':\n case 'i_like':\n return {\n clause: `LOWER(${alias}.${fieldName}) LIKE :args${seq}`,\n parameters: { [`args${seq}`]: `${String(value).toLowerCase()}` }\n }\n\n case 'nlike':\n return {\n clause: `${alias}.${fieldName} NOT LIKE :args${seq}`,\n value: { [`args${seq}`]: `${value}` }\n }\n\n case 'i_nlike':\n return {\n clause: `LOWER(${alias}.${fieldName}) NOT LIKE :args${seq}`,\n value: { [`args${seq}`]: `${String(value).toLowerCase()}` }\n }\n\n case 'lt':\n return {\n clause: `${alias}.${fieldName} < :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'gt':\n return {\n clause: `${alias}.${fieldName} > :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'lte':\n return {\n clause: `${alias}.${fieldName} <= :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'gte':\n return {\n clause: `${alias}.${fieldName} >= :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'noteq':\n return {\n clause: `${alias}.${fieldName} != :args${seq}`,\n parameters: { [`args${seq}`]: value }\n }\n\n case 'in':\n return {\n clause: relation ? `${fieldName}.id IN (:...args${seq})` : `${alias}.${fieldName} IN (:...args${seq})`,\n parameters: { [`args${seq}`]: values }\n }\n\n case 'notin':\n return {\n clause: relation ? `${fieldName}.id NOT IN (:...args${seq})` : `${alias}.${fieldName} NOT IN (:...args${seq})`,\n parameters: { [`args${seq}`]: values }\n }\n\n case 'notin_with_null':\n return {\n clause: relation\n ? `(${fieldName}.id IS NULL OR ${fieldName}.id NOT IN (:...args${seq})`\n : `${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} NOT IN (:...args${seq}))`,\n parameters: { [`args${seq}`]: values }\n }\n\n case 'is_null':\n return {\n clause: `${alias}.${fieldName} IS NULL`\n }\n case 'is_not_null':\n return {\n clause: `${alias}.${fieldName} IS NOT NULL`\n }\n /* boolean 비교는 파라미터 바인딩으로 (IS TRUE/FALSE 는 MSSQL/Oracle 미지원 → 이식성 확보) */\n case 'is_false':\n return {\n clause: `${alias}.${fieldName} = :args${seq}`,\n parameters: { [`args${seq}`]: false }\n }\n case 'is_true':\n return {\n clause: `${alias}.${fieldName} = :args${seq}`,\n parameters: { [`args${seq}`]: true }\n }\n case 'is_not_false':\n /* NOT FALSE = true 또는 NULL */\n return {\n clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,\n parameters: { [`args${seq}`]: true }\n }\n case 'is_not_true':\n /* NOT TRUE = false 또는 NULL */\n return {\n clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,\n parameters: { [`args${seq}`]: false }\n }\n case 'is_present':\n return {\n clause: `${alias}.${fieldName} IS NOT NULL`\n }\n case 'is_blank':\n return {\n clause: `${alias}.${fieldName} IS NULL`\n }\n case 'is_empty_num_id':\n return {\n clause: `(${alias}.${fieldName} IS NULL OR ${alias}.${fieldName} = :args${seq})`,\n parameters: { [`args${seq}`]: 0 }\n }\n\n case 'between':\n return {\n clause: `${alias}.${fieldName} BETWEEN :args${seq}_1 AND :args${seq}_2`,\n parameters: { [`args${seq}_1`]: values[0], [`args${seq}_2`]: values[1] }\n }\n }\n}\n"]}
|
|
@@ -173,8 +173,11 @@ function findSubdomainFromPath(context, path) {
|
|
|
173
173
|
}
|
|
174
174
|
var parsed = getUrlFromContext(context, path);
|
|
175
175
|
var { pathname } = parsed;
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
// 도메인 타입 prefix 를 현재 요청 경로(context)가 아니라 대상 path(예: redirect_to) 에서
|
|
177
|
+
// 직접 파싱한다. checkin 시점의 요청 경로는 '/auth/checkin' 이라 prefix 가 비어
|
|
178
|
+
// 정규식이 '^//(...)' 가 되어 '/project/2026-00019/...' 같은 redirect_to 와 매칭 실패하던 문제 수정.
|
|
179
|
+
// getPathInfo 는 domainTypes(예: ['domain','project']) 중 어떤 prefix 든 인식한다.
|
|
180
|
+
return (0, utils_1.getPathInfo)(pathname, domainTypes)?.subdomain;
|
|
178
181
|
}
|
|
179
182
|
/**
|
|
180
183
|
* Retrieves the route prefix for a specific domain type.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"get-domain.js","sourceRoot":"","sources":["../../server/utils/get-domain.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url'\n\nimport { config } from '@things-factory/env'\nimport { getPathInfo } from '@things-factory/utils'\n\nimport { getRepository } from '../initializers/database'\nimport { Domain } from '../service/domain/domain'\n\nconst protocol: string = config.get('protocol')\nconst fixed = config.get('subdomain')\nconst domainTypes = config.get('domainTypes') || ['domain']\n\n/* 하위 호환성을 위해서, domain prefix가 없는 경우, domain을 추가해준다. */\nif (domainTypes.indexOf('domain') < 0) {\n domainTypes.push('domain')\n}\n\nconst routePrefixForDomainType = `:domainType(${domainTypes.join('|')})`\n\n/**\n * Creates a URL based on the given context and path.\n *\n * @param context {Object} An object containing the current request context information.\n * @param path {string} The path to be added to the created URL (optional).\n * @returns {URL} The generated URL object.\n */\nexport function getUrlFromContext(context, path = '') {\n const { method, href, host, header } = context\n const { referer } = header || {}\n\n var base = href\n\n if (method !== 'GET' && referer) {\n var { host: refererHost } = new URL(referer)\n if (refererHost === host) {\n base = referer\n }\n }\n\n let url: URL = new URL(path, base)\n\n const originalProtocol = context.headers['x-forwarded-proto']\n const originalHost = context.headers['x-forwarded-host']\n const originalPort = context.headers['x-forwarded-port']\n\n if (protocol || originalProtocol) {\n url.protocol = protocol || originalProtocol\n }\n\n if (originalHost) {\n url.host = originalHost\n if (originalPort) {\n url.port = originalPort\n }\n }\n\n return url\n}\n\n/**\n * Extracts a subdomain from the path.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The extracted subdomain.\n */\nfunction getSubdomainFromPath(context: any) {\n var { path } = context\n\n var subdomain = getPathInfo(path || '', domainTypes)?.subdomain\n if (subdomain) {\n return subdomain\n }\n\n var {\n header: { referer }\n } = context\n\n if (referer) {\n var { pathname } = new URL(referer)\n return getPathInfo(pathname || '', domainTypes)?.subdomain\n }\n}\n\n/**\n * Extracts the subdomain type from the given context's path or referer header.\n *\n * @param context - The context object containing path and header information.\n * @returns The subdomain type if found, otherwise undefined.\n */\nfunction getSubdomainTypeFromPath(context: any) {\n var { path } = context\n\n var prefix = getPathInfo(path || '', domainTypes)?.prefix\n if (prefix) {\n return prefix\n }\n\n var {\n header: { referer }\n } = context\n\n if (referer) {\n var { pathname } = new URL(referer)\n return getPathInfo(pathname || '', domainTypes)?.prefix\n }\n}\n\n/**\n * Extracts a subdomain from the URL context.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The extracted subdomain.\n */\nfunction getSubdomainFromURL(context) {\n return fixed || getSubdomainFromPath(context)\n}\n\nfunction getSubdomainTypeFromURL(context) {\n return fixed ? '' : getSubdomainTypeFromPath(context)\n}\n\n/**\n * Asynchronously searches for a domain object based on the URL context.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {Promise<Domain>} A promise that resolves to the domain object.\n */\nexport async function getDomainFromURL(context: any): Promise<Domain> {\n const { header } = context\n\n const subdomain = header['x-things-factory-domain'] || getSubdomainFromURL(context)\n const extType = header['x-things-factory-type'] || getSubdomainTypeFromURL(context)\n\n if (subdomain) {\n return await getRepository(Domain).findOne({\n where:\n fixed || extType == 'domain' || !extType\n ? {\n subdomain\n }\n : {\n subdomain,\n extType\n },\n cache: true\n })\n }\n}\n\n/**\n * Generates a context path based on the subdomain.\n *\n * @param subdomain {string} The subdomain.\n * @returns {string} The generated context path.\n */\nexport function getContextPath(domain: Domain) {\n const type = domain?.extType || 'domain'\n return fixed || !domain ? '' : `${type}/${domain?.subdomain}/`\n}\n\n/**\n * Generates a redirection path considering the subdomain.\n *\n * @param context {Object} An object containing the current request context information.\n * @param subdomain {Domain} The target domain.\n * @param redirectTo {string} The path to redirect to (optional).\n * @returns {string} The generated redirection path.\n */\nexport function getRedirectSubdomainPath(context, domain: Partial<Domain>, redirectTo: string = '/') {\n if (fixed || !domain) {\n return redirectTo || '/'\n }\n\n const type = domain.extType || 'domain'\n const subdomain = domain.subdomain || fixed\n\n var parsed = getUrlFromContext(context, redirectTo)\n var { hostname, pathname } = parsed\n\n const contextPath = `/${type}/${subdomain}`\n const match = pathname.match(new RegExp(`^/${type}/([^\\/]+)`))\n\n if (match) {\n parsed.pathname = pathname.replace(match[0], contextPath)\n } else {\n parsed.pathname = `${contextPath}${pathname}`\n }\n\n return parsed.toString()\n}\n\n/**\n * Finds a subdomain from the given path.\n *\n * @param context {Object} An object containing the current request context information.\n * @param path {string} The path to search in.\n * @returns {string} The found subdomain.\n */\nexport function findSubdomainFromPath(context, path) {\n if (fixed) {\n return fixed\n }\n\n var parsed = getUrlFromContext(context, path)\n var { pathname } = parsed\n\n const match = pathname.match(new RegExp(`^/${getSubdomainTypeFromURL(context)}/([^\\/]+)`))\n return match && match[1]\n}\n\n/**\n * Retrieves the route prefix for a specific domain type.\n *\n * @returns {string} The route prefix associated with the domain type.\n */\nexport function getRoutePrefixForDomainType() {\n return routePrefixForDomainType\n}\n\n/**\n * Generates a site root path based on the current environment.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The generated site root path.\n */\nexport function getSiteRootPath(context) {\n return '/'\n}\n"]}
|
|
1
|
+
{"version":3,"file":"get-domain.js","sourceRoot":"","sources":["../../server/utils/get-domain.ts"],"names":[],"mappings":"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","sourcesContent":["import { URL } from 'url'\n\nimport { config } from '@things-factory/env'\nimport { getPathInfo } from '@things-factory/utils'\n\nimport { getRepository } from '../initializers/database'\nimport { Domain } from '../service/domain/domain'\n\nconst protocol: string = config.get('protocol')\nconst fixed = config.get('subdomain')\nconst domainTypes = config.get('domainTypes') || ['domain']\n\n/* 하위 호환성을 위해서, domain prefix가 없는 경우, domain을 추가해준다. */\nif (domainTypes.indexOf('domain') < 0) {\n domainTypes.push('domain')\n}\n\nconst routePrefixForDomainType = `:domainType(${domainTypes.join('|')})`\n\n/**\n * Creates a URL based on the given context and path.\n *\n * @param context {Object} An object containing the current request context information.\n * @param path {string} The path to be added to the created URL (optional).\n * @returns {URL} The generated URL object.\n */\nexport function getUrlFromContext(context, path = '') {\n const { method, href, host, header } = context\n const { referer } = header || {}\n\n var base = href\n\n if (method !== 'GET' && referer) {\n var { host: refererHost } = new URL(referer)\n if (refererHost === host) {\n base = referer\n }\n }\n\n let url: URL = new URL(path, base)\n\n const originalProtocol = context.headers['x-forwarded-proto']\n const originalHost = context.headers['x-forwarded-host']\n const originalPort = context.headers['x-forwarded-port']\n\n if (protocol || originalProtocol) {\n url.protocol = protocol || originalProtocol\n }\n\n if (originalHost) {\n url.host = originalHost\n if (originalPort) {\n url.port = originalPort\n }\n }\n\n return url\n}\n\n/**\n * Extracts a subdomain from the path.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The extracted subdomain.\n */\nfunction getSubdomainFromPath(context: any) {\n var { path } = context\n\n var subdomain = getPathInfo(path || '', domainTypes)?.subdomain\n if (subdomain) {\n return subdomain\n }\n\n var {\n header: { referer }\n } = context\n\n if (referer) {\n var { pathname } = new URL(referer)\n return getPathInfo(pathname || '', domainTypes)?.subdomain\n }\n}\n\n/**\n * Extracts the subdomain type from the given context's path or referer header.\n *\n * @param context - The context object containing path and header information.\n * @returns The subdomain type if found, otherwise undefined.\n */\nfunction getSubdomainTypeFromPath(context: any) {\n var { path } = context\n\n var prefix = getPathInfo(path || '', domainTypes)?.prefix\n if (prefix) {\n return prefix\n }\n\n var {\n header: { referer }\n } = context\n\n if (referer) {\n var { pathname } = new URL(referer)\n return getPathInfo(pathname || '', domainTypes)?.prefix\n }\n}\n\n/**\n * Extracts a subdomain from the URL context.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The extracted subdomain.\n */\nfunction getSubdomainFromURL(context) {\n return fixed || getSubdomainFromPath(context)\n}\n\nfunction getSubdomainTypeFromURL(context) {\n return fixed ? '' : getSubdomainTypeFromPath(context)\n}\n\n/**\n * Asynchronously searches for a domain object based on the URL context.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {Promise<Domain>} A promise that resolves to the domain object.\n */\nexport async function getDomainFromURL(context: any): Promise<Domain> {\n const { header } = context\n\n const subdomain = header['x-things-factory-domain'] || getSubdomainFromURL(context)\n const extType = header['x-things-factory-type'] || getSubdomainTypeFromURL(context)\n\n if (subdomain) {\n return await getRepository(Domain).findOne({\n where:\n fixed || extType == 'domain' || !extType\n ? {\n subdomain\n }\n : {\n subdomain,\n extType\n },\n cache: true\n })\n }\n}\n\n/**\n * Generates a context path based on the subdomain.\n *\n * @param subdomain {string} The subdomain.\n * @returns {string} The generated context path.\n */\nexport function getContextPath(domain: Domain) {\n const type = domain?.extType || 'domain'\n return fixed || !domain ? '' : `${type}/${domain?.subdomain}/`\n}\n\n/**\n * Generates a redirection path considering the subdomain.\n *\n * @param context {Object} An object containing the current request context information.\n * @param subdomain {Domain} The target domain.\n * @param redirectTo {string} The path to redirect to (optional).\n * @returns {string} The generated redirection path.\n */\nexport function getRedirectSubdomainPath(context, domain: Partial<Domain>, redirectTo: string = '/') {\n if (fixed || !domain) {\n return redirectTo || '/'\n }\n\n const type = domain.extType || 'domain'\n const subdomain = domain.subdomain || fixed\n\n var parsed = getUrlFromContext(context, redirectTo)\n var { hostname, pathname } = parsed\n\n const contextPath = `/${type}/${subdomain}`\n const match = pathname.match(new RegExp(`^/${type}/([^\\/]+)`))\n\n if (match) {\n parsed.pathname = pathname.replace(match[0], contextPath)\n } else {\n parsed.pathname = `${contextPath}${pathname}`\n }\n\n return parsed.toString()\n}\n\n/**\n * Finds a subdomain from the given path.\n *\n * @param context {Object} An object containing the current request context information.\n * @param path {string} The path to search in.\n * @returns {string} The found subdomain.\n */\nexport function findSubdomainFromPath(context, path) {\n if (fixed) {\n return fixed\n }\n\n var parsed = getUrlFromContext(context, path)\n var { pathname } = parsed\n\n // 도메인 타입 prefix 를 현재 요청 경로(context)가 아니라 대상 path(예: redirect_to) 에서\n // 직접 파싱한다. checkin 시점의 요청 경로는 '/auth/checkin' 이라 prefix 가 비어\n // 정규식이 '^//(...)' 가 되어 '/project/2026-00019/...' 같은 redirect_to 와 매칭 실패하던 문제 수정.\n // getPathInfo 는 domainTypes(예: ['domain','project']) 중 어떤 prefix 든 인식한다.\n return getPathInfo(pathname, domainTypes)?.subdomain\n}\n\n/**\n * Retrieves the route prefix for a specific domain type.\n *\n * @returns {string} The route prefix associated with the domain type.\n */\nexport function getRoutePrefixForDomainType() {\n return routePrefixForDomainType\n}\n\n/**\n * Generates a site root path based on the current environment.\n *\n * @param context {Object} An object containing the current request context information.\n * @returns {string} The generated site root path.\n */\nexport function getSiteRootPath(context) {\n return '/'\n}\n"]}
|
|
@@ -2,16 +2,45 @@ import { Repository, SelectQueryBuilder } from 'typeorm';
|
|
|
2
2
|
import { ListParam } from '../service/common-types/list-param.js';
|
|
3
3
|
import { Domain } from '../service/domain/domain.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* `search` 연산자의 매칭 방식.
|
|
6
|
+
* - contains(기본): `%v%` — 어디든 포함(유연하나 인덱스 미활용).
|
|
7
|
+
* - startsWith: `v%` — 접두 일치(인덱스 활용 가능 → 대용량에서 빠름).
|
|
8
|
+
* - exact: `v` — 정확 일치(대소문자 무시).
|
|
9
|
+
*/
|
|
10
|
+
export type SearchMode = 'contains' | 'startsWith' | 'exact';
|
|
11
|
+
/**
|
|
12
|
+
* Builds a TypeORM SelectQueryBuilder from a generic ListParam (filters, search, sortings,
|
|
13
|
+
* pagination) plus optional multi-tenant domain scoping. Returns the builder so callers can
|
|
14
|
+
* further chain joins/select/where before executing (e.g. getMany / getManyAndCount).
|
|
15
|
+
*
|
|
16
|
+
* Supported filter operators (`Filter.operator`):
|
|
17
|
+
* eq, noteq, like, nlike, i_like, i_nlike, search, lt, gt, lte, gte,
|
|
18
|
+
* in, notin, notin_with_null, between,
|
|
19
|
+
* is_null, is_not_null, is_true, is_false, is_not_true, is_not_false,
|
|
20
|
+
* is_present, is_blank, is_empty_num_id.
|
|
21
|
+
* An unknown operator throws a clear, actionable error.
|
|
22
|
+
*
|
|
23
|
+
* Behavior notes:
|
|
24
|
+
* - `search` uses case-insensitive substring match (`LOWER(col) LIKE %v%`) and requires the column
|
|
25
|
+
* to be listed in `searchables`.
|
|
26
|
+
* - `like`/`i_like` filters are IGNORED (with a console.warn) unless the column is in `searchables`,
|
|
27
|
+
* to avoid unindexed full-table scans.
|
|
28
|
+
* - Boolean operators are emitted as parameter-bound comparisons (portable across all supported DBs).
|
|
29
|
+
* - Domain scoping is applied only when `domain` is provided, honoring `params.inherited`
|
|
30
|
+
* (None = own domain, Include = own + parent, Only = parent).
|
|
31
|
+
* - Pagination uses skip/take; with joins this triggers TypeORM's two-query strategy for correctness.
|
|
32
|
+
* - Unknown filter columns / operators / relations throw a clear, actionable error (fail-fast).
|
|
6
33
|
*
|
|
7
|
-
* @param options
|
|
8
|
-
* @param options.
|
|
9
|
-
* @param options.
|
|
10
|
-
* @param [options.
|
|
11
|
-
* @param [options.
|
|
12
|
-
* @param [options.
|
|
13
|
-
*
|
|
14
|
-
* @
|
|
34
|
+
* @param options.repository TypeORM repository (or a tx-scoped repository) to build from.
|
|
35
|
+
* @param options.params ListParam: `{ filters, sortings, pagination, inherited }`.
|
|
36
|
+
* @param [options.domain] Optional current domain `{ id, parentId }` for tenant scoping.
|
|
37
|
+
* @param [options.alias] Optional entity alias (defaults to the entity name).
|
|
38
|
+
* @param [options.searchables] Columns eligible for `search`/`like` filtering.
|
|
39
|
+
* @param [options.filtersMap] Maps a filter/sort name to a column or relation column, e.g.
|
|
40
|
+
* `{ ownerName: { columnName: 'name', relationColumn: 'owner' } }`.
|
|
41
|
+
* @param [options.searchMode] `search` matching mode: 'contains'(default) / 'startsWith' / 'exact'.
|
|
42
|
+
* 'startsWith' emits `v%` (prefix) which can use an index on large tables.
|
|
43
|
+
* @returns {SelectQueryBuilder<Type>} The constructed SelectQueryBuilder (not yet executed).
|
|
15
44
|
*/
|
|
16
45
|
export declare function getQueryBuilderFromListParams<Type>(options: {
|
|
17
46
|
repository: Repository<Type>;
|
|
@@ -25,4 +54,5 @@ export declare function getQueryBuilderFromListParams<Type>(options: {
|
|
|
25
54
|
relationColumn?: string;
|
|
26
55
|
};
|
|
27
56
|
};
|
|
57
|
+
searchMode?: SearchMode;
|
|
28
58
|
}): SelectQueryBuilder<Type>;
|
|
@@ -3,20 +3,43 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getQueryBuilderFromListParams = getQueryBuilderFromListParams;
|
|
4
4
|
const typeorm_1 = require("typeorm");
|
|
5
5
|
const list_param_js_1 = require("../service/common-types/list-param.js");
|
|
6
|
+
const apply_domain_filter_js_1 = require("./apply-domain-filter.js");
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
+
* Builds a TypeORM SelectQueryBuilder from a generic ListParam (filters, search, sortings,
|
|
9
|
+
* pagination) plus optional multi-tenant domain scoping. Returns the builder so callers can
|
|
10
|
+
* further chain joins/select/where before executing (e.g. getMany / getManyAndCount).
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
12
|
+
* Supported filter operators (`Filter.operator`):
|
|
13
|
+
* eq, noteq, like, nlike, i_like, i_nlike, search, lt, gt, lte, gte,
|
|
14
|
+
* in, notin, notin_with_null, between,
|
|
15
|
+
* is_null, is_not_null, is_true, is_false, is_not_true, is_not_false,
|
|
16
|
+
* is_present, is_blank, is_empty_num_id.
|
|
17
|
+
* An unknown operator throws a clear, actionable error.
|
|
18
|
+
*
|
|
19
|
+
* Behavior notes:
|
|
20
|
+
* - `search` uses case-insensitive substring match (`LOWER(col) LIKE %v%`) and requires the column
|
|
21
|
+
* to be listed in `searchables`.
|
|
22
|
+
* - `like`/`i_like` filters are IGNORED (with a console.warn) unless the column is in `searchables`,
|
|
23
|
+
* to avoid unindexed full-table scans.
|
|
24
|
+
* - Boolean operators are emitted as parameter-bound comparisons (portable across all supported DBs).
|
|
25
|
+
* - Domain scoping is applied only when `domain` is provided, honoring `params.inherited`
|
|
26
|
+
* (None = own domain, Include = own + parent, Only = parent).
|
|
27
|
+
* - Pagination uses skip/take; with joins this triggers TypeORM's two-query strategy for correctness.
|
|
28
|
+
* - Unknown filter columns / operators / relations throw a clear, actionable error (fail-fast).
|
|
29
|
+
*
|
|
30
|
+
* @param options.repository TypeORM repository (or a tx-scoped repository) to build from.
|
|
31
|
+
* @param options.params ListParam: `{ filters, sortings, pagination, inherited }`.
|
|
32
|
+
* @param [options.domain] Optional current domain `{ id, parentId }` for tenant scoping.
|
|
33
|
+
* @param [options.alias] Optional entity alias (defaults to the entity name).
|
|
34
|
+
* @param [options.searchables] Columns eligible for `search`/`like` filtering.
|
|
35
|
+
* @param [options.filtersMap] Maps a filter/sort name to a column or relation column, e.g.
|
|
36
|
+
* `{ ownerName: { columnName: 'name', relationColumn: 'owner' } }`.
|
|
37
|
+
* @param [options.searchMode] `search` matching mode: 'contains'(default) / 'startsWith' / 'exact'.
|
|
38
|
+
* 'startsWith' emits `v%` (prefix) which can use an index on large tables.
|
|
39
|
+
* @returns {SelectQueryBuilder<Type>} The constructed SelectQueryBuilder (not yet executed).
|
|
17
40
|
*/
|
|
18
41
|
function getQueryBuilderFromListParams(options) {
|
|
19
|
-
const { repository, params, domain, alias, searchables, filtersMap = {} } = options;
|
|
42
|
+
const { repository, params, domain, alias, searchables, filtersMap = {}, searchMode = 'contains' } = options;
|
|
20
43
|
const { inherited = list_param_js_1.InheritedValueType.None } = params || {};
|
|
21
44
|
const selectQueryBuilder = repository.createQueryBuilder(alias);
|
|
22
45
|
const entityAlias = selectQueryBuilder.alias;
|
|
@@ -49,33 +72,20 @@ function getQueryBuilderFromListParams(options) {
|
|
|
49
72
|
// Apply column filters
|
|
50
73
|
if (columnFilters.length > 0) {
|
|
51
74
|
columnFilters.forEach(filter => {
|
|
52
|
-
addCondition(metadata, selectQueryBuilder, selectQueryBuilder, filter, filtersMap, true);
|
|
75
|
+
addCondition(metadata, selectQueryBuilder, selectQueryBuilder, filter, filtersMap, true, searchMode);
|
|
53
76
|
});
|
|
54
77
|
}
|
|
55
78
|
// Apply search filters
|
|
56
79
|
if (searchFilters.length > 0) {
|
|
57
80
|
selectQueryBuilder.andWhere(new typeorm_1.Brackets(qb => {
|
|
58
81
|
searchFilters.forEach(filter => {
|
|
59
|
-
addCondition(metadata, selectQueryBuilder, qb, filter, filtersMap, false);
|
|
82
|
+
addCondition(metadata, selectQueryBuilder, qb, filter, filtersMap, false, searchMode);
|
|
60
83
|
});
|
|
61
84
|
}));
|
|
62
85
|
}
|
|
63
86
|
// Apply domain filters
|
|
64
87
|
if (domain) {
|
|
65
|
-
|
|
66
|
-
selectQueryBuilder.andWhere(`${entityAlias}.domain = :domain`, { domain: domain.id });
|
|
67
|
-
}
|
|
68
|
-
else if (inherited === list_param_js_1.InheritedValueType.Include) {
|
|
69
|
-
selectQueryBuilder.andWhere(`${entityAlias}.domain IN (:...domains)`, {
|
|
70
|
-
domains: [domain.id, domain.parentId].filter(Boolean)
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
else if (inherited === list_param_js_1.InheritedValueType.Only) {
|
|
74
|
-
selectQueryBuilder.andWhere(`${entityAlias}.domain = :domain`, { domain: domain.parentId || 'Impossible' });
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
selectQueryBuilder.andWhere(`${entityAlias}.domain = :domain`, { domain: 'Impossible' });
|
|
78
|
-
}
|
|
88
|
+
(0, apply_domain_filter_js_1.applyDomainInheritanceFilter)(selectQueryBuilder, entityAlias, domain, inherited);
|
|
79
89
|
}
|
|
80
90
|
// Apply pagination
|
|
81
91
|
addPagination(selectQueryBuilder, pagination);
|
|
@@ -112,7 +122,7 @@ function addPagination(selectQueryBuilder, pagination) {
|
|
|
112
122
|
* @param filtersMap - A mapping of filter names to column names and relation column names.
|
|
113
123
|
* @param andCondition - A flag indicating whether to use "AND" or "OR" for combining conditions.
|
|
114
124
|
*/
|
|
115
|
-
function addCondition(metadata, selectQueryBuilder, whereExpressionBuilder, filter, filtersMap = {}, andCondition) {
|
|
125
|
+
function addCondition(metadata, selectQueryBuilder, whereExpressionBuilder, filter, filtersMap = {}, andCondition, searchMode = 'contains') {
|
|
116
126
|
const { name, operator, value } = filter;
|
|
117
127
|
var entityAlias = selectQueryBuilder.alias;
|
|
118
128
|
var { relationColumn, columnName } = filtersMap[name] || {};
|
|
@@ -166,9 +176,12 @@ function addCondition(metadata, selectQueryBuilder, whereExpressionBuilder, filt
|
|
|
166
176
|
columnMeta = relationIdMeta.relation.joinColumns[0];
|
|
167
177
|
}
|
|
168
178
|
else {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
179
|
+
/* 미지의 컬럼: 이전엔 warn 후 흘러가 columnMeta.databaseName 에서 불투명 TypeError 로 크래시했다.
|
|
180
|
+
원인을 바로 알 수 있도록 명확·실행가능한 오류를 던진다(fail-fast). */
|
|
181
|
+
const target = columnName || name;
|
|
182
|
+
const available = metadata.columns.map(column => column.propertyName).join(', ');
|
|
183
|
+
throw new Error(`[getQueryBuilderFromListParams] unknown filter column "${target}" on entity "${metadata.name}". ` +
|
|
184
|
+
`Add a filtersMap entry for "${name}", or use one of: ${available}`);
|
|
172
185
|
}
|
|
173
186
|
}
|
|
174
187
|
else {
|
|
@@ -191,7 +204,7 @@ function addCondition(metadata, selectQueryBuilder, whereExpressionBuilder, filt
|
|
|
191
204
|
const alias = relationColumnMeta ? `${name}-filter` : entityAlias;
|
|
192
205
|
/* relation columne인 경우 name을 alias로 사용한다. */
|
|
193
206
|
const field = `${alias}.${dbNameForColumn}`;
|
|
194
|
-
var { clause, parameters } = getClauseAndParameters(field, name, operator, value);
|
|
207
|
+
var { clause, parameters } = getClauseAndParameters(field, name, operator, value, searchMode);
|
|
195
208
|
if (relationColumnMeta) {
|
|
196
209
|
const { propertyName } = relationColumnMeta;
|
|
197
210
|
const property = `${entityAlias}.${propertyName}`;
|
|
@@ -272,7 +285,9 @@ function applyJoins(selectQueryBuilder, entityAlias, relationColumn, metadata, j
|
|
|
272
285
|
for (const column of columns) {
|
|
273
286
|
const relation = currentMetadata.relations.find(rel => rel.propertyName === column);
|
|
274
287
|
if (!relation) {
|
|
275
|
-
|
|
288
|
+
const available = currentMetadata.relations.map(rel => rel.propertyName).join(', ');
|
|
289
|
+
throw new Error(`[getQueryBuilderFromListParams] relation "${column}" not found on entity "${currentMetadata.name}"` +
|
|
290
|
+
` (relationColumn "${relationColumn}"). Available relations: ${available}`);
|
|
276
291
|
}
|
|
277
292
|
const nextAlias = `${currentAlias}_${relation.inverseEntityMetadata.tableName}_for_${columnName}`;
|
|
278
293
|
if (!selectQueryBuilder.expressionMap.aliases.some(alias => alias.name === nextAlias)) {
|
|
@@ -286,6 +301,18 @@ function applyJoins(selectQueryBuilder, entityAlias, relationColumn, metadata, j
|
|
|
286
301
|
}
|
|
287
302
|
return currentAlias;
|
|
288
303
|
}
|
|
304
|
+
/** search 값에 searchMode 에 맞는 와일드카드를 씌운다. */
|
|
305
|
+
function wrapSearch(value, mode) {
|
|
306
|
+
switch (mode) {
|
|
307
|
+
case 'startsWith':
|
|
308
|
+
return `${value}%`;
|
|
309
|
+
case 'exact':
|
|
310
|
+
return value;
|
|
311
|
+
case 'contains':
|
|
312
|
+
default:
|
|
313
|
+
return `%${value}%`;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
289
316
|
/**
|
|
290
317
|
* Generates the SQL clause and parameters based on the provided filter.
|
|
291
318
|
*
|
|
@@ -295,7 +322,7 @@ function applyJoins(selectQueryBuilder, entityAlias, relationColumn, metadata, j
|
|
|
295
322
|
* @param value - The value to filter with.
|
|
296
323
|
* @returns An object containing the SQL clause and the parameters.
|
|
297
324
|
*/
|
|
298
|
-
function getClauseAndParameters(field, name, operator, value) {
|
|
325
|
+
function getClauseAndParameters(field, name, operator, value, searchMode = 'contains') {
|
|
299
326
|
const values = value instanceof Array ? value : [value];
|
|
300
327
|
let clause = '';
|
|
301
328
|
let parameters = {};
|
|
@@ -309,6 +336,10 @@ function getClauseAndParameters(field, name, operator, value) {
|
|
|
309
336
|
parameters = { [name]: `%${value}%` };
|
|
310
337
|
break;
|
|
311
338
|
case 'search':
|
|
339
|
+
/* searchMode 로 매칭 방식 선택(기본 contains). startsWith 는 접두 인덱스 활용 가능. */
|
|
340
|
+
clause = `LOWER(${field}) LIKE :${name}`;
|
|
341
|
+
parameters = { [name]: wrapSearch(String(value).toLowerCase(), searchMode) };
|
|
342
|
+
break;
|
|
312
343
|
case 'i_like':
|
|
313
344
|
clause = `LOWER(${field}) LIKE :${name}`;
|
|
314
345
|
parameters = { [name]: `%${String(value).toLowerCase()}%` };
|
|
@@ -359,31 +390,43 @@ function getClauseAndParameters(field, name, operator, value) {
|
|
|
359
390
|
case 'is_not_null':
|
|
360
391
|
clause = `${field} IS NOT NULL`;
|
|
361
392
|
break;
|
|
393
|
+
/* boolean 비교는 파라미터 바인딩으로 (IS TRUE/FALSE 는 MSSQL/Oracle 미지원 → 이식성 확보) */
|
|
362
394
|
case 'is_false':
|
|
363
|
-
clause = `${field}
|
|
395
|
+
clause = `${field} = :${name}`;
|
|
396
|
+
parameters = { [name]: false };
|
|
364
397
|
break;
|
|
365
398
|
case 'is_true':
|
|
366
|
-
clause = `${field}
|
|
399
|
+
clause = `${field} = :${name}`;
|
|
400
|
+
parameters = { [name]: true };
|
|
367
401
|
break;
|
|
368
402
|
case 'is_not_false':
|
|
369
|
-
|
|
403
|
+
/* NOT FALSE = true 또는 NULL */
|
|
404
|
+
clause = `(${field} IS NULL OR ${field} = :${name})`;
|
|
405
|
+
parameters = { [name]: true };
|
|
370
406
|
break;
|
|
371
407
|
case 'is_not_true':
|
|
372
|
-
|
|
408
|
+
/* NOT TRUE = false 또는 NULL */
|
|
409
|
+
clause = `(${field} IS NULL OR ${field} = :${name})`;
|
|
410
|
+
parameters = { [name]: false };
|
|
373
411
|
break;
|
|
374
412
|
case 'is_present':
|
|
375
|
-
clause = `${field} IS
|
|
413
|
+
clause = `${field} IS NOT NULL`;
|
|
376
414
|
break;
|
|
377
415
|
case 'is_blank':
|
|
378
|
-
clause = `${field} IS
|
|
416
|
+
clause = `${field} IS NULL`;
|
|
379
417
|
break;
|
|
380
418
|
case 'is_empty_num_id':
|
|
381
|
-
clause =
|
|
419
|
+
clause = `(${field} IS NULL OR ${field} = :${name})`;
|
|
420
|
+
parameters = { [name]: 0 };
|
|
382
421
|
break;
|
|
383
422
|
case 'between':
|
|
384
423
|
clause = `${field} BETWEEN :${name}_1 AND :${name}_2`;
|
|
385
424
|
parameters = { [`${name}_1`]: values[0], [`${name}_2`]: values[1] };
|
|
386
425
|
break;
|
|
426
|
+
default:
|
|
427
|
+
/* 지원하지 않는 연산자: 이전엔 clause='' 로 andWhere('') 가 되어 malformed/무음이었다.
|
|
428
|
+
명확한 오류로 원인을 알린다(fail-fast). */
|
|
429
|
+
throw new Error(`[getQueryBuilderFromListParams] unsupported filter operator "${operator}" for field "${field}".`);
|
|
387
430
|
}
|
|
388
431
|
return { clause, parameters };
|
|
389
432
|
}
|