@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.
- package/dist/helper.d.ts +8 -0
- package/dist/helper.d.ts.map +1 -0
- package/dist/helper.js +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/module/core/cls/constraint_action.json +10 -0
- package/dist/module/core/cls/constraint_matchtype.json +6 -0
- package/dist/module/core/cls/constraint_type.json +14 -0
- package/dist/module/core/cls/constraint_type_full.json +18 -0
- package/dist/module/core/cls/constraint_type_table.json +18 -0
- package/dist/module/core/cls/core.user_type.json +14 -0
- package/dist/module/core/pt/schemaItem.pt.hbs +18 -0
- package/dist/module/core/select/core.user_mentioned.sql +2 -0
- package/dist/script/dump.d.ts +2 -0
- package/dist/script/dump.d.ts.map +1 -0
- package/dist/script/dump.js +102 -129
- package/dist/script/dump.ts +205 -0
- package/dist/script/migrate.d.ts +2 -0
- package/dist/script/migrate.d.ts.map +1 -0
- package/dist/script/migrate.js +24 -25
- package/dist/script/migrate.ts +31 -0
- package/dist/server/plugins/sqlite/funcs/getSqlite.d.ts.map +1 -1
- package/dist/server/plugins/sqlite/funcs/getSqlite.js +1 -2
- package/dist/server/plugins/sqlite/sqliteClients.d.ts.map +1 -1
- package/dist/server/plugins/sqlite/sqliteClients.js +1 -2
- package/dist/server/plugins/table/funcs/getColumnCLS.d.ts.map +1 -1
- package/dist/server/plugins/table/funcs/getColumnCLS.js +18 -7
- package/dist/server/plugins/util/funcs/eventStream.d.ts +4 -1
- package/dist/server/plugins/util/funcs/eventStream.d.ts.map +1 -1
- package/dist/server/routes/file/controllers/export.d.ts.map +1 -1
- package/dist/server/routes/file/controllers/export.js +21 -14
- package/dist/server/routes/file/controllers/utils/pubsub.d.ts +2 -0
- package/dist/server/routes/file/controllers/utils/pubsub.d.ts.map +1 -0
- package/dist/server/routes/file/controllers/utils/pubsub.js +112 -0
- package/dist/server/routes/table/controllers/dataInfo.d.ts +8 -1
- package/dist/server/routes/table/controllers/dataInfo.d.ts.map +1 -1
- package/dist/server/routes/table/controllers/dataInfo.js +7 -5
- package/dist/server/types/core.d.ts.map +1 -1
- package/dist/server/types/core.js +0 -1
- package/dist/test.setup.js +0 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -0
- package/package.json +5 -4
- package/dist/log/migration/dist-geoname-cls.json +0 -1
- package/dist/log/migration/dist-geoname-cls.sql +0 -0
- package/dist/server/plugins/upload/finishUpload.d.ts +0 -9
- package/dist/server/plugins/upload/finishUpload.d.ts.map +0 -1
- package/dist/server/plugins/upload/finishUpload.js +0 -33
- package/dist/server/plugins/util/funcs/routeOptions.d.ts +0 -3
- package/dist/server/plugins/util/funcs/routeOptions.d.ts.map +0 -1
- package/dist/server/plugins/util/funcs/routeOptions.js +0 -2
- package/dist/server/routes/auth/controllers/2factor/generate.d.ts +0 -19
- package/dist/server/routes/auth/controllers/2factor/generate.d.ts.map +0 -1
- package/dist/server/routes/auth/controllers/2factor/generate.js +0 -38
- package/dist/server/routes/auth/controllers/2factor/toggle.d.ts +0 -18
- package/dist/server/routes/auth/controllers/2factor/toggle.d.ts.map +0 -1
- package/dist/server/routes/auth/controllers/2factor/toggle.js +0 -39
package/dist/helper.d.ts
ADDED
|
@@ -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, };
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"
|
|
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,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 @@
|
|
|
1
|
+
{"version":3,"file":"dump.d.ts","sourceRoot":"","sources":["../../script/dump.ts"],"names":[],"mappings":"AAqBA,wBAA8B,cAAc,8BA6D3C"}
|
package/dist/script/dump.js
CHANGED
|
@@ -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
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
app
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
async function
|
|
77
|
-
if (!
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Object.assign(col, { constraint_type });
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
return schemaContent.replace(/'/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(/'/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(/'/g, "'");
|
|
205
|
+
}
|