@opengis/fastify-table 2.4.0 → 2.4.2
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/README.md +86 -86
- package/dist/functions.js +20 -20
- package/dist/module/core/cls/constraint_action.json +9 -9
- package/dist/module/core/cls/constraint_matchtype.json +5 -5
- package/dist/module/core/cls/constraint_type_full.json +17 -17
- package/dist/module/core/cls/core.user_type.json +13 -13
- package/dist/module/core/pt/schemaItem.pt.hbs +17 -17
- package/dist/script/adduser +14 -14
- package/dist/script/dump.js +48 -48
- package/dist/script/dump.ts +216 -216
- package/dist/script/migrate.ts +41 -41
- package/dist/server/helpers/core/badge.js +1 -1
- package/dist/server/helpers/list/descriptionList.js +8 -8
- package/dist/server/helpers/list/tableList.js +4 -4
- package/dist/server/helpers/list/utils/button.js +1 -1
- package/dist/server/helpers/list/utils/buttonDel.js +3 -3
- package/dist/server/helpers/list/utils/buttonEdit.js +3 -3
- package/dist/server/helpers/utils/button.js +1 -1
- package/dist/server/helpers/utils/buttonAdd.js +15 -15
- package/dist/server/helpers/utils/buttonDel.js +11 -11
- package/dist/server/helpers/utils/buttonEdit.js +3 -3
- package/dist/server/migrations/0.sql +99 -99
- package/dist/server/migrations/cls.sql +105 -105
- package/dist/server/migrations/context.sql +136 -136
- package/dist/server/migrations/oauth.sql +79 -79
- package/dist/server/migrations/properties.sql +115 -115
- package/dist/server/migrations/roles.sql +195 -195
- package/dist/server/migrations/template.sql +43 -43
- package/dist/server/migrations/users.sql +151 -151
- package/dist/server/plugins/access/funcs/getUserPermissions.js +7 -7
- package/dist/server/plugins/auth/funcs/authorizeUser.js +4 -4
- package/dist/server/plugins/auth/funcs/getQuery.js +20 -20
- package/dist/server/plugins/crud/funcs/dataUpdate.js +7 -7
- package/dist/server/plugins/crud/funcs/getAccess.js +14 -14
- package/dist/server/plugins/crud/funcs/utils/getInsertQuery.js +6 -6
- package/dist/server/plugins/crud/funcs/utils/logChanges.js +18 -18
- package/dist/server/plugins/grpc/utils/convertp.proto +136 -136
- package/dist/server/plugins/grpc/utils/htmlTemplate.js +10 -10
- package/dist/server/plugins/grpc/utils/office2pdf.proto +13 -13
- package/dist/server/plugins/logger/errorMessage.d.ts.map +1 -1
- package/dist/server/plugins/logger/errorMessage.js +3 -0
- package/dist/server/plugins/metric/loggerSystem.js +1 -1
- package/dist/server/plugins/pg/funcs/autoIndex.js +5 -5
- package/dist/server/plugins/pg/funcs/getMeta.js +10 -10
- package/dist/server/plugins/pg/funcs/init.js +36 -36
- package/dist/server/plugins/sqlite/funcs/init.js +22 -22
- package/dist/server/plugins/table/funcs/getFilterSQL/util/getCustomQuery.js +1 -1
- package/dist/server/plugins/table/funcs/getSelect.js +1 -1
- package/dist/server/plugins/table/funcs/gisIRColumn.js +3 -3
- package/dist/server/plugins/usercls/index.js +2 -2
- package/dist/server/routes/access/controllers/access.group.js +6 -6
- package/dist/server/routes/access/controllers/access.group.post.js +5 -5
- package/dist/server/routes/access/controllers/access.interface.js +14 -14
- package/dist/server/routes/access/controllers/access.user.js +6 -6
- package/dist/server/routes/auth/controllers/2factor/providers/totp.js +5 -5
- package/dist/server/routes/auth/controllers/2factor/qrcode.js +1 -1
- package/dist/server/routes/auth/controllers/2factor/recovery.js +1 -1
- package/dist/server/routes/auth/controllers/2factor/verify.js +1 -1
- package/dist/server/routes/auth/controllers/core/getUserInfo.js +33 -33
- package/dist/server/routes/auth/controllers/core/passwordRecovery.js +1 -1
- package/dist/server/routes/auth/controllers/core/registration.js +2 -2
- package/dist/server/routes/auth/controllers/page/login2faTemplate.js +1 -1
- package/dist/server/routes/cron/controllers/cronApi.d.ts.map +1 -1
- package/dist/server/routes/cron/controllers/cronApi.js +9 -4
- package/dist/server/routes/file/controllers/resizeAll.js +6 -6
- package/dist/server/routes/grpc/controllers/file2geojson.js +13 -13
- package/dist/server/routes/menu/controllers/getMenu.js +9 -9
- package/dist/server/routes/notifications/controllers/readNotifications.js +4 -4
- package/dist/server/routes/notifications/controllers/userNotifications.js +3 -3
- package/dist/server/routes/table/controllers/card.js +1 -1
- package/dist/server/routes/table/controllers/filter.js +6 -6
- package/dist/server/routes/table/controllers/form.js +1 -1
- package/dist/server/routes/table/controllers/getFormByTable.js +6 -6
- package/dist/server/routes/table/controllers/suggest.js +3 -3
- package/dist/server/routes/table/controllers/tableData.js +2 -2
- package/dist/server/routes/table/controllers/tableInfo.js +10 -10
- package/dist/server/routes/table/functions/getData.js +13 -13
- package/dist/server/routes/widget/controllers/widget.get.js +33 -33
- package/dist/server/routes/widget/controllers/widget.set.js +3 -3
- package/dist/server/templates/page/2factor-recovery.html +101 -101
- package/dist/server/templates/page/2factor.html +140 -140
- package/dist/server/templates/page/login.html +90 -90
- package/dist/server/templates/page/loginEuSign.html +123 -123
- package/dist/server/templates/pt/recovery-codes-email-template.hbs +12 -12
- package/dist/server/templates/pt/recovery-password-email-template.html +20 -20
- package/package.json +98 -98
package/dist/script/dump.ts
CHANGED
|
@@ -1,216 +1,216 @@
|
|
|
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 = await 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
|
-
// app.close();
|
|
79
|
-
} catch (err) {
|
|
80
|
-
console.error(err);
|
|
81
|
-
// app.close();
|
|
82
|
-
} finally {
|
|
83
|
-
await app.close();
|
|
84
|
-
process.exit(0);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
async function saveFile(data: any, filename: string) {
|
|
89
|
-
if (!data) throw new Error(`no data - ${filename}`);
|
|
90
|
-
|
|
91
|
-
const filepath = path.join("log/dump", `${filename}.sql`);
|
|
92
|
-
const fileExists = existsSync(filepath);
|
|
93
|
-
|
|
94
|
-
// overwrite old file
|
|
95
|
-
if (fileExists) {
|
|
96
|
-
await rm(filepath, { force: true, recursive: true });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
await mkdir(path.dirname(filepath), { recursive: true });
|
|
100
|
-
await writeFile(filepath, Buffer.from(data, "utf-8"));
|
|
101
|
-
|
|
102
|
-
return filepath;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async function schemaItem({
|
|
106
|
-
pg,
|
|
107
|
-
table,
|
|
108
|
-
schema,
|
|
109
|
-
debug,
|
|
110
|
-
}: {
|
|
111
|
-
pg: ExtendedPG;
|
|
112
|
-
table?: string | null;
|
|
113
|
-
schema?: string;
|
|
114
|
-
debug?: boolean;
|
|
115
|
-
}) {
|
|
116
|
-
if (!schema && !table) return new Error("param schema is required");
|
|
117
|
-
|
|
118
|
-
const { rows: schemaInfo } =
|
|
119
|
-
await pg.query(`select c.oid,relname,nspname,obj_description(c.oid) as description,
|
|
120
|
-
(
|
|
121
|
-
select json_agg(row_to_json(q))
|
|
122
|
-
from (
|
|
123
|
-
select
|
|
124
|
-
column_name,
|
|
125
|
-
case
|
|
126
|
-
when data_type='USER-DEFINED' AND udt_name='geometry' THEN 'geometry'
|
|
127
|
-
when data_type='ARRAY' AND udt_name='_text' THEN 'text[]'
|
|
128
|
-
when data_type='ARRAY' AND udt_name='_int4' THEN 'integer[]'
|
|
129
|
-
else data_type
|
|
130
|
-
end as data_type,
|
|
131
|
-
ordinal_position,
|
|
132
|
-
column_default,
|
|
133
|
-
is_nullable,
|
|
134
|
-
case
|
|
135
|
-
when column_name='uid' then 'ідентифікатор автора запису в БД'
|
|
136
|
-
when column_name='cdate' then 'Дата створення запису в БД'
|
|
137
|
-
when column_name='editor_id' then 'Ідентифікатор автора, який останій вніс зміни в запис'
|
|
138
|
-
when column_name='editor_date' then 'Час останії зміни в записі'
|
|
139
|
-
when column_name='files' then 'Системна колонка'
|
|
140
|
-
when column_name='doc_status' then 'Статус документа'
|
|
141
|
-
when column_name='reg_status' then 'Статус реєстрації'
|
|
142
|
-
when column_name='obj_version' then 'Версія запису'
|
|
143
|
-
else col_description(a.attrelid,ordinal_position)
|
|
144
|
-
end as description
|
|
145
|
-
from information_schema.columns col
|
|
146
|
-
LEFT JOIN pg_attribute a ON col.column_name=a.attname and c.oid = a.attrelid
|
|
147
|
-
where col.table_schema=nspname and col.table_name=relname
|
|
148
|
-
)q
|
|
149
|
-
) as columns
|
|
150
|
-
from pg_class c
|
|
151
|
-
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
152
|
-
where ${
|
|
153
|
-
table ? `nspname||'.'||relname='${table}'` : `'${schema}'=nspname`
|
|
154
|
-
} and relam=2
|
|
155
|
-
order by nspname,relname`);
|
|
156
|
-
|
|
157
|
-
if (!schemaInfo?.length) throw new Error("invalid params");
|
|
158
|
-
|
|
159
|
-
const { rows: constraints } =
|
|
160
|
-
await pg.query(`select con.conrelid::regclass as constraint_table, a.column_name,
|
|
161
|
-
con.conname as constraint_name,contype as constraint_type, con.confrelid::regclass as foreign_table,
|
|
162
|
-
con.confupdtype, con.confdeltype, con.confmatchtype, u.column_name as foreign_column,
|
|
163
|
-
case when contype='c' then pg_get_constraintdef(con.oid, true) else null end AS check_definition from pg_constraint con
|
|
164
|
-
left join pg_class c ON c.oid = con.conrelid
|
|
165
|
-
left join pg_namespace n ON n.oid = c.relnamespace
|
|
166
|
-
left join lateral (
|
|
167
|
-
select string_agg(a.attname,',') as column_name from pg_attribute a
|
|
168
|
-
where con.conrelid = a.attrelid and a.attnum = any(con.conkey) limit 1
|
|
169
|
-
)a on 1=1
|
|
170
|
-
left join lateral (
|
|
171
|
-
select column_name from information_schema.constraint_column_usage u
|
|
172
|
-
where conname=u.constraint_name limit 1
|
|
173
|
-
)u on 1=1
|
|
174
|
-
where ${
|
|
175
|
-
table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`
|
|
176
|
-
}`);
|
|
177
|
-
|
|
178
|
-
// add table constraints, mermaid
|
|
179
|
-
schemaInfo?.forEach((row: any) => {
|
|
180
|
-
// constraint type to column
|
|
181
|
-
row?.columns?.forEach((col: any) => {
|
|
182
|
-
const { constraint_type } =
|
|
183
|
-
constraints?.find(
|
|
184
|
-
(con: any) =>
|
|
185
|
-
con?.column_name === col?.column_name &&
|
|
186
|
-
con.constraint_table === `${row.nspname}.${row.relname}`
|
|
187
|
-
) || {};
|
|
188
|
-
Object.assign(col, { constraint_type });
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
// table relations
|
|
192
|
-
const tableConstraints = constraints
|
|
193
|
-
?.filter(
|
|
194
|
-
(el: any) => el?.constraint_table === `${row.nspname}.${row.relname}`
|
|
195
|
-
)
|
|
196
|
-
?.map((el: any) => ({
|
|
197
|
-
...el,
|
|
198
|
-
check_definition: el.check_definition
|
|
199
|
-
? new handlebars.SafeString(el.check_definition)
|
|
200
|
-
: null,
|
|
201
|
-
}));
|
|
202
|
-
Object.assign(row, { constraints: tableConstraints });
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
if (debug) return schemaInfo;
|
|
206
|
-
|
|
207
|
-
const body = await getTemplate("pt", "schemaItem.pt");
|
|
208
|
-
|
|
209
|
-
const schemaContent = await handlebars.compile(
|
|
210
|
-
typeof body === "string"
|
|
211
|
-
? body
|
|
212
|
-
: body?.hbs || "template not found schemaItem.pt"
|
|
213
|
-
)({ nspname: schema, rows: schemaInfo, constraints });
|
|
214
|
-
|
|
215
|
-
return schemaContent.replace(/'/g, "'");
|
|
216
|
-
}
|
|
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 = await 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
|
+
// app.close();
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.error(err);
|
|
81
|
+
// app.close();
|
|
82
|
+
} finally {
|
|
83
|
+
await app.close();
|
|
84
|
+
process.exit(0);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function saveFile(data: any, filename: string) {
|
|
89
|
+
if (!data) throw new Error(`no data - ${filename}`);
|
|
90
|
+
|
|
91
|
+
const filepath = path.join("log/dump", `${filename}.sql`);
|
|
92
|
+
const fileExists = existsSync(filepath);
|
|
93
|
+
|
|
94
|
+
// overwrite old file
|
|
95
|
+
if (fileExists) {
|
|
96
|
+
await rm(filepath, { force: true, recursive: true });
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await mkdir(path.dirname(filepath), { recursive: true });
|
|
100
|
+
await writeFile(filepath, Buffer.from(data, "utf-8"));
|
|
101
|
+
|
|
102
|
+
return filepath;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function schemaItem({
|
|
106
|
+
pg,
|
|
107
|
+
table,
|
|
108
|
+
schema,
|
|
109
|
+
debug,
|
|
110
|
+
}: {
|
|
111
|
+
pg: ExtendedPG;
|
|
112
|
+
table?: string | null;
|
|
113
|
+
schema?: string;
|
|
114
|
+
debug?: boolean;
|
|
115
|
+
}) {
|
|
116
|
+
if (!schema && !table) return new Error("param schema is required");
|
|
117
|
+
|
|
118
|
+
const { rows: schemaInfo } =
|
|
119
|
+
await pg.query(`select c.oid,relname,nspname,obj_description(c.oid) as description,
|
|
120
|
+
(
|
|
121
|
+
select json_agg(row_to_json(q))
|
|
122
|
+
from (
|
|
123
|
+
select
|
|
124
|
+
column_name,
|
|
125
|
+
case
|
|
126
|
+
when data_type='USER-DEFINED' AND udt_name='geometry' THEN 'geometry'
|
|
127
|
+
when data_type='ARRAY' AND udt_name='_text' THEN 'text[]'
|
|
128
|
+
when data_type='ARRAY' AND udt_name='_int4' THEN 'integer[]'
|
|
129
|
+
else data_type
|
|
130
|
+
end as data_type,
|
|
131
|
+
ordinal_position,
|
|
132
|
+
column_default,
|
|
133
|
+
is_nullable,
|
|
134
|
+
case
|
|
135
|
+
when column_name='uid' then 'ідентифікатор автора запису в БД'
|
|
136
|
+
when column_name='cdate' then 'Дата створення запису в БД'
|
|
137
|
+
when column_name='editor_id' then 'Ідентифікатор автора, який останій вніс зміни в запис'
|
|
138
|
+
when column_name='editor_date' then 'Час останії зміни в записі'
|
|
139
|
+
when column_name='files' then 'Системна колонка'
|
|
140
|
+
when column_name='doc_status' then 'Статус документа'
|
|
141
|
+
when column_name='reg_status' then 'Статус реєстрації'
|
|
142
|
+
when column_name='obj_version' then 'Версія запису'
|
|
143
|
+
else col_description(a.attrelid,ordinal_position)
|
|
144
|
+
end as description
|
|
145
|
+
from information_schema.columns col
|
|
146
|
+
LEFT JOIN pg_attribute a ON col.column_name=a.attname and c.oid = a.attrelid
|
|
147
|
+
where col.table_schema=nspname and col.table_name=relname
|
|
148
|
+
)q
|
|
149
|
+
) as columns
|
|
150
|
+
from pg_class c
|
|
151
|
+
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
152
|
+
where ${
|
|
153
|
+
table ? `nspname||'.'||relname='${table}'` : `'${schema}'=nspname`
|
|
154
|
+
} and relam=2
|
|
155
|
+
order by nspname,relname`);
|
|
156
|
+
|
|
157
|
+
if (!schemaInfo?.length) throw new Error("invalid params");
|
|
158
|
+
|
|
159
|
+
const { rows: constraints } =
|
|
160
|
+
await pg.query(`select con.conrelid::regclass as constraint_table, a.column_name,
|
|
161
|
+
con.conname as constraint_name,contype as constraint_type, con.confrelid::regclass as foreign_table,
|
|
162
|
+
con.confupdtype, con.confdeltype, con.confmatchtype, u.column_name as foreign_column,
|
|
163
|
+
case when contype='c' then pg_get_constraintdef(con.oid, true) else null end AS check_definition from pg_constraint con
|
|
164
|
+
left join pg_class c ON c.oid = con.conrelid
|
|
165
|
+
left join pg_namespace n ON n.oid = c.relnamespace
|
|
166
|
+
left join lateral (
|
|
167
|
+
select string_agg(a.attname,',') as column_name from pg_attribute a
|
|
168
|
+
where con.conrelid = a.attrelid and a.attnum = any(con.conkey) limit 1
|
|
169
|
+
)a on 1=1
|
|
170
|
+
left join lateral (
|
|
171
|
+
select column_name from information_schema.constraint_column_usage u
|
|
172
|
+
where conname=u.constraint_name limit 1
|
|
173
|
+
)u on 1=1
|
|
174
|
+
where ${
|
|
175
|
+
table ? `conrelid::regclass::text = '${table}'` : `nspname = '${schema}'`
|
|
176
|
+
}`);
|
|
177
|
+
|
|
178
|
+
// add table constraints, mermaid
|
|
179
|
+
schemaInfo?.forEach((row: any) => {
|
|
180
|
+
// constraint type to column
|
|
181
|
+
row?.columns?.forEach((col: any) => {
|
|
182
|
+
const { constraint_type } =
|
|
183
|
+
constraints?.find(
|
|
184
|
+
(con: any) =>
|
|
185
|
+
con?.column_name === col?.column_name &&
|
|
186
|
+
con.constraint_table === `${row.nspname}.${row.relname}`
|
|
187
|
+
) || {};
|
|
188
|
+
Object.assign(col, { constraint_type });
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// table relations
|
|
192
|
+
const tableConstraints = constraints
|
|
193
|
+
?.filter(
|
|
194
|
+
(el: any) => el?.constraint_table === `${row.nspname}.${row.relname}`
|
|
195
|
+
)
|
|
196
|
+
?.map((el: any) => ({
|
|
197
|
+
...el,
|
|
198
|
+
check_definition: el.check_definition
|
|
199
|
+
? new handlebars.SafeString(el.check_definition)
|
|
200
|
+
: null,
|
|
201
|
+
}));
|
|
202
|
+
Object.assign(row, { constraints: tableConstraints });
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (debug) return schemaInfo;
|
|
206
|
+
|
|
207
|
+
const body = await getTemplate("pt", "schemaItem.pt");
|
|
208
|
+
|
|
209
|
+
const schemaContent = await handlebars.compile(
|
|
210
|
+
typeof body === "string"
|
|
211
|
+
? body
|
|
212
|
+
: body?.hbs || "template not found schemaItem.pt"
|
|
213
|
+
)({ nspname: schema, rows: schemaInfo, constraints });
|
|
214
|
+
|
|
215
|
+
return schemaContent.replace(/'/g, "'");
|
|
216
|
+
}
|
package/dist/script/migrate.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import Fastify from "fastify";
|
|
3
|
-
|
|
4
|
-
import config from "../config.js";
|
|
5
|
-
import plugin from "../index.js";
|
|
6
|
-
|
|
7
|
-
import execMigrations from "../server/plugins/migration/exec.migrations.js";
|
|
8
|
-
import pgClients from "../server/plugins/pg/pgClients.js";
|
|
9
|
-
|
|
10
|
-
const timeoutMs = +(config.migrationTimeout || 5000);
|
|
11
|
-
|
|
12
|
-
const port = process.env.PORT || config.port || 3000;
|
|
13
|
-
|
|
14
|
-
if (import.meta.main) {
|
|
15
|
-
migrate();
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export default function migrate() {
|
|
19
|
-
const app = Fastify();
|
|
20
|
-
|
|
21
|
-
app.register(plugin, config);
|
|
22
|
-
|
|
23
|
-
app.listen({ host: "0.0.0.0", port }, (err: any) => {
|
|
24
|
-
console.log(`Server started via port: ${port}`);
|
|
25
|
-
|
|
26
|
-
execMigrations(
|
|
27
|
-
path.join(process.cwd(), "server/migrations"),
|
|
28
|
-
pgClients.client
|
|
29
|
-
).catch((err) => console.error(err.toString()));
|
|
30
|
-
|
|
31
|
-
setTimeout(() => {
|
|
32
|
-
console.log("Server closed after timeout", timeoutMs);
|
|
33
|
-
app.close();
|
|
34
|
-
process.exit(0);
|
|
35
|
-
}, timeoutMs);
|
|
36
|
-
if (err) {
|
|
37
|
-
console.error("migrations error", err.toString());
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import Fastify from "fastify";
|
|
3
|
+
|
|
4
|
+
import config from "../config.js";
|
|
5
|
+
import plugin from "../index.js";
|
|
6
|
+
|
|
7
|
+
import execMigrations from "../server/plugins/migration/exec.migrations.js";
|
|
8
|
+
import pgClients from "../server/plugins/pg/pgClients.js";
|
|
9
|
+
|
|
10
|
+
const timeoutMs = +(config.migrationTimeout || 5000);
|
|
11
|
+
|
|
12
|
+
const port = process.env.PORT || config.port || 3000;
|
|
13
|
+
|
|
14
|
+
if (import.meta.main) {
|
|
15
|
+
migrate();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default function migrate() {
|
|
19
|
+
const app = Fastify();
|
|
20
|
+
|
|
21
|
+
app.register(plugin, config);
|
|
22
|
+
|
|
23
|
+
app.listen({ host: "0.0.0.0", port }, (err: any) => {
|
|
24
|
+
console.log(`Server started via port: ${port}`);
|
|
25
|
+
|
|
26
|
+
execMigrations(
|
|
27
|
+
path.join(process.cwd(), "server/migrations"),
|
|
28
|
+
pgClients.client
|
|
29
|
+
).catch((err) => console.error(err.toString()));
|
|
30
|
+
|
|
31
|
+
setTimeout(() => {
|
|
32
|
+
console.log("Server closed after timeout", timeoutMs);
|
|
33
|
+
app.close();
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}, timeoutMs);
|
|
36
|
+
if (err) {
|
|
37
|
+
console.error("migrations error", err.toString());
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -11,6 +11,6 @@ export default async function badge(id, options) {
|
|
|
11
11
|
const item = classifier?.arr.find((el) => el.id?.toString?.() === id.toString());
|
|
12
12
|
if (!item)
|
|
13
13
|
return id;
|
|
14
|
-
return `<span style='color:${item.color || '#14b8a6'};border-color:${item.color || '#14b8a6'}; overflow:hidden;
|
|
14
|
+
return `<span style='color:${item.color || '#14b8a6'};border-color:${item.color || '#14b8a6'}; overflow:hidden;
|
|
15
15
|
text-overflow:ellipsis; white-space:nowrap;' class="block w-fit py-1.5 px-3 rounded-full text-xs font-medium border border-teal-500 text-teal-500 max-w-[220px] text-nowrap ">${item.text}</span>`;
|
|
16
16
|
}
|
|
@@ -33,14 +33,14 @@ export default async function descriptionList(data, opt) {
|
|
|
33
33
|
continue;
|
|
34
34
|
const key = keys[i + 1];
|
|
35
35
|
const d1 = (await format(data[key], key, data)) || '-';
|
|
36
|
-
result.push(`<div class="flex flex-wrap sm:grid sm:grid-cols-3 gap-1 py-3 even:bg-gray-50 text-[12px]">
|
|
37
|
-
<dt class="text-gray-900 w-full sm:w-auto sm:col-span-1 pr-2 break-words">
|
|
38
|
-
${nameHBS || name}
|
|
39
|
-
</dt>
|
|
40
|
-
|
|
41
|
-
<dd class="text-gray-700 sm:col-span-2 w-full sm:w-auto break-words whitespace-normal">
|
|
42
|
-
${d1}
|
|
43
|
-
</dd>
|
|
36
|
+
result.push(`<div class="flex flex-wrap sm:grid sm:grid-cols-3 gap-1 py-3 even:bg-gray-50 text-[12px]">
|
|
37
|
+
<dt class="text-gray-900 w-full sm:w-auto sm:col-span-1 pr-2 break-words">
|
|
38
|
+
${nameHBS || name}
|
|
39
|
+
</dt>
|
|
40
|
+
|
|
41
|
+
<dd class="text-gray-700 sm:col-span-2 w-full sm:w-auto break-words whitespace-normal">
|
|
42
|
+
${d1}
|
|
43
|
+
</dd>
|
|
44
44
|
</div>`);
|
|
45
45
|
}
|
|
46
46
|
return `<dl class=" divide-y divide-gray-100 py-[5px] w-full">${result.join('')}</dl>`;
|
|
@@ -65,8 +65,8 @@ export default async function tableList(data, opt) {
|
|
|
65
65
|
const isActionsColumn = hash.noactions && i === keys.length - 2;
|
|
66
66
|
result.push(`<th class="py-2 min-w-[200px] ${isActionsColumn
|
|
67
67
|
? "last:min-w-[60px] last:max-w-[60px] last:bg-white last:sticky last:right-0"
|
|
68
|
-
: ""}">
|
|
69
|
-
${nameHBS || name}
|
|
68
|
+
: ""}">
|
|
69
|
+
${nameHBS || name}
|
|
70
70
|
</th>`);
|
|
71
71
|
}
|
|
72
72
|
result.push('</tr></thead><tbody class="divide-y divide-gray-200">');
|
|
@@ -90,9 +90,9 @@ export default async function tableList(data, opt) {
|
|
|
90
90
|
? (await handlebars.compile(key)({ ...row, token, hash })) || "-"
|
|
91
91
|
: null;
|
|
92
92
|
const isActionsColumn = hash.noactions && i === keys.length - 2;
|
|
93
|
-
result.push(`<td class="py-2 pr-5 ${isActionsColumn ? "last:sticky last:right-0" : ""}">
|
|
93
|
+
result.push(`<td class="py-2 pr-5 ${isActionsColumn ? "last:sticky last:right-0" : ""}">
|
|
94
94
|
${d1 ||
|
|
95
|
-
format(tokenData || row[key], key, row, hash)}
|
|
95
|
+
format(tokenData || row[key], key, row, hash)}
|
|
96
96
|
</td>`);
|
|
97
97
|
}
|
|
98
98
|
// action token
|
|
@@ -6,6 +6,6 @@ export default function button(token, title, permission = '') {
|
|
|
6
6
|
const permissionAttr = handlePermission
|
|
7
7
|
? `v-permission="${handlePermission}"`
|
|
8
8
|
: '';
|
|
9
|
-
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
9
|
+
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
10
10
|
class="inline-flex items-center px-2 py-1 text-sm font-medium text-white duration-300 bg-blue-600 border border-transparent rounded-lg gap-x-2 hover:bg-blue-700 hover:text-white">${title || 'Додати'}</button>`;
|
|
11
11
|
}
|
|
@@ -11,8 +11,8 @@ export default function button(token, permission = '') {
|
|
|
11
11
|
const permissionAttr = handlePermission
|
|
12
12
|
? `v-permission="${handlePermission}"`
|
|
13
13
|
: '';
|
|
14
|
-
return `<button ${permissionAttr} onclick="window.v3plugin.$api({ api: '/api/table/${token}', method:'delete',confirm: { title:'Підтвердити операцію', text: 'Ви впевнені що хочете вилучити запис?', cancel: 'Скасувати', confirm : 'Виконати'} })"
|
|
15
|
-
class="group px-2 py-1 inline-flex border-solid justify-center items-center gap-2 rounded-md font-semibold focus:outline-none text-sm transition-all border border-transparent hover:text-white ring-offset-white bg-${newColor}-100 text-${newColor}-100 hover:bg-${newColor}-500 focus:ring-${newColor}-500">
|
|
16
|
-
<svg class="group-hover:fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="13" height="13" fill="#ef4444"><path d="M135.2 17.7L128 32 32 32C14.3 32 0 46.3 0 64S14.3 96 32 96l384 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-96 0-7.2-14.3C307.4 6.8 296.3 0 284.2 0L163.8 0c-12.1 0-23.2 6.8-28.6 17.7zM416 128L32 128 53.2 467c1.6 25.3 22.6 45 47.9 45l245.8 0c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>
|
|
14
|
+
return `<button ${permissionAttr} onclick="window.v3plugin.$api({ api: '/api/table/${token}', method:'delete',confirm: { title:'Підтвердити операцію', text: 'Ви впевнені що хочете вилучити запис?', cancel: 'Скасувати', confirm : 'Виконати'} })"
|
|
15
|
+
class="group px-2 py-1 inline-flex border-solid justify-center items-center gap-2 rounded-md font-semibold focus:outline-none text-sm transition-all border border-transparent hover:text-white ring-offset-white bg-${newColor}-100 text-${newColor}-100 hover:bg-${newColor}-500 focus:ring-${newColor}-500">
|
|
16
|
+
<svg class="group-hover:fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="13" height="13" fill="#ef4444"><path d="M135.2 17.7L128 32 32 32C14.3 32 0 46.3 0 64S14.3 96 32 96l384 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-96 0-7.2-14.3C307.4 6.8 296.3 0 284.2 0L163.8 0c-12.1 0-23.2 6.8-28.6 17.7zM416 128L32 128 53.2 467c1.6 25.3 22.6 45 47.9 45l245.8 0c25.3 0 46.3-19.7 47.9-45L416 128z"/></svg>
|
|
17
17
|
</button>`;
|
|
18
18
|
}
|
|
@@ -11,8 +11,8 @@ export default function button(token, permission = '') {
|
|
|
11
11
|
const permissionAttr = handlePermission
|
|
12
12
|
? `v-permission="${handlePermission}"`
|
|
13
13
|
: '';
|
|
14
|
-
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
15
|
-
class="group px-2 py-1 inline-flex border-solid justify-center items-center gap-2 rounded-md font-semibold focus:outline-none text-sm transition-all border border-transparent hover:text-white ring-offset-white bg-${newColor}-100 text-${newColor}-100 hover:bg-${newColor}-500 focus:ring-${newColor}-500">
|
|
16
|
-
<svg class="group-hover:fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="13" height="13" fill="#3b82f6"><path d="M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L362.3 51.7l97.9 97.9 30.1-30.1c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L437.7 172.3 339.7 74.3 172.4 241.7zM96 64C43 64 0 107 0 160L0 416c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 64z"/></svg>
|
|
14
|
+
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
15
|
+
class="group px-2 py-1 inline-flex border-solid justify-center items-center gap-2 rounded-md font-semibold focus:outline-none text-sm transition-all border border-transparent hover:text-white ring-offset-white bg-${newColor}-100 text-${newColor}-100 hover:bg-${newColor}-500 focus:ring-${newColor}-500">
|
|
16
|
+
<svg class="group-hover:fill-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="13" height="13" fill="#3b82f6"><path d="M471.6 21.7c-21.9-21.9-57.3-21.9-79.2 0L362.3 51.7l97.9 97.9 30.1-30.1c21.9-21.9 21.9-57.3 0-79.2L471.6 21.7zm-299.2 220c-6.1 6.1-10.8 13.6-13.5 21.9l-29.6 88.8c-2.9 8.6-.6 18.1 5.8 24.6s15.9 8.7 24.6 5.8l88.8-29.6c8.2-2.7 15.7-7.4 21.9-13.5L437.7 172.3 339.7 74.3 172.4 241.7zM96 64C43 64 0 107 0 160L0 416c0 53 43 96 96 96l256 0c53 0 96-43 96-96l0-96c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 96c0 17.7-14.3 32-32 32L96 448c-17.7 0-32-14.3-32-32l0-256c0-17.7 14.3-32 32-32l96 0c17.7 0 32-14.3 32-32s-14.3-32-32-32L96 64z"/></svg>
|
|
17
17
|
</button>`;
|
|
18
18
|
}
|
|
@@ -6,6 +6,6 @@ export default function button(token, title, permission = '') {
|
|
|
6
6
|
const permissionAttr = handlePermission
|
|
7
7
|
? `v-permission="${handlePermission}"`
|
|
8
8
|
: '';
|
|
9
|
-
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
9
|
+
return `<button ${permissionAttr} onclick="window.v3plugin.$form({ token: '${token}' })"
|
|
10
10
|
class="inline-flex items-center px-2 py-1 text-sm font-medium text-white duration-300 bg-blue-600 border border-transparent rounded-lg gap-x-2 hover:bg-blue-700 hover:text-white">${title || 'Редагувати'}</button>`;
|
|
11
11
|
}
|