@galadrim/galadmin 0.2.11 → 0.3.0-alpha.2.1
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/.agents/report/blockers.json +1 -0
- package/.agents/report/failures/2026-09-23-api-user-filter-contract.md +31 -0
- package/.agents/report/failures/2026-09-23-api-values-contract.md +25 -0
- package/.agents/report/failures/2026-10-01-cycle-02-create-callback-contract.md +17 -0
- package/.agents/report/failures/2026-10-01-cycle-06-delete-callback-id-contract.md +17 -0
- package/.agents/report/failures/2026-10-01-cycle-07-panel-delete-callback-id-contract.md +24 -0
- package/.agents/report/failures/2026-10-01-cycle-13-update-callback-id-contract.md +17 -0
- package/.agents/report/failures/2026-10-01-cycle-15-rich-text-upload-boundary-contract.md +24 -0
- package/.agents/report/failures/2026-10-03-cycle-06-record-import-parser-contract.md +11 -0
- package/.agents/report/failures/2026-10-03-cycle-07-panel-import-parser-contract.md +24 -0
- package/.agents/report/failures/2026-10-07-resume-cycle-02-api-assembly-contract.md +46 -0
- package/.agents/report/port/2026-09-23-api-config-enrichment.md +13 -0
- package/.agents/report/port/2026-09-23-api-file-upload.md +11 -0
- package/.agents/report/port/2026-09-23-api-query-authorization.md +14 -0
- package/.agents/report/port/2026-09-23-api-request-configuration.md +11 -0
- package/.agents/report/port/2026-09-23-database-manager.md +13 -0
- package/.agents/report/port/2026-10-01-api-adjacent-record-id.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-01-api-config-route.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-03-custom-action.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-04-custom-component.md +7 -0
- package/.agents/report/port/2026-10-01-cycle-05-custom-query.md +7 -0
- package/.agents/report/port/2026-10-01-cycle-08-bulk-delete.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-09-duplicate-record.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-10-notification-badges.md +9 -0
- package/.agents/report/port/2026-10-01-cycle-11-record.md +7 -0
- package/.agents/report/port/2026-10-01-cycle-12-search-companies.md +11 -0
- package/.agents/report/port/2026-10-01-cycle-14-upload-file.md +7 -0
- package/.agents/report/port/2026-10-03-cycle-01-create-record.md +11 -0
- package/.agents/report/port/2026-10-03-cycle-02-delete-record.md +11 -0
- package/.agents/report/port/2026-10-03-cycle-03-panel-delete-record.md +11 -0
- package/.agents/report/port/2026-10-03-cycle-04-update-record.md +11 -0
- package/.agents/report/port/2026-10-03-cycle-05-api-values.md +11 -0
- package/.agents/report/port/2026-10-06-cycle-01-record-import.md +9 -0
- package/.agents/report/port/2026-10-06-cycle-03-rich-text-upload.md +7 -0
- package/.agents/report/port/2026-10-06-cycle-04-user-filters.md +11 -0
- package/.agents/report/port/2026-10-06-resume-cycle-01-panel-import.md +9 -0
- package/.agents/report/port/2026-10-07-cycle-01-api-assembly.md +16 -0
- package/.agents/report/port/2026-10-07-cycle-02-index-entry-point.md +13 -0
- package/.agents/report/remediation/2026-10-02-api-user-filter-remediation.md +71 -0
- package/.agents/report/remediation/2026-10-02-api-values-remediation.md +17 -0
- package/.agents/report/remediation/2026-10-03-callback-id-contract-remediation.md +36 -0
- package/.agents/report/remediation/2026-10-06-import-parser-remediation.md +41 -0
- package/.agents/report/remediation/2026-10-06-rich-text-upload-remediation.md +33 -0
- package/.agents/report/remediation/2026-10-07-api-assembly-remediation.md +32 -0
- package/.agents/skills/port-javascript-to-typescript/SKILL.md +42 -0
- package/.agents/skills/port-javascript-to-typescript/agents/openai.yaml +3 -0
- package/.agents/skills/port-javascript-to-typescript/references/blockers.md +31 -0
- package/.agents/skills/port-javascript-to-typescript/references/planning.md +43 -0
- package/.agents/skills/port-javascript-to-typescript/references/porting.md +9 -0
- package/.agents/skills/port-javascript-to-typescript/references/reporting.md +24 -0
- package/.agents/skills/port-javascript-to-typescript/references/rules.md +8 -0
- package/.agents/skills/port-javascript-to-typescript/references/selection.md +16 -0
- package/.agents/skills/port-javascript-to-typescript/references/tools.md +20 -0
- package/.agents/skills/port-javascript-to-typescript/scripts/check.sh +17 -0
- package/.agents/skills/port-javascript-to-typescript/scripts/graph.sh +30 -0
- package/.agents/skills/port-javascript-to-typescript/scripts/madge.json +12 -0
- package/.agents/skills/port-javascript-to-typescript/scripts/oxlint.json +14 -0
- package/.agents/skills/port-javascript-to-typescript/scripts/test-tools.cjs +76 -0
- package/.github/actions/version-bump/action.yml +41 -0
- package/.github/workflows/alpha.yml +53 -0
- package/.github/workflows/ci.yml +8 -9
- package/.github/workflows/main.yml +13 -14
- package/AGENTS.md +5 -0
- package/dist/api/authorization.d.ts +38 -0
- package/dist/api/authorization.js +74 -0
- package/dist/api/authorization.js.map +1 -0
- package/dist/api/config.d.ts +42 -0
- package/dist/api/config.js +105 -0
- package/dist/api/config.js.map +1 -0
- package/dist/api/filterUser.d.ts +2 -0
- package/dist/api/filterUser.js +24 -0
- package/dist/api/filterUser.js.map +1 -0
- package/dist/api/importRecords.d.ts +1 -0
- package/dist/api/importRecords.js +16 -0
- package/dist/api/importRecords.js.map +1 -0
- package/dist/api/queries.d.ts +48 -0
- package/dist/api/queries.js +139 -0
- package/dist/api/queries.js.map +1 -0
- package/dist/api/request.d.ts +29 -0
- package/dist/api/request.js +3 -0
- package/dist/api/request.js.map +1 -0
- package/dist/api/routes/adjacentRecordId.d.ts +10 -0
- package/dist/api/routes/adjacentRecordId.js +13 -0
- package/dist/api/routes/adjacentRecordId.js.map +1 -0
- package/dist/api/routes/config.d.ts +3 -0
- package/dist/api/routes/config.js +27 -0
- package/dist/api/routes/config.js.map +1 -0
- package/dist/api/routes/createRecord.d.ts +19 -0
- package/dist/api/routes/createRecord.js +93 -0
- package/dist/api/routes/createRecord.js.map +1 -0
- package/dist/api/routes/customAction.d.ts +17 -0
- package/dist/api/routes/customAction.js +35 -0
- package/dist/api/routes/customAction.js.map +1 -0
- package/dist/api/routes/customComponent.d.ts +3 -0
- package/dist/api/routes/customComponent.js +34 -0
- package/dist/api/routes/customComponent.js.map +1 -0
- package/dist/api/routes/customQuery.d.ts +7 -0
- package/dist/api/routes/customQuery.js +33 -0
- package/dist/api/routes/customQuery.js.map +1 -0
- package/dist/api/routes/deleteRecord.d.ts +9 -0
- package/dist/api/routes/deleteRecord.js +46 -0
- package/dist/api/routes/deleteRecord.js.map +1 -0
- package/dist/api/routes/deleteRecordFromPanel.d.ts +10 -0
- package/dist/api/routes/deleteRecordFromPanel.js +36 -0
- package/dist/api/routes/deleteRecordFromPanel.js.map +1 -0
- package/dist/api/routes/deleteRecords.d.ts +9 -0
- package/dist/api/routes/deleteRecords.js +12 -0
- package/dist/api/routes/deleteRecords.js.map +1 -0
- package/dist/api/routes/duplicateRecord.d.ts +17 -0
- package/dist/api/routes/duplicateRecord.js +55 -0
- package/dist/api/routes/duplicateRecord.js.map +1 -0
- package/dist/api/routes/foreignSelectRecords.d.ts +10 -0
- package/dist/api/routes/foreignSelectRecords.js +61 -0
- package/dist/api/routes/foreignSelectRecords.js.map +1 -0
- package/dist/api/routes/notificationBadges.d.ts +6 -0
- package/dist/api/routes/notificationBadges.js +31 -0
- package/dist/api/routes/notificationBadges.js.map +1 -0
- package/dist/api/routes/record.d.ts +7 -0
- package/dist/api/routes/record.js +32 -0
- package/dist/api/routes/record.js.map +1 -0
- package/dist/api/routes/records.d.ts +14 -0
- package/dist/api/routes/records.js +57 -0
- package/dist/api/routes/records.js.map +1 -0
- package/dist/api/routes/recordsExport.d.ts +13 -0
- package/dist/api/routes/recordsExport.js +67 -0
- package/dist/api/routes/recordsExport.js.map +1 -0
- package/dist/api/routes/recordsImport.d.ts +4 -0
- package/dist/api/routes/recordsImport.js +100 -0
- package/dist/api/routes/recordsImport.js.map +1 -0
- package/dist/api/routes/searchCompanies.d.ts +17 -0
- package/dist/api/routes/searchCompanies.js +72 -0
- package/dist/api/routes/searchCompanies.js.map +1 -0
- package/dist/api/routes/subrecordsImport.d.ts +4 -0
- package/dist/api/routes/subrecordsImport.js +94 -0
- package/dist/api/routes/subrecordsImport.js.map +1 -0
- package/dist/api/routes/updateRecord.d.ts +13 -0
- package/dist/api/routes/updateRecord.js +55 -0
- package/dist/api/routes/updateRecord.js.map +1 -0
- package/dist/api/routes/uploadFile.d.ts +3 -0
- package/dist/api/routes/uploadFile.js +13 -0
- package/dist/api/routes/uploadFile.js.map +1 -0
- package/dist/api/routes/uploadFileFromBetterRichTextInput.d.ts +3 -0
- package/dist/api/routes/uploadFileFromBetterRichTextInput.js +33 -0
- package/dist/api/routes/uploadFileFromBetterRichTextInput.js.map +1 -0
- package/dist/api/uploadFile.d.ts +4 -0
- package/dist/api/uploadFile.js +209 -0
- package/dist/api/uploadFile.js.map +1 -0
- package/dist/api/values.d.ts +6 -0
- package/dist/api/values.js +88 -0
- package/dist/api/values.js.map +1 -0
- package/dist/api.d.ts +8 -27
- package/dist/api.js +80 -1387
- package/dist/api.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.strict.tsbuildinfo +1 -0
- package/dist/types/config.d.ts +15 -5
- package/dist/types/config.js +4 -0
- package/dist/types/config.js.map +1 -1
- package/dist/types/database.d.ts +1 -0
- package/dist/types/database.js +3 -0
- package/dist/types/database.js.map +1 -0
- package/dist/utils/dbManager.d.ts +32 -7
- package/dist/utils/dbManager.js +59 -7
- package/dist/utils/dbManager.js.map +1 -1
- package/package.json +12 -3
- package/src/api/authorization.ts +129 -0
- package/src/api/config.ts +152 -0
- package/src/api/filterUser.ts +28 -0
- package/src/api/importRecords.ts +16 -0
- package/src/api/queries.ts +194 -0
- package/src/api/request.ts +31 -0
- package/src/api/routes/adjacentRecordId.ts +20 -0
- package/src/api/routes/config.ts +15 -0
- package/src/api/routes/createRecord.ts +131 -0
- package/src/api/routes/customAction.ts +60 -0
- package/src/api/routes/customComponent.ts +44 -0
- package/src/api/routes/customQuery.ts +53 -0
- package/src/api/routes/deleteRecord.ts +59 -0
- package/src/api/routes/deleteRecordFromPanel.ts +58 -0
- package/src/api/routes/deleteRecords.ts +28 -0
- package/src/api/routes/duplicateRecord.ts +73 -0
- package/src/api/routes/foreignSelectRecords.ts +80 -0
- package/src/api/routes/notificationBadges.ts +47 -0
- package/src/api/routes/record.ts +48 -0
- package/src/api/routes/records.ts +91 -0
- package/src/api/routes/recordsExport.ts +92 -0
- package/src/api/routes/recordsImport.ts +115 -0
- package/src/api/routes/searchCompanies.ts +96 -0
- package/src/api/routes/subrecordsImport.ts +114 -0
- package/src/api/routes/updateRecord.ts +74 -0
- package/src/api/routes/uploadFile.ts +16 -0
- package/src/api/routes/uploadFileFromBetterRichTextInput.ts +41 -0
- package/src/api/uploadFile.ts +249 -0
- package/src/api/values.ts +96 -0
- package/src/api.ts +94 -1580
- package/src/front/ckeditor5-build/node_modules/semver/bin/semver.js +0 -0
- package/src/index.ts +42 -25
- package/src/types/config.ts +15 -5
- package/src/types/database.ts +2 -0
- package/src/utils/dbManager.ts +99 -14
- package/tests/api-helpers.test.ts +59 -0
- package/tests/dbManager.test.ts +16 -1
- package/tests/helpers/fakeDb.ts +9 -7
- package/tests/integration/authorizationBoundary.integration.test.ts +57 -0
- package/tests/integration/configEnrichment.integration.test.ts +79 -0
- package/tests/integration/customComponentBoundary.integration.test.ts +58 -0
- package/tests/integration/dbManager.integration.test.ts +105 -3
- package/tests/integration/importParserChild.cjs +55 -0
- package/tests/integration/indexRuntime.integration.test.ts +22 -0
- package/tests/integration/indexRuntimeChild.cjs +110 -0
- package/tests/integration/queries.integration.test.ts +88 -0
- package/tests/integration/requestConfig.integration.test.ts +118 -0
- package/tests/integration/routes.authorization.integration.test.ts +1 -0
- package/tests/integration/routes.config.integration.test.ts +39 -1
- package/tests/integration/routes.crud.integration.test.ts +233 -1
- package/tests/integration/routes.custom.integration.test.ts +119 -0
- package/tests/integration/routes.foreign.integration.test.ts +3 -2
- package/tests/integration/routes.importExport.integration.test.ts +308 -6
- package/tests/integration/routes.integration.test.ts +4 -0
- package/tests/integration/routes.records.integration.test.ts +82 -6
- package/tests/integration/routes.upload.integration.test.ts +123 -0
- package/tests/integration/userFilters.integration.test.ts +225 -0
- package/tsconfig.json +1 -1
- package/tsconfig.strict.json +45 -0
- package/dist/front/ckeditor5-build/src/ckeditor.d.ts +0 -43
- package/dist/front/ckeditor5-build/src/ckeditor.js +0 -130
- package/dist/front/ckeditor5-build/src/ckeditor.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/front/ckeditor5-build/node_modules/.bin/semver +0 -12
- package/src/front/ckeditor5-build/node_modules/.bin/semver.cmd +0 -17
- package/src/front/ckeditor5-build/node_modules/.bin/semver.ps1 +0 -28
package/dist/types/config.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
+
import type { DatabaseId } from "./database";
|
|
1
2
|
export type Filter = {};
|
|
3
|
+
export type RootUser = {
|
|
4
|
+
role: "root";
|
|
5
|
+
};
|
|
6
|
+
export type RegularUser = {
|
|
7
|
+
role: string;
|
|
8
|
+
userId?: DatabaseId;
|
|
9
|
+
};
|
|
10
|
+
export type FilterUser = RootUser | RegularUser;
|
|
11
|
+
export declare function isRegularUser(user: FilterUser): user is RegularUser;
|
|
2
12
|
export type BaseTypeColumn = {
|
|
3
13
|
label: string;
|
|
4
14
|
hideInTable?: boolean;
|
|
@@ -81,7 +91,7 @@ export type ForeignSelectColumn = {
|
|
|
81
91
|
selectExpression?: string;
|
|
82
92
|
join?: string;
|
|
83
93
|
filters?: Filter[];
|
|
84
|
-
filtersFromUser?: (
|
|
94
|
+
filtersFromUser?: (user: FilterUser) => string;
|
|
85
95
|
orderBy?: string;
|
|
86
96
|
optional?: boolean;
|
|
87
97
|
formatForeignValue?: (record: any) => string;
|
|
@@ -96,7 +106,7 @@ export type ForeignDatalistColumn = {
|
|
|
96
106
|
selectExpression?: string;
|
|
97
107
|
join?: string;
|
|
98
108
|
filters?: Filter[];
|
|
99
|
-
filtersFromUser?: (
|
|
109
|
+
filtersFromUser?: (user: FilterUser) => string;
|
|
100
110
|
orderBy?: string;
|
|
101
111
|
optional?: boolean;
|
|
102
112
|
formatForeignValue?: (record: any) => string;
|
|
@@ -194,7 +204,7 @@ export type TableView = {
|
|
|
194
204
|
isDesc?: boolean;
|
|
195
205
|
limit?: number;
|
|
196
206
|
filters?: string;
|
|
197
|
-
filtersFromUser?: (
|
|
207
|
+
filtersFromUser?: (user: FilterUser) => string;
|
|
198
208
|
notClickable?: boolean;
|
|
199
209
|
clickToEdit?: boolean;
|
|
200
210
|
openInNewTab?: boolean;
|
|
@@ -242,7 +252,7 @@ export type BaseTypePanel = {
|
|
|
242
252
|
label: string;
|
|
243
253
|
folded?: boolean;
|
|
244
254
|
displayCondition?: (record: any) => boolean;
|
|
245
|
-
}
|
|
255
|
+
} & Pick<BaseTypeView, "roles">;
|
|
246
256
|
export type CreateConfig = {
|
|
247
257
|
label: string;
|
|
248
258
|
recordIdKey: string;
|
|
@@ -361,7 +371,7 @@ export type AuthRole = {
|
|
|
361
371
|
};
|
|
362
372
|
export type TrustedUser = {
|
|
363
373
|
role: string;
|
|
364
|
-
userId?:
|
|
374
|
+
userId?: DatabaseId;
|
|
365
375
|
};
|
|
366
376
|
export type Config = {
|
|
367
377
|
enriched?: boolean;
|
package/dist/types/config.js
CHANGED
package/dist/types/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/types/config.ts"],"names":[],"mappings":";;AAQA,sCAEC;AAFD,SAAgB,aAAa,CAAC,IAAgB;IAC5C,OAAO,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type DatabaseId = string | number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/types/database.ts"],"names":[],"mappings":""}
|
|
@@ -1,12 +1,37 @@
|
|
|
1
|
+
import * as mysql from "mysql";
|
|
2
|
+
import * as mysql2 from "mysql2";
|
|
3
|
+
import * as pg from "pg";
|
|
4
|
+
type DatabaseValue = string | number | bigint | boolean | null | Date | Buffer | DatabaseValue[] | DatabaseRow;
|
|
5
|
+
export type DatabaseRow = {
|
|
6
|
+
[column: string]: DatabaseValue;
|
|
7
|
+
};
|
|
8
|
+
type MutationResult = Pick<mysql2.ResultSetHeader, "insertId" | "affectedRows">;
|
|
9
|
+
type QueryRows = DatabaseRow[] | MutationResult | QueryRows[];
|
|
10
|
+
type QueryField = Pick<pg.FieldDef, "name">;
|
|
11
|
+
type QueryFields = (QueryField | QueryField[] | undefined)[];
|
|
12
|
+
type QueryResult = {
|
|
13
|
+
err: Error | null;
|
|
14
|
+
rows: QueryRows | undefined;
|
|
15
|
+
fields: QueryFields | undefined;
|
|
16
|
+
};
|
|
17
|
+
type DatabaseConfig = {
|
|
18
|
+
useMysql2?: boolean;
|
|
19
|
+
mysql?: Pick<mysql.PoolConfig, "host" | "user" | "password" | "database" | "port" | "multipleStatements">;
|
|
20
|
+
postgresql?: Pick<pg.PoolConfig, "host" | "user" | "password" | "database" | "port" | "ssl">;
|
|
21
|
+
};
|
|
22
|
+
type DatabasePool = {
|
|
23
|
+
query(sql: string, values: mysql2.QueryValues[], callback: (error: Error | null | undefined, result: unknown, fields?: unknown) => void): void;
|
|
24
|
+
};
|
|
1
25
|
export declare function quoteIdentifier(name: string, dbType: string): string;
|
|
2
|
-
export declare function convertQuery(mysqlQuery:
|
|
26
|
+
export declare function convertQuery(mysqlQuery: string, params: mysql2.QueryValues[]): {
|
|
3
27
|
pgQuery: string;
|
|
4
|
-
pgParams:
|
|
28
|
+
pgParams: mysql2.QueryValues[];
|
|
5
29
|
};
|
|
6
|
-
export declare function quotePostgresQuery(query:
|
|
30
|
+
export declare function quotePostgresQuery(query: string): string;
|
|
7
31
|
export default class dbManager {
|
|
8
|
-
_db
|
|
9
|
-
dbType
|
|
10
|
-
init(config:
|
|
11
|
-
query(queryString:
|
|
32
|
+
_db?: DatabasePool;
|
|
33
|
+
dbType?: "MYSQL" | "POSTGRESQL";
|
|
34
|
+
init(config: DatabaseConfig): void;
|
|
35
|
+
query(queryString: string, args?: mysql2.QueryValues[]): Promise<QueryResult>;
|
|
12
36
|
}
|
|
37
|
+
export {};
|
package/dist/utils/dbManager.js
CHANGED
|
@@ -6,6 +6,53 @@ exports.quotePostgresQuery = quotePostgresQuery;
|
|
|
6
6
|
const mysql = require("mysql");
|
|
7
7
|
const mysql2 = require("mysql2");
|
|
8
8
|
const pg = require("pg");
|
|
9
|
+
function isDatabaseValue(value) {
|
|
10
|
+
return value === null || typeof value === "string" || typeof value === "number"
|
|
11
|
+
|| typeof value === "bigint" || typeof value === "boolean"
|
|
12
|
+
|| value instanceof Date || Buffer.isBuffer(value)
|
|
13
|
+
|| (Array.isArray(value) ? value.every(isDatabaseValue)
|
|
14
|
+
: typeof value === "object" && Object.values(value).every(isDatabaseValue));
|
|
15
|
+
}
|
|
16
|
+
function isDatabaseRow(value) {
|
|
17
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
18
|
+
&& !(value instanceof Date) && !Buffer.isBuffer(value)
|
|
19
|
+
&& Object.values(value).every(isDatabaseValue);
|
|
20
|
+
}
|
|
21
|
+
function isQueryRows(value) {
|
|
22
|
+
return Array.isArray(value)
|
|
23
|
+
? value.every(isDatabaseRow) || value.every(isQueryRows)
|
|
24
|
+
: value !== null && typeof value === "object"
|
|
25
|
+
&& "insertId" in value && typeof value.insertId === "number"
|
|
26
|
+
&& "affectedRows" in value && typeof value.affectedRows === "number";
|
|
27
|
+
}
|
|
28
|
+
function isQueryField(value) {
|
|
29
|
+
return value !== null && typeof value === "object" && "name" in value && typeof value.name === "string";
|
|
30
|
+
}
|
|
31
|
+
function isQueryFields(value) {
|
|
32
|
+
return Array.isArray(value) && value.every((field) => field === undefined || isQueryField(field) || (Array.isArray(field) && field.every(isQueryField)));
|
|
33
|
+
}
|
|
34
|
+
function readQueryResult(result, fields, postgres) {
|
|
35
|
+
let rows = result;
|
|
36
|
+
if (postgres) {
|
|
37
|
+
if (result === undefined || result === null)
|
|
38
|
+
return { rows: undefined, fields: undefined };
|
|
39
|
+
if (typeof result !== "object")
|
|
40
|
+
throw new TypeError("Invalid PostgreSQL result");
|
|
41
|
+
rows = "rows" in result ? result.rows : undefined;
|
|
42
|
+
fields = "fields" in result ? result.fields : undefined;
|
|
43
|
+
}
|
|
44
|
+
let checkedFields;
|
|
45
|
+
if (fields !== undefined) {
|
|
46
|
+
if (!isQueryFields(fields))
|
|
47
|
+
throw new TypeError("Invalid database fields");
|
|
48
|
+
checkedFields = fields;
|
|
49
|
+
}
|
|
50
|
+
if (rows === undefined || rows === null)
|
|
51
|
+
return { rows: undefined, fields: checkedFields };
|
|
52
|
+
if (isQueryRows(rows))
|
|
53
|
+
return { rows, fields: checkedFields };
|
|
54
|
+
throw new TypeError("Invalid database rows");
|
|
55
|
+
}
|
|
9
56
|
function quoteIdentifier(name, dbType) {
|
|
10
57
|
const quoteChar = dbType === "POSTGRESQL" ? '"' : "`";
|
|
11
58
|
return quoteChar + name + quoteChar;
|
|
@@ -186,7 +233,7 @@ function quotePostgresQuery(query) {
|
|
|
186
233
|
}
|
|
187
234
|
else {
|
|
188
235
|
for (let x of part.split(".")) {
|
|
189
|
-
if (isNaN(x)) {
|
|
236
|
+
if (isNaN(Number(x))) {
|
|
190
237
|
wordsToAddQuotes.add(x);
|
|
191
238
|
}
|
|
192
239
|
}
|
|
@@ -205,7 +252,8 @@ function quotePostgresQuery(query) {
|
|
|
205
252
|
class dbManager {
|
|
206
253
|
init(config) {
|
|
207
254
|
if (config.mysql) {
|
|
208
|
-
|
|
255
|
+
const driver = config.useMysql2 ? mysql2 : mysql;
|
|
256
|
+
this._db = driver.createPool({
|
|
209
257
|
connectionLimit: 10,
|
|
210
258
|
host: config.mysql.host,
|
|
211
259
|
user: config.mysql.user,
|
|
@@ -237,12 +285,16 @@ class dbManager {
|
|
|
237
285
|
queryString = quotePostgresQuery(result.pgQuery);
|
|
238
286
|
args = result.pgParams;
|
|
239
287
|
}
|
|
288
|
+
if (!this._db)
|
|
289
|
+
throw new TypeError("Cannot read properties of undefined (reading 'query')");
|
|
240
290
|
this._db.query(queryString, args, (a, b, c) => {
|
|
241
|
-
if (this.dbType === "MYSQL") {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
291
|
+
if (this.dbType === "MYSQL" || this.dbType === "POSTGRESQL") {
|
|
292
|
+
try {
|
|
293
|
+
resolve(Object.assign({ err: a !== null && a !== void 0 ? a : null }, readQueryResult(b, c, this.dbType === "POSTGRESQL")));
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
reject(error);
|
|
297
|
+
}
|
|
246
298
|
}
|
|
247
299
|
});
|
|
248
300
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dbManager.js","sourceRoot":"","sources":["../../src/utils/dbManager.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"dbManager.js","sourceRoot":"","sources":["../../src/utils/dbManager.ts"],"names":[],"mappings":";;AAgFA,0CAGC;AAqCD,oCAqCC;AAED,gDAyIC;AAxSD,+BAA+B;AAC/B,iCAAiC;AACjC,yBAAyB;AAyBzB,SAAS,eAAe,CAAC,KAAc;IACrC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,QAAQ;WAC1E,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAO,KAAK,KAAK,SAAS;WACvD,KAAK,YAAY,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;WAC/C,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,eAAe,CAAC;YACrD,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;WACtE,CAAC,CAAC,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;WACnD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACzB,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC;QACxD,CAAC,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;eACxC,UAAU,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ;eACzD,cAAc,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,YAAY,KAAK,QAAQ,CAAC;AAC3E,CAAC;AAED,SAAS,YAAY,CAAC,KAAc;IAClC,OAAO,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC1G,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE,CAC5D,KAAK,KAAK,SAAS,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;AACvG,CAAC;AAMD,SAAS,eAAe,CAAC,MAAe,EAAE,MAAe,EAAE,QAAiB;IAC1E,IAAI,IAAI,GAAG,MAAM,CAAC;IAClB,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC3F,IAAI,OAAO,MAAM,KAAK,QAAQ;YAAE,MAAM,IAAI,SAAS,CAAC,2BAA2B,CAAC,CAAC;QACjF,IAAI,GAAG,MAAM,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;QAClD,MAAM,GAAG,QAAQ,IAAI,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC1D,CAAC;IACD,IAAI,aAAsC,CAAC;IAC3C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAC3E,aAAa,GAAG,MAAM,CAAC;IACzB,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAC3F,IAAI,WAAW,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAC9D,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;AAC/C,CAAC;AAED,SAAgB,eAAe,CAAC,IAAY,EAAE,MAAc;IAC1D,MAAM,SAAS,GAAG,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACtD,OAAO,SAAS,GAAG,IAAI,GAAG,SAAS,CAAC;AACtC,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAa,EAAE,KAAa;IACvD,MAAM,EAAE,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAErC,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC3C,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IACnD,CAAC;IACD,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7C,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,CAAC,CAAC;IACd,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACvC,MAAM,gBAAgB,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC;IAEtG,IAAI,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;IACtB,OAAO,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,gBAAgB,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACvC,KAAK,IAAI,CAAC,CAAC;QACf,CAAC;aAAM,IAAI,OAAO,KAAK,GAAG,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;YAC5D,KAAK,IAAI,CAAC,CAAC;QACf,CAAC;aAAM,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YACzB,OAAO,KAAK,GAAG,CAAC,CAAC;QACrB,CAAC;aAAM,CAAC;YACJ,KAAK,EAAE,CAAC;QACZ,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC,MAAM,CAAC;AACxB,CAAC;AAED,SAAgB,YAAY,CAAC,UAAkB,EAAE,MAA4B;IAC3E,IAAI,OAAO,GAAG,EAAE,CAAC;IACjB,IAAI,QAAQ,GAAyB,EAAE,CAAC;IAExC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAKlB,OAAO,SAAS,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QACrC,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;QACxC,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,qBAAqB,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAEjE,IAAI,WAAW,KAAK,CAAC,CAAC,EAAE,CAAC;YACvB,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YACpD,SAAS,GAAG,WAAW,CAAC;QAC1B,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YAEtC,OAAO,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC1B,SAAS,IAAI,CAAC,CAAC;QACjB,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YAEtB,OAAO,IAAI,IAAI,UAAU,EAAE,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9B,SAAS,EAAE,CAAC;QACd,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,EAAE,CAAC;YACd,SAAS,EAAE,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,SAAgB,kBAAkB,CAAC,KAAa;IAC9C,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAEjC,MAAM,aAAa,GAAG;QACpB,GAAG;QACH,OAAO;QACP,GAAG;QACH,QAAQ;QACR,QAAQ;QACR,KAAK;QACL,GAAG;QACH,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,MAAM;QACN,OAAO;QACP,KAAK;QACL,IAAI;QACJ,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,MAAM;QACN,QAAQ;QACR,KAAK;QACL,KAAK;QACL,KAAK;QACL,MAAM;QACN,MAAM;QACN,OAAO;QACP,OAAO;QACP,IAAI;QACJ,OAAO;QACP,OAAO;QACP,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,MAAM;QACN,OAAO;QACP,OAAO;QACP,QAAQ;QACR,WAAW;QACX,MAAM;QACN,MAAM;QACN,MAAM;QACN,MAAM;QACN,KAAK;QACL,KAAK;QACL,IAAI;QACJ,UAAU;QACV,cAAc;QACd,KAAK;QACL,UAAU;QACV,QAAQ;QACR,MAAM;QACN,GAAG;QACH,OAAO;QACP,YAAY;QACZ,IAAI;QACJ,GAAG;QACH,UAAU;QACV,KAAK;QACL,KAAK;QACL,KAAK;QACL,SAAS;QACT,QAAQ;QACR,UAAU;QACV,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,OAAO;QACP,MAAM;QACN,WAAW;QACX,YAAY;QACZ,SAAS;QACT,UAAU;QACV,UAAU;QACV,YAAY;QACZ,gBAAgB;QAChB,YAAY;QACZ,SAAS;QACT,WAAW;QACX,WAAW;QACX,UAAU;QACV,UAAU;QACV,UAAU;QACV,UAAU;QACV,IAAI;QACJ,UAAU;QACV,WAAW;KACZ,CAAC;IAEF,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAU,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IAEtC,IAAI,QAAQ,GAAG,KAAK,CAAC;IAErB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,SAAS;QACX,CAAC;QACD,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,QAAQ,GAAG,CAAC,QAAQ,CAAC;QACvB,CAAC;QAED,IACE,QAAQ;YACR,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACpB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClB,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,EAC1C,CAAC;QACH,CAAC;aAAM,CAAC;YACN,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBACrB,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,KAAK,IAAI,IAAI,IAAI,gBAAgB,EAAE,CAAC;QAClC,IAAI,IAAI,KAAK,GAAG,EAAE,CAAC;YACjB,QAAQ,GAAG,QAAQ,CAAC,OAAO,CACzB,IAAI,MAAM,CAAC,gBAAgB,IAAI,cAAc,EAAE,GAAG,CAAC,EACnD,IAAI,IAAI,GAAG,CACZ,CAAC;QACJ,CAAC;IACH,CAAC;IAED,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACrD,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAExD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAqB,SAAS;IAI5B,IAAI,CAAC,MAAsB;QACzB,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,MAAM,MAAM,GACV,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;YACpC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC;gBAC3B,eAAe,EAAE,EAAE;gBACnB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI;gBACvB,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI;gBACvB,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;gBAC/B,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;gBAC/B,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,IAAI;gBAC/B,OAAO,EAAE,oBAAoB;gBAC7B,WAAW,EAAE,IAAI;gBAIjB,kBAAkB,EAAE,MAAM,CAAC,KAAK,CAAC,kBAAkB,KAAK,IAAI;aAC7D,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC;QACxB,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;YAC7B,IAAI,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC;gBACrB,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI;gBAC5B,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI;gBAC5B,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ;gBACpC,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ;gBACpC,IAAI,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,IAAI,IAAI;gBACpC,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC,GAAG;aAC3B,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,YAAY,CAAC;QAC7B,CAAC;IACH,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,WAAmB,EAAE,OAA6B,EAAE;QAC9D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;gBACjC,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;gBAC/C,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBACjD,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC;YACzB,CAAC;YACD,IAAI,CAAC,IAAI,CAAC,GAAG;gBAAE,MAAM,IAAI,SAAS,CAAC,uDAAuD,CAAC,CAAC;YAC5F,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE;gBAC5C,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,EAAE,CAAC;oBAC5D,IAAI,CAAC;wBAKH,OAAO,iBAAG,GAAG,EAAE,CAAC,aAAD,CAAC,cAAD,CAAC,GAAI,IAAI,IAAK,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,MAAM,KAAK,YAAY,CAAC,EAAG,CAAC;oBACtF,CAAC;oBAAC,OAAO,KAAK,EAAE,CAAC;wBACf,MAAM,CAAC,KAAK,CAAC,CAAC;oBAChB,CAAC;gBACH,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AA3DD,4BA2DC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@galadrim/galadmin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.3.0-alpha.2.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "jest",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"test:coverage": "TEST_INTEGRATION=1 jest --coverage --runInBand --projects jest.config.js jest.integration.config.js",
|
|
10
10
|
"test:integration": "TEST_INTEGRATION=1 jest --config jest.integration.config.js --runInBand",
|
|
11
11
|
"test:all": "npm run test:unit && npm run test:integration",
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"typecheck": "tsc --noEmit
|
|
12
|
+
"build": "tsc -p tsconfig.strict.json",
|
|
13
|
+
"typecheck": "tsc -p tsconfig.strict.json --noEmit"
|
|
14
14
|
},
|
|
15
15
|
"engines": {
|
|
16
16
|
"node": ">=22"
|
|
@@ -54,12 +54,21 @@
|
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@testcontainers/mysql": "^12.1.0",
|
|
56
56
|
"@testcontainers/postgresql": "^12.1.0",
|
|
57
|
+
"@types/bcryptjs": "2.4.6",
|
|
58
|
+
"@types/connect-pg-simple": "7.0.3",
|
|
57
59
|
"@types/express": "^4.17.13",
|
|
60
|
+
"@types/express-mysql-session": "3.0.8",
|
|
58
61
|
"@types/express-session": "1.17.0",
|
|
59
62
|
"@types/jest": "^30.0.0",
|
|
60
63
|
"@types/mime": "^2.0.3",
|
|
64
|
+
"@types/multiparty": "^4.2.1",
|
|
65
|
+
"@types/mysql": "^2.15.27",
|
|
61
66
|
"@types/node": "^16.18.11",
|
|
67
|
+
"@types/pg": "^8.23.1",
|
|
68
|
+
"@types/serialize-javascript": "^5.0.4",
|
|
69
|
+
"@types/ssh2-sftp-client": "^9.0.6",
|
|
62
70
|
"@types/supertest": "^7.2.1",
|
|
71
|
+
"@types/uuid": "^9.0.8",
|
|
63
72
|
"jest": "^29.7.0",
|
|
64
73
|
"supertest": "^7.2.2",
|
|
65
74
|
"testcontainers": "^12.1.0",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { BaseTypeView, TablePanel } from "../types/config";
|
|
2
|
+
|
|
3
|
+
type SessionRequest = { session?: object };
|
|
4
|
+
type WritableView = Pick<BaseTypeView, "roles"> & Pick<TablePanel, "tableName"> & {
|
|
5
|
+
recordViewPanels?: (Pick<TablePanel, "tableName"> & {
|
|
6
|
+
createConfig?: Pick<TablePanel, "tableName">;
|
|
7
|
+
})[];
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// SECURITY: server-side authorization.
|
|
12
|
+
//
|
|
13
|
+
// The config-filtering middleware below only decides which views the *client*
|
|
14
|
+
// is shown, and sets canEdit/canCreate/canDelete flags on that served config.
|
|
15
|
+
// Nothing enforced those decisions server-side, so a session with the lowest
|
|
16
|
+
// role could POST directly to /updateRecord, /deleteRecord, /customQuery, …
|
|
17
|
+
// and act on any view — including read-only ones — as the DB user (root).
|
|
18
|
+
// This block re-checks every sensitive route against the view's declared roles.
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
export const ROOT_ROLE = "root";
|
|
21
|
+
/*
|
|
22
|
+
#PORTING-EDIT
|
|
23
|
+
Use undefined for missing or null users and for non-object users or non-text roles from session storage.
|
|
24
|
+
*/
|
|
25
|
+
export const roleOf = (req: SessionRequest) => {
|
|
26
|
+
const user: unknown = req.session && Reflect.get(req.session, "user");
|
|
27
|
+
if (!user || typeof user !== "object") return undefined;
|
|
28
|
+
const role: unknown = Reflect.get(user, "role");
|
|
29
|
+
return typeof role === "string" ? role : undefined;
|
|
30
|
+
};
|
|
31
|
+
// Write access = the view's `roles` (root always allowed).
|
|
32
|
+
export const canWriteView = (view: Pick<BaseTypeView, "roles"> | undefined, role: string | undefined) =>
|
|
33
|
+
role === ROOT_ROLE || !view || !view.roles || (role !== undefined && view.roles.includes(role));
|
|
34
|
+
|
|
35
|
+
export const viewFromRequest = <T = WritableView>(req: {
|
|
36
|
+
body?: { viewIndex?: unknown };
|
|
37
|
+
query?: { viewIndex?: unknown };
|
|
38
|
+
config?: { views?: T[] };
|
|
39
|
+
}): T | undefined => {
|
|
40
|
+
const idx =
|
|
41
|
+
req.body && req.body.viewIndex !== undefined
|
|
42
|
+
? req.body.viewIndex
|
|
43
|
+
: req.query && req.query.viewIndex !== undefined
|
|
44
|
+
? req.query.viewIndex
|
|
45
|
+
: undefined;
|
|
46
|
+
if (idx === undefined || !req.config || !req.config.views) return undefined;
|
|
47
|
+
/*
|
|
48
|
+
#PORTING-EDIT
|
|
49
|
+
Only array indices identify views; reject metadata and prototype properties before returning a view.
|
|
50
|
+
Keep property-key coercion and reject noncanonical numeric strings such as "01".
|
|
51
|
+
*/
|
|
52
|
+
const key = String(idx);
|
|
53
|
+
const index = Number(key);
|
|
54
|
+
if (!Number.isInteger(index) || index < 0 || index >= req.config.views.length || String(index) !== key) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return req.config.views[index];
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// Routes that mutate data and reference their target view via viewIndex
|
|
61
|
+
// (body for POST bodies, query for /uploadFile). Import routes carry the
|
|
62
|
+
// viewIndex inside a multipart body and are guarded inline in their handlers.
|
|
63
|
+
export const MUTATING_VIEW_ROUTES = new Set([
|
|
64
|
+
"/updateRecord",
|
|
65
|
+
"/createRecord",
|
|
66
|
+
"/deleteRecord",
|
|
67
|
+
"/deleteRecords",
|
|
68
|
+
"/deleteRecordFromPanel",
|
|
69
|
+
"/customAction",
|
|
70
|
+
"/uploadFile",
|
|
71
|
+
]);
|
|
72
|
+
// /duplicateRecord references its table via req.body.table (not a viewIndex),
|
|
73
|
+
// so it is guarded inline in its handler instead of by the generic middleware.
|
|
74
|
+
|
|
75
|
+
// Column names are interpolated into INSERT/UPDATE statements; only accept plain
|
|
76
|
+
// SQL identifiers so a crafted key cannot break out of the column list.
|
|
77
|
+
export const areSafeColumnNames = (data: unknown) =>
|
|
78
|
+
!data || Object.keys(data).every((k) => /^[A-Za-z0-9_]+$/.test(k));
|
|
79
|
+
|
|
80
|
+
// Tables the current role is allowed to write, gathered from its writable views
|
|
81
|
+
// and their creatable panels — used to bound /duplicateRecord's target table.
|
|
82
|
+
export const writableTables = (req: SessionRequest & { config?: { views?: WritableView[] } }) => {
|
|
83
|
+
const role = roleOf(req);
|
|
84
|
+
const tables = new Set<string>();
|
|
85
|
+
for (const view of req.config?.views || []) {
|
|
86
|
+
if (!canWriteView(view, role)) continue;
|
|
87
|
+
if (view.tableName) tables.add(view.tableName);
|
|
88
|
+
for (const panel of view.recordViewPanels || []) {
|
|
89
|
+
if (panel.tableName) tables.add(panel.tableName);
|
|
90
|
+
if (panel.createConfig?.tableName)
|
|
91
|
+
tables.add(panel.createConfig.tableName);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return tables;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Reject client-supplied SQL fragments (the `filters` array is concatenated
|
|
98
|
+
// raw into the WHERE clause). With multipleStatements off this already blocks
|
|
99
|
+
// stacked writes; the pattern also blocks UNION/comment/file exfil breakouts.
|
|
100
|
+
export const looksLikeSqlBreakout = (frag: unknown) =>
|
|
101
|
+
typeof frag === "string" &&
|
|
102
|
+
/(;|--|\/\*|\bunion\b|\binto\s+(out|dump)file\b|\bsleep\s*\(|\bbenchmark\s*\(|\bload_file\s*\()/i.test(
|
|
103
|
+
frag,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
type HiddenColumnsView = { __hiddenColumns?: string[] };
|
|
107
|
+
|
|
108
|
+
// Columns of `view` the current role may not see (cf. the config middleware below).
|
|
109
|
+
export const hiddenColumnsOf = (
|
|
110
|
+
view: HiddenColumnsView | undefined,
|
|
111
|
+
) => (view && view.__hiddenColumns) || [];
|
|
112
|
+
|
|
113
|
+
// Removes the hidden columns from a record before it is sent.
|
|
114
|
+
export const stripHiddenColumns = <T extends Record<string, unknown>>(
|
|
115
|
+
view: HiddenColumnsView | undefined,
|
|
116
|
+
record: T,
|
|
117
|
+
) => {
|
|
118
|
+
for (const name of hiddenColumnsOf(view)) delete record[name];
|
|
119
|
+
return record;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
// Whether a write payload touches a hidden column.
|
|
123
|
+
export const touchesHiddenColumn = (
|
|
124
|
+
view: HiddenColumnsView | undefined,
|
|
125
|
+
data: unknown,
|
|
126
|
+
) =>
|
|
127
|
+
hiddenColumnsOf(view).some(
|
|
128
|
+
(name) => data && Object.prototype.hasOwnProperty.call(data, name),
|
|
129
|
+
);
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import * as crypto from "crypto";
|
|
2
|
+
import type {
|
|
3
|
+
BaseTypeColumn, Config, FileUploadProperties, RichTextColumn,
|
|
4
|
+
SelectColumn, StandardColumn, Subview, View,
|
|
5
|
+
} from "../types/config";
|
|
6
|
+
import type dbManager from "../utils/dbManager";
|
|
7
|
+
|
|
8
|
+
type Identified = { __id__?: string };
|
|
9
|
+
type EnrichmentColumn = StandardColumn & Pick<BaseTypeColumn, "required">
|
|
10
|
+
& Pick<RichTextColumn, "contentField">
|
|
11
|
+
& Pick<FileUploadProperties, "s3Config" | "gcpConfig">
|
|
12
|
+
& Partial<Pick<SelectColumn, "options">> & Identified & { baseUrl?: string };
|
|
13
|
+
type EnrichmentView = Identified & { subviews?: (Subview & Identified)[] } & (
|
|
14
|
+
| { type: "table"; tableName: string; primaryId?: string; columns: EnrichmentColumn[];
|
|
15
|
+
recordViewPanels?: (Identified & { type: string; createConfig?: { columns: EnrichmentColumn[] } })[] }
|
|
16
|
+
| { type: Exclude<View["type"], "table"> }
|
|
17
|
+
);
|
|
18
|
+
type EnrichmentConfig = Pick<Config, "mysql" | "postgresql" | "enriched"> & {
|
|
19
|
+
views: EnrichmentView[];
|
|
20
|
+
};
|
|
21
|
+
type SchemaColumn = {
|
|
22
|
+
TABLE_NAME?: string;
|
|
23
|
+
COLUMN_NAME?: string;
|
|
24
|
+
COLUMN_KEY?: string;
|
|
25
|
+
IS_NULLABLE?: string;
|
|
26
|
+
DATA_TYPE?: string;
|
|
27
|
+
COLUMN_TYPE?: string;
|
|
28
|
+
};
|
|
29
|
+
type SchemaDatabase = {
|
|
30
|
+
query(...args: Parameters<dbManager["query"]>): Promise<Partial<
|
|
31
|
+
Pick<Awaited<ReturnType<dbManager["query"]>>, "err" | "rows">
|
|
32
|
+
>>;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
#PORTING-EDIT
|
|
37
|
+
Reject non-row schema results and non-text metadata before applying it to configuration.
|
|
38
|
+
PostgreSQL's lower-case metadata remains unmatched, as before.
|
|
39
|
+
*/
|
|
40
|
+
function readSchemaColumns(value: unknown): SchemaColumn[] {
|
|
41
|
+
if (!Array.isArray(value) || !value.every((row: unknown): row is SchemaColumn =>
|
|
42
|
+
row !== null && typeof row === "object" && !Array.isArray(row)
|
|
43
|
+
&& (!("TABLE_NAME" in row) || typeof row.TABLE_NAME === "string")
|
|
44
|
+
&& (!("COLUMN_NAME" in row) || typeof row.COLUMN_NAME === "string")
|
|
45
|
+
&& (!("COLUMN_KEY" in row) || typeof row.COLUMN_KEY === "string")
|
|
46
|
+
&& (!("IS_NULLABLE" in row) || typeof row.IS_NULLABLE === "string")
|
|
47
|
+
&& (!("DATA_TYPE" in row) || typeof row.DATA_TYPE === "string")
|
|
48
|
+
&& (!("COLUMN_TYPE" in row) || typeof row.COLUMN_TYPE === "string")
|
|
49
|
+
)) throw new TypeError("Invalid database schema columns");
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
#PORTING-EDIT
|
|
55
|
+
Require enum metadata to decode to text options before assigning select options.
|
|
56
|
+
*/
|
|
57
|
+
function readEnumOptions(columnType: string | undefined): string[] {
|
|
58
|
+
if (columnType === undefined) throw new TypeError("Missing enum column type");
|
|
59
|
+
const options: unknown = JSON.parse(
|
|
60
|
+
columnType.replace("enum(", "[").replace(")", "]").replace(/'/g, '"'),
|
|
61
|
+
);
|
|
62
|
+
if (!Array.isArray(options) || !options.every((option: unknown) => typeof option === "string")) {
|
|
63
|
+
throw new TypeError("Invalid enum options");
|
|
64
|
+
}
|
|
65
|
+
return options;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function generateUniqueIdForObject(obj: unknown) {
|
|
69
|
+
const str = JSON.stringify(obj);
|
|
70
|
+
const hash = crypto.createHash("sha256").update(str).digest("hex");
|
|
71
|
+
return hash;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function enrichConfig(config: EnrichmentConfig, db: SchemaDatabase) {
|
|
75
|
+
const { rows, err = null } = await db.query(
|
|
76
|
+
"SELECT * FROM information_schema.columns WHERE table_schema = ?",
|
|
77
|
+
[config.mysql?.database || config.postgresql?.database],
|
|
78
|
+
);
|
|
79
|
+
if (err) {
|
|
80
|
+
console.error(err);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const info = readSchemaColumns(rows);
|
|
84
|
+
for (const view of config.views) {
|
|
85
|
+
if (view.type == "table") {
|
|
86
|
+
const tableName = view.tableName;
|
|
87
|
+
for (const infoEl of info) {
|
|
88
|
+
if (infoEl.TABLE_NAME == tableName && infoEl.COLUMN_KEY == "PRI") {
|
|
89
|
+
view.primaryId = infoEl.COLUMN_NAME;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
for (const column of view.columns) {
|
|
93
|
+
enrichColumn(column, tableName, info);
|
|
94
|
+
}
|
|
95
|
+
if (view.recordViewPanels) {
|
|
96
|
+
for (const panel of view.recordViewPanels) {
|
|
97
|
+
if (panel.createConfig) {
|
|
98
|
+
for (const column of panel.createConfig.columns) {
|
|
99
|
+
enrichColumn(column, tableName, info);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (!panel.__id__) {
|
|
103
|
+
panel.__id__ = generateUniqueIdForObject(panel);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (view.subviews) {
|
|
109
|
+
for (const subview of view.subviews) {
|
|
110
|
+
if (!subview.__id__) {
|
|
111
|
+
subview.__id__ = generateUniqueIdForObject(subview);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (!view.__id__) {
|
|
116
|
+
view.__id__ = generateUniqueIdForObject(view);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
config.enriched = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function enrichColumn(column: EnrichmentColumn, tableName: string, info: SchemaColumn[]) {
|
|
123
|
+
if (!column.__id__) {
|
|
124
|
+
column.__id__ = generateUniqueIdForObject(column);
|
|
125
|
+
}
|
|
126
|
+
const infoEl = info.find(
|
|
127
|
+
(x) => x.TABLE_NAME == tableName && x.COLUMN_NAME == column.name,
|
|
128
|
+
);
|
|
129
|
+
if (
|
|
130
|
+
infoEl?.IS_NULLABLE == "NO" &&
|
|
131
|
+
infoEl?.DATA_TYPE != "text" &&
|
|
132
|
+
column.required !== true &&
|
|
133
|
+
column.required !== false
|
|
134
|
+
) {
|
|
135
|
+
column.required = true;
|
|
136
|
+
}
|
|
137
|
+
if (!column.dataType && infoEl) {
|
|
138
|
+
column.dataType = infoEl.DATA_TYPE;
|
|
139
|
+
if (column.dataType == "enum") {
|
|
140
|
+
column.dataType = "select";
|
|
141
|
+
column.options = readEnumOptions(infoEl.COLUMN_TYPE);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (column.dataType == "richText") {
|
|
145
|
+
column.name = column.contentField;
|
|
146
|
+
}
|
|
147
|
+
if (column.dataType == "file" && (column.s3Config || column.gcpConfig)) {
|
|
148
|
+
const host = column.s3Config?.host || column.gcpConfig?.host;
|
|
149
|
+
const bucket = column.s3Config?.bucket || column.gcpConfig?.bucket;
|
|
150
|
+
column.baseUrl = `https://${host}/${bucket}/`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { FilterUser } from "../types/config";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
#PORTING-EDIT
|
|
5
|
+
Construct the checked callback identity without changing session data; root takes precedence and invalid filtered sessions are rejected.
|
|
6
|
+
*/
|
|
7
|
+
export function filterUserFromSession(value: unknown): FilterUser | undefined {
|
|
8
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
9
|
+
return undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const role: unknown = "role" in value ? value.role : undefined;
|
|
13
|
+
if (typeof role !== "string") {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (role === "root") {
|
|
17
|
+
return { role };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const userId: unknown = "userId" in value ? value.userId : undefined;
|
|
21
|
+
if (userId === undefined) {
|
|
22
|
+
return { role };
|
|
23
|
+
}
|
|
24
|
+
if (typeof userId === "string" || typeof userId === "number") {
|
|
25
|
+
return { role, userId };
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function isImportRecords(
|
|
2
|
+
value: unknown,
|
|
3
|
+
): value is Record<string, unknown>[] {
|
|
4
|
+
if (!Array.isArray(value)) {
|
|
5
|
+
return false;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
9
|
+
const record: unknown = value[index];
|
|
10
|
+
if (typeof record !== "object" || record === null || Array.isArray(record)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return true;
|
|
16
|
+
}
|