@opengis/fastify-table 1.3.72 → 1.3.74
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/package.json +1 -1
- package/server/plugins/table/funcs/addMenu.js +16 -0
- package/server/plugins/table/funcs/addTemplateDir.js +1 -1
- package/server/plugins/table/funcs/menuDirs.js +1 -0
- package/server/routes/menu/controllers/getMenu.js +7 -7
- package/server/routes/table/controllers/suggest.js +28 -14
- package/server/routes/table/schema.js +1 -0
- package/utils.js +6 -0
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
|
|
4
|
+
import menuDirs from './menuDirs.js';
|
|
5
|
+
|
|
6
|
+
export default function addMenu(filepath) {
|
|
7
|
+
if (basename(filepath) !== 'menu.json') {
|
|
8
|
+
throw new Error('addMenu: filepath must be a menu.json file');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if (filepath && !menuDirs.includes(filepath) && existsSync(filepath)) {
|
|
12
|
+
menuDirs.push(filepath);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return menuDirs;
|
|
16
|
+
}
|
|
@@ -2,7 +2,7 @@ import { existsSync, readFileSync } from 'node:fs';
|
|
|
2
2
|
|
|
3
3
|
import userTemplateDir from './userTemplateDir.js';
|
|
4
4
|
import customTokens from './customTokens.js';
|
|
5
|
-
import yml2json from '../../yml/funcs/yml2json.js'
|
|
5
|
+
import yml2json from '../../yml/funcs/yml2json.js';
|
|
6
6
|
|
|
7
7
|
export default function addTemplateDir(dir) {
|
|
8
8
|
if (dir && !userTemplateDir.includes(dir)) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default [];
|
|
@@ -5,7 +5,7 @@ import { join } from 'node:path';
|
|
|
5
5
|
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
|
|
8
|
+
menuDirs, pgClients, applyHook, config,
|
|
9
9
|
} from '../../../../utils.js';
|
|
10
10
|
|
|
11
11
|
const menuCache = [];
|
|
@@ -13,17 +13,16 @@ const menuCache = [];
|
|
|
13
13
|
// check module dir
|
|
14
14
|
const moduleDir = join(process.cwd(), 'module');
|
|
15
15
|
const dirs = existsSync(moduleDir)
|
|
16
|
-
? readdirSync(moduleDir).map(el => join(moduleDir, el))
|
|
16
|
+
? readdirSync(moduleDir).map(el => join(moduleDir, el, 'menu.json'))
|
|
17
17
|
: [];
|
|
18
18
|
|
|
19
19
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
20
20
|
// readMenu();
|
|
21
21
|
|
|
22
22
|
async function readMenu() {
|
|
23
|
-
const menuList = dirs.concat(
|
|
24
|
-
.map(el => join(el, 'menu.json'))
|
|
23
|
+
const menuList = dirs.concat(menuDirs)
|
|
25
24
|
.filter(el => existsSync(el));
|
|
26
|
-
|
|
25
|
+
|
|
27
26
|
const list = menuList.filter((el, idx, arr) => el && arr.indexOf(el) === idx).map(el => readFileSync(el, 'utf-8')); // sync
|
|
28
27
|
const menus = list.reduce((p, el) => p.concat(JSON.parse(el)), [])
|
|
29
28
|
.map(el => ({ order: 0, ...el }))
|
|
@@ -32,11 +31,12 @@ async function readMenu() {
|
|
|
32
31
|
if (!menuCache.length) {
|
|
33
32
|
menus.forEach(el => menuCache.push(el));
|
|
34
33
|
}
|
|
35
|
-
|
|
36
34
|
return menus;
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
export default async function adminMenu({
|
|
37
|
+
export default async function adminMenu({
|
|
38
|
+
user = {}, session, pg = pgClients.client,
|
|
39
|
+
}, reply) {
|
|
40
40
|
const time = Date.now();
|
|
41
41
|
|
|
42
42
|
if (!user.uid) {
|
|
@@ -2,7 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
config, getPG, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger,
|
|
5
|
+
config, getPG, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger, getSelect,
|
|
6
6
|
} from '../../../../utils.js';
|
|
7
7
|
|
|
8
8
|
const limit = 50;
|
|
@@ -12,12 +12,19 @@ const headers = {
|
|
|
12
12
|
'Cache-Control': 'no-cache',
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
-
function getTableColumnMeta(
|
|
15
|
+
async function getTableColumnMeta(tableName, column, filtered, pg) {
|
|
16
|
+
if (!tableName || !column) { return null; }
|
|
17
|
+
|
|
18
|
+
const loadTable = await getTemplate('table', tableName);
|
|
19
|
+
const { data: clsName } = loadTable?.columns?.find?.(el => el.name === column) || {};
|
|
20
|
+
const { arr } = await getSelect(clsName, pg) || {};
|
|
21
|
+
|
|
16
22
|
const original = {
|
|
17
|
-
true: `with c(id,text) as (select ${column}, ${column} from ${table} group by ${column}) select id, text from c`,
|
|
18
|
-
false: `with c(id,text) as (select ${column}, ${column}, count(*) from ${table} group by ${column} limit ${limit}) select * from c`,
|
|
23
|
+
true: `with c(id,text) as (select ${column} as id, ${column} as text from ${loadTable?.table || tableName} group by ${column}) select id, text from c`,
|
|
24
|
+
false: `with c(id,text) as (select ${column} as id, ${column} as text, count(*) from ${loadTable?.table || tableName} group by ${column} limit ${limit}) select * from c`,
|
|
19
25
|
}[!!filtered];
|
|
20
26
|
return {
|
|
27
|
+
arr,
|
|
21
28
|
original,
|
|
22
29
|
searchQuery: '(lower("text") ~ $1 )',
|
|
23
30
|
pk: 'id',
|
|
@@ -41,13 +48,13 @@ export default async function suggest(req) {
|
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
const [table, column] = params.data?.includes(':') ? params.data.split(':') : [];
|
|
51
|
+
const [table, column] = params.data?.includes(':') ? params.data.split(':') : [query.token, query.column];
|
|
45
52
|
const selectName = query.sel || query.name || params.data;
|
|
46
53
|
|
|
47
54
|
if (!selectName) return { status: 400, message: 'name is required' };
|
|
48
55
|
|
|
49
56
|
const { body: hookBody } = table || query.token ? await applyHook('preSuggest', { pg: pg1, table: table || query.token }) || {} : {};
|
|
50
|
-
const body = await getTemplate('table', table);
|
|
57
|
+
const body = await getTemplate('table', table || query.token);
|
|
51
58
|
const tableName = hookBody?.table || body?.table || table || query.token;
|
|
52
59
|
|
|
53
60
|
if (table && !pg1.pk?.[tableName]) {
|
|
@@ -55,7 +62,7 @@ export default async function suggest(req) {
|
|
|
55
62
|
}
|
|
56
63
|
|
|
57
64
|
const tableMeta = await getMeta({ pg: pg1, table: tableName });
|
|
58
|
-
const columnExists = (hookBody?.columns || body?.columns || tableMeta?.columns)?.find(
|
|
65
|
+
const columnExists = (hookBody?.columns || body?.columns || tableMeta?.columns)?.find(col => col?.name === (column || query.column));
|
|
59
66
|
|
|
60
67
|
if (table && (!column || !columnExists)) {
|
|
61
68
|
return { status: 400, message: 'param name is invalid: 2' };
|
|
@@ -65,8 +72,8 @@ export default async function suggest(req) {
|
|
|
65
72
|
return { status: 400, message: 'param limit is invalid' };
|
|
66
73
|
}
|
|
67
74
|
|
|
68
|
-
const meta =
|
|
69
|
-
? getTableColumnMeta(table, column, query?.key || query?.val)
|
|
75
|
+
const meta = tableName && column
|
|
76
|
+
? await getTableColumnMeta(table, column, query?.key || query?.val, pg1)
|
|
70
77
|
: await getSelectMeta({
|
|
71
78
|
pg: pg1, name: selectName, nocache: query?.nocache, parent,
|
|
72
79
|
});
|
|
@@ -95,17 +102,19 @@ export default async function suggest(req) {
|
|
|
95
102
|
const column1 = columns.find(el => el.name === query.column);
|
|
96
103
|
const args = { table: tableName };
|
|
97
104
|
if (!column1) return [];
|
|
98
|
-
const sqlCls =
|
|
105
|
+
const sqlCls = query.count
|
|
106
|
+
? `select value, count(*) from (select ${pg.pgType?.[column1.dataTypeID]?.includes('[]') ? `unnest(${column1.name})` : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || '1=1'})q group by value`
|
|
107
|
+
: `select array_agg(distinct value)::text[] from (select ${pg.pgType?.[column1.dataTypeID]?.includes('[]') ? `unnest(${column1.name})` : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || '1=1'})q`;
|
|
99
108
|
|
|
100
109
|
if (query.sql && (config.local || user?.user_type?.includes?.('admin'))) {
|
|
101
110
|
return sqlCls;
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
if (pg.pk?.[loadTable?.table || tableName] &&
|
|
105
|
-
const
|
|
106
|
-
|
|
113
|
+
if (pg.pk?.[loadTable?.table || tableName] && column1?.name) {
|
|
114
|
+
const qRes = await pg.queryCache(sqlCls, args);
|
|
115
|
+
const vals = (query.count ? qRes.rows?.map?.(el => el.value?.toString?.()) : qRes.rows?.[0]?.array_agg) || [];
|
|
107
116
|
|
|
108
|
-
const lower = query.key?.toLowerCase();
|
|
117
|
+
const lower = query.key?.toLowerCase?.();
|
|
109
118
|
const data1 = query.key || query.val
|
|
110
119
|
? arr?.filter((el) => !lower || (el[lang] || el.text)?.toLowerCase()?.indexOf(lower) !== -1)?.filter((el) => !query.val || el.id === query.val)
|
|
111
120
|
: arr;
|
|
@@ -113,6 +122,11 @@ export default async function suggest(req) {
|
|
|
113
122
|
const data2 = data1.filter((el) => el.id && vals.includes(el.id.toString()));
|
|
114
123
|
const data = data2.slice(0, Math.min(query.limit || limit, limit));
|
|
115
124
|
|
|
125
|
+
if (data.length && query.count) {
|
|
126
|
+
const counts = qRes.rows?.reduce?.((acc, curr) => ({ ...acc, [curr.value]: curr.count }), {});
|
|
127
|
+
data.forEach(el => Object.assign(el, { count: counts?.[el.id] || 0 }));
|
|
128
|
+
}
|
|
129
|
+
|
|
116
130
|
if (config.debug) {
|
|
117
131
|
logger.file('suggest/debug', {
|
|
118
132
|
type: 1,
|
|
@@ -41,6 +41,7 @@ const suggestSchema = {
|
|
|
41
41
|
// key: { type: 'string', pattern: '^([\\d\\w_.-]+)$' },
|
|
42
42
|
val: { type: 'string', pattern: '([\\d\\w]+)$' },
|
|
43
43
|
sql: { type: 'string', pattern: '^(\\d)$' },
|
|
44
|
+
count: { type: 'string', pattern: '^(\\d)$' },
|
|
44
45
|
},
|
|
45
46
|
params: {
|
|
46
47
|
// data: { type: 'string', pattern: '^([\\d\\w]+)$' },
|
package/utils.js
CHANGED
|
@@ -23,6 +23,8 @@ import getTemplateSync from './server/plugins/table/funcs/getTemplateSync.js';
|
|
|
23
23
|
import getTemplates from './server/plugins/table/funcs/getTemplates.js';
|
|
24
24
|
import getTemplatePath from './server/plugins/table/funcs/getTemplatePath.js';
|
|
25
25
|
import addTemplateDir from './server/plugins/table/funcs/addTemplateDir.js';
|
|
26
|
+
import addMenu from './server/plugins/table/funcs/addMenu.js';
|
|
27
|
+
import menuDirs from './server/plugins/table/funcs/menuDirs.js';
|
|
26
28
|
import userTemplateDir from './server/plugins/table/funcs/userTemplateDir.js';
|
|
27
29
|
import customTokens from './server/plugins/table/funcs/customTokens.js';
|
|
28
30
|
import userTokens from './server/plugins/table/funcs/userTokens.js';
|
|
@@ -36,6 +38,7 @@ import getSelect from './server/plugins/table/funcs/getSelect.js';
|
|
|
36
38
|
import getSelectMeta from './server/plugins/table/funcs/getSelectMeta.js';
|
|
37
39
|
import gisIRColumn from './server/plugins/table/funcs/gisIRColumn.js';
|
|
38
40
|
import getData from './server/plugins/table/funcs/getData.js';
|
|
41
|
+
import getMenu from './server/routes/menu/controllers/getMenu.js';
|
|
39
42
|
import getFilter from './server/plugins/table/funcs/getFilter.js';
|
|
40
43
|
|
|
41
44
|
// crud
|
|
@@ -109,6 +112,8 @@ export {
|
|
|
109
112
|
getTemplates,
|
|
110
113
|
getTemplatePath,
|
|
111
114
|
addTemplateDir,
|
|
115
|
+
menuDirs,
|
|
116
|
+
addMenu,
|
|
112
117
|
userTemplateDir,
|
|
113
118
|
customTokens,
|
|
114
119
|
userTokens,
|
|
@@ -134,6 +139,7 @@ export {
|
|
|
134
139
|
gisIRColumn,
|
|
135
140
|
getData,
|
|
136
141
|
getFilter,
|
|
142
|
+
getMenu,
|
|
137
143
|
|
|
138
144
|
// pg
|
|
139
145
|
initPG,
|