@opengis/fastify-table 2.0.111 → 2.0.113

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/helper.d.ts +8 -0
  2. package/dist/helper.d.ts.map +1 -0
  3. package/dist/helper.js +34 -0
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +6 -6
  6. package/dist/module/core/cls/constraint_action.json +10 -0
  7. package/dist/module/core/cls/constraint_matchtype.json +6 -0
  8. package/dist/module/core/cls/constraint_type.json +14 -0
  9. package/dist/module/core/cls/constraint_type_full.json +18 -0
  10. package/dist/module/core/cls/constraint_type_table.json +18 -0
  11. package/dist/module/core/cls/core.user_type.json +14 -0
  12. package/dist/module/core/pt/schemaItem.pt.hbs +18 -0
  13. package/dist/module/core/select/core.user_mentioned.sql +2 -0
  14. package/dist/script/dump.d.ts +2 -0
  15. package/dist/script/dump.d.ts.map +1 -0
  16. package/dist/script/dump.js +102 -129
  17. package/dist/script/dump.ts +205 -0
  18. package/dist/script/migrate.d.ts +2 -0
  19. package/dist/script/migrate.d.ts.map +1 -0
  20. package/dist/script/migrate.js +24 -25
  21. package/dist/script/migrate.ts +31 -0
  22. package/dist/server/plugins/sqlite/funcs/getSqlite.d.ts.map +1 -1
  23. package/dist/server/plugins/sqlite/funcs/getSqlite.js +1 -2
  24. package/dist/server/plugins/sqlite/sqliteClients.d.ts.map +1 -1
  25. package/dist/server/plugins/sqlite/sqliteClients.js +1 -2
  26. package/dist/server/plugins/table/funcs/getColumnCLS.d.ts.map +1 -1
  27. package/dist/server/plugins/table/funcs/getColumnCLS.js +18 -7
  28. package/dist/server/plugins/util/funcs/eventStream.d.ts +4 -1
  29. package/dist/server/plugins/util/funcs/eventStream.d.ts.map +1 -1
  30. package/dist/server/routes/file/controllers/export.d.ts.map +1 -1
  31. package/dist/server/routes/file/controllers/export.js +21 -14
  32. package/dist/server/routes/file/controllers/utils/pubsub.d.ts +2 -0
  33. package/dist/server/routes/file/controllers/utils/pubsub.d.ts.map +1 -0
  34. package/dist/server/routes/file/controllers/utils/pubsub.js +112 -0
  35. package/dist/server/routes/table/controllers/dataInfo.d.ts +8 -1
  36. package/dist/server/routes/table/controllers/dataInfo.d.ts.map +1 -1
  37. package/dist/server/routes/table/controllers/dataInfo.js +7 -5
  38. package/dist/server/types/core.d.ts.map +1 -1
  39. package/dist/server/types/core.js +0 -1
  40. package/dist/test.setup.js +0 -1
  41. package/dist/utils.d.ts +2 -0
  42. package/dist/utils.d.ts.map +1 -1
  43. package/dist/utils.js +3 -0
  44. package/package.json +5 -4
  45. package/dist/log/migration/dist-geoname-cls.json +0 -1
  46. package/dist/log/migration/dist-geoname-cls.sql +0 -0
  47. package/dist/server/plugins/upload/finishUpload.d.ts +0 -9
  48. package/dist/server/plugins/upload/finishUpload.d.ts.map +0 -1
  49. package/dist/server/plugins/upload/finishUpload.js +0 -33
  50. package/dist/server/plugins/util/funcs/routeOptions.d.ts +0 -3
  51. package/dist/server/plugins/util/funcs/routeOptions.d.ts.map +0 -1
  52. package/dist/server/plugins/util/funcs/routeOptions.js +0 -2
  53. package/dist/server/routes/auth/controllers/2factor/generate.d.ts +0 -19
  54. package/dist/server/routes/auth/controllers/2factor/generate.d.ts.map +0 -1
  55. package/dist/server/routes/auth/controllers/2factor/generate.js +0 -38
  56. package/dist/server/routes/auth/controllers/2factor/toggle.d.ts +0 -18
  57. package/dist/server/routes/auth/controllers/2factor/toggle.d.ts.map +0 -1
  58. package/dist/server/routes/auth/controllers/2factor/toggle.js +0 -39
@@ -0,0 +1,8 @@
1
+ import config from "./config.js";
2
+ import { pgClients } from "./utils.js";
3
+ declare const prefix: any;
4
+ export declare function setup(): Promise<void>;
5
+ export declare function teardown(): Promise<void>;
6
+ export declare function build(): any;
7
+ export { prefix, config, pgClients, };
8
+ //# sourceMappingURL=helper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../helper.ts"],"names":[],"mappings":"AAGA,OAAO,MAAM,MAAM,aAAa,CAAC;AAGjC,OAAO,EAAE,SAAS,EAAkB,MAAM,YAAY,CAAC;AAEvD,QAAA,MAAQ,MAAM,KAAoB,CAAC;AAInC,wBAAsB,KAAK,kBAI1B;AAGD,wBAAsB,QAAQ,kBAW7B;AAED,wBAAgB,KAAK,QAQpB;AAED,OAAO,EAEL,MAAM,EACN,MAAM,EACN,SAAS,GACV,CAAC"}
package/dist/helper.js ADDED
@@ -0,0 +1,34 @@
1
+ import path from "node:path";
2
+ import Fastify from "fastify";
3
+ import config from "./config.js";
4
+ import appService from "./index.js";
5
+ import { pgClients, addTemplateDir } from "./utils.js";
6
+ const { prefix = "/api" } = config;
7
+ let app;
8
+ export async function setup() {
9
+ console.log("Global setup");
10
+ app = await build();
11
+ console.log("Global setup finish");
12
+ }
13
+ // ? close clients via vitest.config.js settings deploy, for npx vitest run
14
+ export async function teardown() {
15
+ console.log("Global teardown: closing all clients");
16
+ if (app) {
17
+ app.close();
18
+ console.log("Global teardown: app closed");
19
+ }
20
+ console.log("Global teardown: all clients closed");
21
+ process.exit(0);
22
+ }
23
+ export function build() {
24
+ if (!app) {
25
+ app = Fastify({ logger: false });
26
+ app.register(appService, config);
27
+ addTemplateDir(path.join(process.cwd(), "module/test"));
28
+ // await app.ready(); // ? ensure plugins registered, can not add fastify hooks after app is ready
29
+ }
30
+ return app;
31
+ }
32
+ export {
33
+ // teardown,
34
+ prefix, config, pgClients, };
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAuFA,iBAAS,MAAM,CAAC,OAAO,EAAE,GAAG,QA6J3B;;AACD,wBAA0B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AA2FA,iBAAS,MAAM,CAAC,OAAO,EAAE,GAAG,QAyJ3B;;AACD,wBAA0B"}
package/dist/index.js CHANGED
@@ -2,6 +2,12 @@ import fp from "fastify-plugin";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import proxy from "@fastify/http-proxy";
5
+ import addTemplateDir from "./server/plugins/table/funcs/addTemplateDir.js";
6
+ const filename = fileURLToPath(import.meta.url);
7
+ const cwd = path.dirname(filename);
8
+ // core templates && cls
9
+ config.templates?.forEach((el) => addTemplateDir(el));
10
+ addTemplateDir(path.join(cwd, "module/core"));
5
11
  import config from "./config.js";
6
12
  import { preForm, afterInsert, afterTable, afterTemplate, afterUpdate, preTemplate, onReady, onListen1, onListen2, } from "./functions.js";
7
13
  import addHook from "./server/plugins/hook/addHook.js";
@@ -29,7 +35,6 @@ import redisPlugin from "./server/plugins/redis/index.js";
29
35
  import loggerPlugin from "./server/plugins/logger/index.js";
30
36
  import authPlugin from "./server/plugins/auth/index.js";
31
37
  // utils
32
- import addTemplateDir from "./server/plugins/table/funcs/addTemplateDir.js";
33
38
  import execMigrations from "./server/plugins/migration/exec.migrations.js";
34
39
  import pgClients from "./server/plugins/pg/pgClients.js";
35
40
  // routes
@@ -49,8 +54,6 @@ import fileRoutes from "./server/routes/file/index.js";
49
54
  import uploadChunkRoutes from "./server/routes/upload/index.js";
50
55
  import grpcRoutes from "./server/routes/grpc/index.js";
51
56
  import notificationsRoutes from "./server/routes/notifications/index.js";
52
- const filename = fileURLToPath(import.meta.url);
53
- const cwd = path.dirname(filename);
54
57
  addHook("preForm", preForm);
55
58
  addHook("afterTable", afterTable);
56
59
  addHook("preTemplate", preTemplate);
@@ -95,9 +98,6 @@ function plugin(fastify) {
95
98
  fastify.addHook("onReady", onReady);
96
99
  // core migrations (second argument for core only)
97
100
  execMigrations(path.join(cwd, "server/migrations"), pgClients.client, true).catch((err) => console.warn(err.toString()));
98
- // core templates && cls
99
- config.templates?.forEach((el) => addTemplateDir(el));
100
- addTemplateDir(path.join(cwd, "module/core"));
101
101
  // plugins / utils / funcs
102
102
  policyPlugin(fastify);
103
103
  authPlugin(fastify); // fastify-auth api + hooks integrated to core
@@ -0,0 +1,10 @@
1
+ [
2
+ {
3
+ "id": "a",
4
+ "text": "NO ACTION"
5
+ },
6
+ {
7
+ "id": "c",
8
+ "text": "CASCADE"
9
+ }
10
+ ]
@@ -0,0 +1,6 @@
1
+ [
2
+ {
3
+ "id": "s",
4
+ "text": "SIMPLE"
5
+ }
6
+ ]
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": "u",
4
+ "text": "UK"
5
+ },
6
+ {
7
+ "id": "p",
8
+ "text": "PK"
9
+ },
10
+ {
11
+ "id": "f",
12
+ "text": "FK"
13
+ }
14
+ ]
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "id": "u",
4
+ "text": "UNIQUE"
5
+ },
6
+ {
7
+ "id": "p",
8
+ "text": "PRIMARY KEY"
9
+ },
10
+ {
11
+ "id": "f",
12
+ "text": "FOREIGN KEY"
13
+ },
14
+ {
15
+ "id": "c",
16
+ "text": "CHECK"
17
+ }
18
+ ]
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "id": "u",
4
+ "text": "UK"
5
+ },
6
+ {
7
+ "id": "p",
8
+ "text": "PK"
9
+ },
10
+ {
11
+ "id": "f",
12
+ "text": "FK"
13
+ },
14
+ {
15
+ "id": "c",
16
+ "text": "CHECK"
17
+ }
18
+ ]
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "id": "regular",
4
+ "text": "Regular"
5
+ },
6
+ {
7
+ "id": "viewer",
8
+ "text": "Viewer"
9
+ },
10
+ {
11
+ "id": "admin",
12
+ "text": "Admin"
13
+ }
14
+ ]
@@ -0,0 +1,18 @@
1
+ -- schema: {{nspname}}
2
+
3
+ CREATE schema if not exists {{nspname}};
4
+
5
+ {{#each rows}}
6
+ -- {{{description}}}
7
+
8
+ CREATE TABLE if not exists {{nspname}}.{{relname}}
9
+ (
10
+ {{#each columns}}
11
+ {{column_name}} {{data_type}} {{#unless is_nullable}}NOT NULL{{/unless}}{{#if column_default}}DEFAULT {{column_default}}{{/if}}{{#ifCond @index '!=' (_math ../columns.length '-' 1)}},{{/ifCond}} -- {{{coalesce description '-'}}}
12
+ {{/each}}{{#if constraints.length}},{{/if}}
13
+ {{#each constraints}}
14
+ CONSTRAINT {{constraint_name}} {{#ifCond constraint_type '==' 'f'}}FOREIGN KEY ({{foreign_column}}) REFERENCES {{foreign_table}} ({{foreign_column}}) MATCH {{select confmatchtype data="constraint_matchtype"}} ON UPDATE {{select confupdtype data="constraint_action"}} ON DELETE {{select confdeltype data="constraint_action"}}{{^}}{{select constraint_type data="constraint_type_full"}} ({{column_name}}){{/ifCond}}{{#ifCond @index '!=' (_math ../constraints.length '-' 1)}},{{/ifCond}}
15
+ {{/each}}
16
+ );
17
+
18
+ {{/each}}
@@ -0,0 +1,2 @@
1
+ select uid, coalesce(sur_name,'')||coalesce(' '||user_name,'') as text, email from admin.users
2
+ where enabled order by coalesce(sur_name,'')||coalesce(' '||user_name,'')
@@ -0,0 +1,2 @@
1
+ export default function dumpMigrateSQL(): Promise<null | undefined>;
2
+ //# sourceMappingURL=dump.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dump.d.ts","sourceRoot":"","sources":["../../script/dump.ts"],"names":[],"mappings":"AAqBA,wBAA8B,cAAc,8BA6D3C"}
@@ -1,105 +1,81 @@
1
- // Вигрузка схеми бд
2
- // Usage examples:
3
- // bun .\script\dump.js --table=bpmn.tasks
4
- // bun .\script\dump.js --schema=bpmn
5
-
6
- import path from 'node:path';
7
- import { existsSync } from 'node:fs';
8
- import { mkdir, writeFile, rm, rmdir, stat } from 'node:fs/promises';
9
-
10
- import { config, handlebars, pgClients, getTemplate } from '../utils.js';
11
-
12
- import { build, teardown } from '../helper.js';
13
-
14
- const app = build();
15
- app.addHook('onClose', async () => teardown());
16
- dumpMigrateSQL();
17
- // app.close();
18
-
19
- const debug = false;
20
-
21
- export default async function dumpMigrateSQL() {
22
- try {
23
- // const { database, host, port, user, password } = config.pg;
24
-
25
- if (!config.pg) {
26
- console.error('empty config.pg, skip...');
27
- return null;
28
- }
29
-
30
- if (!Bun.argv[2]) {
31
- console.error('missing schema / table name, skip...');
32
- }
33
-
34
- const [key, value] = Bun.argv[2].substring(2).split('=');
35
- const tableName = key === 'table' ? value : null;
36
- const schemaName = key === 'schema' ? value : value.split('.').shift();
37
-
38
- const pg = pgClients.client;
39
- // const pg = await getPGAsync({ database, host, port, user, password });
40
- await pg.query(`select 1`);
41
-
42
- const schemaExists = await pg.query(`SELECT 1 FROM information_schema.schemata WHERE schema_name = $1`, [schemaName]).then(el => el.rowCount);
43
-
44
- if (!schemaExists) {
45
- console.error('Вказаної схеми не існує', config.pg?.database);
46
- return null;
47
- }
48
-
49
- // if (tableName && !pg.pk?.[tableName]) {
50
- // console.error('Вказаної таблиці не існує', config.pg?.database);
51
- // return null;
52
- // }
53
-
54
- const dump = await schemaItem({
55
- pg,
56
- table: tableName,
57
- schema: schemaName,
58
- debug,
59
- is_erd: false
60
- });
61
-
62
- if (debug) {
63
- console.log(dump);
64
- return null;
65
- }
66
-
67
- const filepath = await saveFile(dump, tableName || schemaName);
68
- console.log('sucess', filepath);
69
- } catch (err) {
70
- console.error(err);
71
- } finally {
72
- app.close();
73
- }
74
- }
75
-
76
- async function saveFile(data, filename) {
77
- if (!data) throw new Error(`no data - ${filename}`);
78
-
79
- const filepath = path.join('log/dump', `${filename}.sql`);
80
- const fileExists = existsSync(filepath);
81
-
82
- // overwrite old file
83
- if (fileExists) {
84
- const stats = await stat(filepath);
85
- if (stats.isDirectory()) {
86
- await rmdir(filepath, { force: true, recursive: true });
87
- } else {
88
- await rm(filepath)
89
- }
90
- }
91
-
92
- await mkdir(path.dirname(filepath), { recursive: true });
93
- await writeFile(filepath, Buffer.from(data, 'utf-8'));
94
-
95
- return filepath;
96
- }
97
-
98
- async function schemaItem({
99
- pg, table, schema, debug
100
- }) {
101
- if (!schema && !table) return new Error('param schema is required');
102
-
1
+ // Вигрузка схеми бд
2
+ // Usage examples:
3
+ // bun .\script\dump.js --table=bpmn.tasks
4
+ // bun .\script\dump.js --schema=bpmn
5
+ import path from "node:path";
6
+ import { existsSync } from "node:fs";
7
+ import { mkdir, writeFile, rm } from "node:fs/promises";
8
+ import { config, handlebars, pgClients, getTemplate } from "../utils.js";
9
+ import { build, teardown } from "../helper.js";
10
+ if (import.meta.main) {
11
+ dumpMigrateSQL();
12
+ }
13
+ const debug = false;
14
+ export default async function dumpMigrateSQL() {
15
+ const app = build();
16
+ app.addHook("onClose", async () => teardown());
17
+ try {
18
+ // const { database, host, port, user, password } = config.pg;
19
+ if (!config.pg) {
20
+ console.error("empty config.pg, skip...");
21
+ return null;
22
+ }
23
+ if (!Bun.argv[2]) {
24
+ console.error("missing schema / table name, skip...");
25
+ }
26
+ const [key, value] = Bun.argv[2]?.substring?.(2)?.split?.("=") || [];
27
+ const tableName = key === "table" ? value : null;
28
+ const schemaName = key === "schema" ? value : value?.split?.(".")?.shift?.();
29
+ const pg = pgClients.client;
30
+ // const pg = await getPGAsync({ database, host, port, user, password });
31
+ await pg.query(`select 1`);
32
+ const schemaExists = await pg
33
+ .query(`SELECT 1 FROM information_schema.schemata WHERE schema_name = $1`, [schemaName])
34
+ .then((el) => el.rowCount);
35
+ if (!schemaExists) {
36
+ console.error("Вказаної схеми не існує", config.pg?.database);
37
+ return null;
38
+ }
39
+ // if (tableName && !pg.pk?.[tableName]) {
40
+ // console.error('Вказаної таблиці не існує', config.pg?.database);
41
+ // return null;
42
+ // }
43
+ const dump = await schemaItem({
44
+ pg,
45
+ table: tableName,
46
+ schema: schemaName,
47
+ debug,
48
+ });
49
+ if (debug) {
50
+ console.log(dump);
51
+ return null;
52
+ }
53
+ const filepath = await saveFile(dump, tableName || schemaName || "");
54
+ console.log("success", filepath);
55
+ }
56
+ catch (err) {
57
+ console.error(err);
58
+ }
59
+ finally {
60
+ app.close();
61
+ }
62
+ }
63
+ async function saveFile(data, filename) {
64
+ if (!data)
65
+ throw new Error(`no data - ${filename}`);
66
+ const filepath = path.join("log/dump", `${filename}.sql`);
67
+ const fileExists = existsSync(filepath);
68
+ // overwrite old file
69
+ if (fileExists) {
70
+ await rm(filepath, { force: true, recursive: true });
71
+ }
72
+ await mkdir(path.dirname(filepath), { recursive: true });
73
+ await writeFile(filepath, Buffer.from(data, "utf-8"));
74
+ return filepath;
75
+ }
76
+ async function schemaItem({ pg, table, schema, debug, }) {
77
+ if (!schema && !table)
78
+ return new Error("param schema is required");
103
79
  const { rows: schemaInfo } = await pg.query(`select c.oid,relname,nspname,obj_description(c.oid) as description,
104
80
  (
105
81
  select json_agg(row_to_json(q))
@@ -134,10 +110,9 @@ async function schemaItem({
134
110
  from pg_class c
135
111
  LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
136
112
  where ${table ? `nspname||'.'||relname='${table}'` : `'${schema}'=nspname`} and relam=2
137
- order by nspname,relname`);
138
-
139
- if (!schemaInfo?.length) throw new Error('invalid params');
140
-
113
+ order by nspname,relname`);
114
+ if (!schemaInfo?.length)
115
+ throw new Error("invalid params");
141
116
  const { rows: constraints } = await pg.query(`select con.conrelid::regclass as constraint_table, a.column_name,
142
117
  con.conname as constraint_name,contype as constraint_type, con.confrelid::regclass as foreign_table,
143
118
  con.confupdtype, con.confdeltype, con.confmatchtype, u.column_name as foreign_column from pg_constraint con
@@ -151,26 +126,24 @@ async function schemaItem({
151
126
  select column_name from information_schema.constraint_column_usage u
152
127
  where conname=u.constraint_name limit 1
153
128
  )u on 1=1
154
- where ${table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`}`);
155
-
156
- // add table constraints, mermaid
157
- schemaInfo?.forEach((row) => {
158
- // constraint type to column
159
- row?.columns?.forEach((col) => {
160
- const { constraint_type } = constraints?.find((con) => con?.column_name === col?.column_name && con.constraint_table === `${row.nspname}.${row.relname}`) || {};
161
- Object.assign(col, { constraint_type });
162
- });
163
-
164
- // table relations
165
- const tableConstraints = constraints?.filter((el) => el?.constraint_table === `${row.nspname}.${row.relname}`);
166
- Object.assign(row, { constraints: tableConstraints });
167
- });
168
-
169
- if (debug) return schemaInfo;
170
-
171
- const body = await getTemplate('pt', 'schemaItem.pt');
172
-
173
- const schemaContent = await handlebars.compile(body?.hbs || 'template not found: schemaItem.pt')({ nspname: schema, rows: schemaInfo, constraints });
174
-
175
- return schemaContent.replace(/&#x27;/g, "'");
176
- }
129
+ where ${table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`}`);
130
+ // add table constraints, mermaid
131
+ schemaInfo?.forEach((row) => {
132
+ // constraint type to column
133
+ row?.columns?.forEach((col) => {
134
+ const { constraint_type } = constraints?.find((con) => con?.column_name === col?.column_name &&
135
+ con.constraint_table === `${row.nspname}.${row.relname}`) || {};
136
+ Object.assign(col, { constraint_type });
137
+ });
138
+ // table relations
139
+ const tableConstraints = constraints?.filter((el) => el?.constraint_table === `${row.nspname}.${row.relname}`);
140
+ Object.assign(row, { constraints: tableConstraints });
141
+ });
142
+ if (debug)
143
+ return schemaInfo;
144
+ const body = await getTemplate("pt", "schemaItem.pt");
145
+ const schemaContent = await handlebars.compile(typeof body === "string"
146
+ ? body
147
+ : body?.hbs || "template not found schemaItem.pt")({ nspname: schema, rows: schemaInfo, constraints });
148
+ return schemaContent.replace(/&#x27;/g, "'");
149
+ }
@@ -0,0 +1,205 @@
1
+ // Вигрузка схеми бд
2
+ // Usage examples:
3
+ // bun .\script\dump.js --table=bpmn.tasks
4
+ // bun .\script\dump.js --schema=bpmn
5
+
6
+ import { type ExtendedPG } from "../server/types/core.js";
7
+
8
+ import path from "node:path";
9
+ import { existsSync } from "node:fs";
10
+ import { mkdir, writeFile, rm } from "node:fs/promises";
11
+
12
+ import { config, handlebars, pgClients, getTemplate } from "../utils.js";
13
+
14
+ import { build, teardown } from "../helper.js";
15
+
16
+ if (import.meta.main) {
17
+ dumpMigrateSQL();
18
+ }
19
+
20
+ const debug = false;
21
+
22
+ export default async function dumpMigrateSQL() {
23
+ const app = build();
24
+ app.addHook("onClose", async () => teardown());
25
+
26
+ try {
27
+ // const { database, host, port, user, password } = config.pg;
28
+
29
+ if (!config.pg) {
30
+ console.error("empty config.pg, skip...");
31
+ return null;
32
+ }
33
+
34
+ if (!Bun.argv[2]) {
35
+ console.error("missing schema / table name, skip...");
36
+ }
37
+
38
+ const [key, value] = Bun.argv[2]?.substring?.(2)?.split?.("=") || [];
39
+ const tableName = key === "table" ? value : null;
40
+ const schemaName =
41
+ key === "schema" ? value : value?.split?.(".")?.shift?.();
42
+
43
+ const pg = pgClients.client;
44
+ // const pg = await getPGAsync({ database, host, port, user, password });
45
+ await pg.query(`select 1`);
46
+
47
+ const schemaExists = await pg
48
+ .query(
49
+ `SELECT 1 FROM information_schema.schemata WHERE schema_name = $1`,
50
+ [schemaName]
51
+ )
52
+ .then((el: any) => el.rowCount);
53
+
54
+ if (!schemaExists) {
55
+ console.error("Вказаної схеми не існує", config.pg?.database);
56
+ return null;
57
+ }
58
+
59
+ // if (tableName && !pg.pk?.[tableName]) {
60
+ // console.error('Вказаної таблиці не існує', config.pg?.database);
61
+ // return null;
62
+ // }
63
+
64
+ const dump = await schemaItem({
65
+ pg,
66
+ table: tableName,
67
+ schema: schemaName,
68
+ debug,
69
+ });
70
+
71
+ if (debug) {
72
+ console.log(dump);
73
+ return null;
74
+ }
75
+
76
+ const filepath = await saveFile(dump, tableName || schemaName || "");
77
+ console.log("success", filepath);
78
+ } catch (err) {
79
+ console.error(err);
80
+ } finally {
81
+ app.close();
82
+ }
83
+ }
84
+
85
+ async function saveFile(data: any, filename: string) {
86
+ if (!data) throw new Error(`no data - ${filename}`);
87
+
88
+ const filepath = path.join("log/dump", `${filename}.sql`);
89
+ const fileExists = existsSync(filepath);
90
+
91
+ // overwrite old file
92
+ if (fileExists) {
93
+ await rm(filepath, { force: true, recursive: true });
94
+ }
95
+
96
+ await mkdir(path.dirname(filepath), { recursive: true });
97
+ await writeFile(filepath, Buffer.from(data, "utf-8"));
98
+
99
+ return filepath;
100
+ }
101
+
102
+ async function schemaItem({
103
+ pg,
104
+ table,
105
+ schema,
106
+ debug,
107
+ }: {
108
+ pg: ExtendedPG;
109
+ table?: string | null;
110
+ schema?: string;
111
+ debug?: boolean;
112
+ }) {
113
+ if (!schema && !table) return new Error("param schema is required");
114
+
115
+ const { rows: schemaInfo } =
116
+ await pg.query(`select c.oid,relname,nspname,obj_description(c.oid) as description,
117
+ (
118
+ select json_agg(row_to_json(q))
119
+ from (
120
+ select
121
+ column_name,
122
+ case
123
+ when data_type='USER-DEFINED' AND udt_name='geometry' THEN 'geometry'
124
+ when data_type='ARRAY' AND udt_name='_text' THEN 'text[]'
125
+ when data_type='ARRAY' AND udt_name='_int4' THEN 'integer[]'
126
+ else data_type
127
+ end as data_type,
128
+ ordinal_position,
129
+ column_default,
130
+ is_nullable,
131
+ case
132
+ when column_name='uid' then 'ідентифікатор автора запису в БД'
133
+ when column_name='cdate' then 'Дата створення запису в БД'
134
+ when column_name='editor_id' then 'Ідентифікатор автора, який останій вніс зміни в запис'
135
+ when column_name='editor_date' then 'Час останії зміни в записі'
136
+ when column_name='files' then 'Системна колонка'
137
+ when column_name='doc_status' then 'Статус документа'
138
+ when column_name='reg_status' then 'Статус реєстрації'
139
+ when column_name='obj_version' then 'Версія запису'
140
+ else col_description(a.attrelid,ordinal_position)
141
+ end as description
142
+ from information_schema.columns col
143
+ LEFT JOIN pg_attribute a ON col.column_name=a.attname and c.oid = a.attrelid
144
+ where col.table_schema=nspname and col.table_name=relname
145
+ )q
146
+ ) as columns
147
+ from pg_class c
148
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
149
+ where ${
150
+ table ? `nspname||'.'||relname='${table}'` : `'${schema}'=nspname`
151
+ } and relam=2
152
+ order by nspname,relname`);
153
+
154
+ if (!schemaInfo?.length) throw new Error("invalid params");
155
+
156
+ const { rows: constraints } =
157
+ await pg.query(`select con.conrelid::regclass as constraint_table, a.column_name,
158
+ con.conname as constraint_name,contype as constraint_type, con.confrelid::regclass as foreign_table,
159
+ con.confupdtype, con.confdeltype, con.confmatchtype, u.column_name as foreign_column from pg_constraint con
160
+ left join pg_class c ON c.oid = con.conrelid
161
+ left join pg_namespace n ON n.oid = c.relnamespace
162
+ left join lateral (
163
+ select string_agg(a.attname,',') as column_name from pg_attribute a
164
+ where con.conrelid = a.attrelid and a.attnum = any(con.conkey) limit 1
165
+ )a on 1=1
166
+ left join lateral (
167
+ select column_name from information_schema.constraint_column_usage u
168
+ where conname=u.constraint_name limit 1
169
+ )u on 1=1
170
+ where ${
171
+ table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`
172
+ }`);
173
+
174
+ // add table constraints, mermaid
175
+ schemaInfo?.forEach((row: any) => {
176
+ // constraint type to column
177
+ row?.columns?.forEach((col: any) => {
178
+ const { constraint_type } =
179
+ constraints?.find(
180
+ (con: any) =>
181
+ con?.column_name === col?.column_name &&
182
+ con.constraint_table === `${row.nspname}.${row.relname}`
183
+ ) || {};
184
+ Object.assign(col, { constraint_type });
185
+ });
186
+
187
+ // table relations
188
+ const tableConstraints = constraints?.filter(
189
+ (el: any) => el?.constraint_table === `${row.nspname}.${row.relname}`
190
+ );
191
+ Object.assign(row, { constraints: tableConstraints });
192
+ });
193
+
194
+ if (debug) return schemaInfo;
195
+
196
+ const body = await getTemplate("pt", "schemaItem.pt");
197
+
198
+ const schemaContent = await handlebars.compile(
199
+ typeof body === "string"
200
+ ? body
201
+ : body?.hbs || "template not found schemaItem.pt"
202
+ )({ nspname: schema, rows: schemaInfo, constraints });
203
+
204
+ return schemaContent.replace(/&#x27;/g, "'");
205
+ }