@opengis/admin 0.1.0

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 (65) hide show
  1. package/README.md +29 -0
  2. package/config.js +5 -0
  3. package/dist/add-page-C-msiPrU.js +64 -0
  4. package/dist/admin-interface-vPkHXzQK.js +762 -0
  5. package/dist/admin-view-BYF4ITZY.js +220 -0
  6. package/dist/card-page-HOwuHNjV.js +49 -0
  7. package/dist/card-view-CjZRvfqy.js +11 -0
  8. package/dist/edit-page-7yrRusn4.js +58 -0
  9. package/dist/form-TrZSpRSC.js +38 -0
  10. package/dist/import-file-DPHo57R5.js +3726 -0
  11. package/dist/settings.js +4 -0
  12. package/dist/settings.umd.cjs +36 -0
  13. package/dist/style.css +1 -0
  14. package/dist/userMenu-CT1xO2Pt.js +5 -0
  15. package/package.json +52 -0
  16. package/plugin.js +25 -0
  17. package/server/helpers/controllers/badge.js +12 -0
  18. package/server/helpers/controllers/hb.js +3 -0
  19. package/server/helpers/controllers/map.js +3 -0
  20. package/server/helpers/controllers/mls.js +3 -0
  21. package/server/helpers/controllers/vue.js +3 -0
  22. package/server/helpers/index.mjs +13 -0
  23. package/server/plugins/docs.js +28 -0
  24. package/server/plugins/hook.js +94 -0
  25. package/server/plugins/vite.js +64 -0
  26. package/server/routes/data/controllers/cardData.js +30 -0
  27. package/server/routes/data/controllers/metaFormat/getSelectVal.js +19 -0
  28. package/server/routes/data/controllers/metaFormat/index.js +29 -0
  29. package/server/routes/data/controllers/tableData.js +129 -0
  30. package/server/routes/data/controllers/tableDataId.js +28 -0
  31. package/server/routes/data/controllers/tableFilter.js +51 -0
  32. package/server/routes/data/index.mjs +10 -0
  33. package/server/routes/menu/controllers/getMenu.js +50 -0
  34. package/server/routes/menu/index.mjs +5 -0
  35. package/server/routes/root.mjs +3 -0
  36. package/server/routes/templates/controllers/getTemplate.js +9 -0
  37. package/server/routes/templates/funcs/addTempateFolder.js +47 -0
  38. package/server/routes/templates/funcs/getTemplate.js +70 -0
  39. package/server/routes/templates/funcs/getTemplatePath.js +39 -0
  40. package/server/routes/templates/funcs/loadTemplate.js +1 -0
  41. package/server/routes/templates/funcs/loadTemplateDir.js +1 -0
  42. package/server/routes/templates/funcs/loadTemplatePath.js +1 -0
  43. package/server/routes/templates/index.mjs +14 -0
  44. package/server/templates/cls/test.json +10 -0
  45. package/server/templates/form/admin.user_cls.data.form.json +50 -0
  46. package/server/templates/form/admin.user_group_rel.form.json +22 -0
  47. package/server/templates/form/cp_building.form.json +33 -0
  48. package/server/templates/form/form-user-pass.json +11 -0
  49. package/server/templates/form/form-user_group.json +40 -0
  50. package/server/templates/form/form-users.json +157 -0
  51. package/server/templates/form/user_group_access.form.json +23 -0
  52. package/server/templates/interface/management.user_group.html +1 -0
  53. package/server/templates/interface/management.user_group.json +10 -0
  54. package/server/templates/interface/management.users.html +1 -0
  55. package/server/templates/interface/management.users.json +10 -0
  56. package/server/templates/map/dgm_level_line.xml +52 -0
  57. package/server/templates/pt/admin-menu-pt.html +108 -0
  58. package/server/templates/pt/management.user_group.table.pt.html +127 -0
  59. package/server/templates/pt/management.users.table.pt.html +130 -0
  60. package/server/templates/pt/table-standard-pt.html +202 -0
  61. package/server/templates/select/account_id.json +3 -0
  62. package/server/templates/select/account_id.sql +1 -0
  63. package/server/templates/table/gis.dataset.table.json +44 -0
  64. package/server/templates/table/management.user_group.table.json +113 -0
  65. package/server/templates/table/management.users.table.json +127 -0
@@ -0,0 +1,94 @@
1
+ import fp from 'fastify-plugin';
2
+
3
+ import fs from 'fs';
4
+ import path from 'path'
5
+
6
+ import config from '../../config.js';
7
+
8
+
9
+
10
+ // to export the decorators to the outer scope
11
+
12
+ async function plugin(fastify) {
13
+ fastify.decorate('config', config);
14
+
15
+
16
+
17
+ // pre Request
18
+ fastify.addHook('onRequest', async (req) => {
19
+ req.funcs = fastify;
20
+ const { user } = req.session?.passport || {};
21
+ req.user = user;
22
+ });
23
+
24
+ // preSerialization
25
+ fastify.addHook('preSerialization', async (req, reply, payload) => {
26
+ if (req.url.includes('/suggest/') && !req.query.json) {
27
+ return payload?.data;
28
+ }
29
+ if (payload?.redirect) {
30
+ return reply.redirect(payload.redirect);
31
+ }
32
+ if (reply.sent) {
33
+ return null;
34
+ }
35
+
36
+ if (['200', '400', '500', '403', '404'].includes(payload?.status)) {
37
+ reply.status(payload.status);
38
+ }
39
+ /* if (payload.headers) {
40
+ reply.headers(payload.headers);
41
+ } */
42
+ if (payload?.buffer) {
43
+ return payload.buffer;
44
+ }
45
+ if (payload?.file) {
46
+ // const buffer = await readFile(payload.file);
47
+ // return reply.send(buffer);
48
+ const stream = fs.createReadStream(payload.file);
49
+ return stream;
50
+ // return reply.send(stream);
51
+ }
52
+
53
+ if (payload?.message) {
54
+ return payload.message;
55
+ }
56
+ return payload;
57
+ });
58
+
59
+ // preValidation
60
+ fastify.addHook('preValidation', async (req) => {
61
+ const parseRawBody = ['POST', 'PUT'].includes(req.method) && req.body && typeof req.body === 'string'
62
+ && req.body.trim(/\r\n/g).startsWith('{')
63
+ && req.body.trim(/\r\n/g).endsWith('}');
64
+ if (parseRawBody) {
65
+ try {
66
+ req.body = JSON.parse(req.body || '{}');
67
+ }
68
+ catch (err) {
69
+ // throw new Error('invalid body');
70
+ // return { error: 'invalid body', status: 400 };
71
+ }
72
+ }
73
+ });
74
+
75
+ // allow upload file
76
+ const kIsMultipart = Symbol.for('[FastifyMultipart.isMultipart]');
77
+ fastify.addContentTypeParser('multipart', (request, _, done) => {
78
+ request[kIsMultipart] = true;
79
+ done(null);
80
+ });
81
+
82
+ // parse Body
83
+ function contentParser(req, body, done) {
84
+ const parseBody = decodeURIComponent(body.toString()).split('&').reduce((acc, el) => {
85
+ const [key, val] = el.split('=');
86
+ return { ...acc, [key]: val };
87
+ }, {});
88
+ done(null, parseBody);
89
+ }
90
+
91
+ fastify.addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'buffer' }, contentParser);
92
+ }
93
+
94
+ export default fp(plugin);
@@ -0,0 +1,64 @@
1
+ import fs from 'fs';
2
+ import path, { dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const dir = dirname(fileURLToPath(import.meta.url));
6
+ const root = `${dir}/../..`;
7
+
8
+ const isProduction = process.env.NODE_ENV === 'production';
9
+
10
+ async function plugin(fastify) {
11
+ // vite server
12
+ if (!isProduction) {
13
+ const vite = await import('vite');
14
+ const viteDevMiddleware = (
15
+ await vite.createServer({
16
+ server: {
17
+ middlewareMode: true,
18
+ },
19
+ })
20
+ ).middlewares;
21
+
22
+ // this is middleware for vite's dev servert
23
+ fastify.addHook('onRequest', async (req, reply) => {
24
+ const { user } = req.session?.passport || {};
25
+ const { disableAuth } = req.funcs?.config || {};
26
+ if (!user && !disableAuth) {
27
+ // return reply.send('access restricted');
28
+ }
29
+
30
+ const next = () => new Promise((resolve) => {
31
+ viteDevMiddleware(req.raw, reply.raw, () => resolve());
32
+ });
33
+ await next();
34
+ });
35
+ fastify.get('*', async () => { });
36
+ return;
37
+ }
38
+
39
+ // From Build
40
+ fastify.get('*', async (req, reply) => {
41
+ const { user } = req.session?.passport || {};
42
+
43
+ // if (!user) return reply.redirect('/login');
44
+ const stream = fs.createReadStream('dist/index.html');
45
+ return reply.type('text/html').send(stream);
46
+ });
47
+
48
+ async function staticFile(req, reply) {
49
+ const assetsDir = 'dist';
50
+ const filePath = path.join(root, assetsDir, req.url);
51
+ const ext = path.extname(filePath);
52
+ const mime = {
53
+ '.js': 'text/javascript', '.css': 'text/css', '.woff2': 'application/font-woff', '.png': 'image/png', '.svg': 'image/svg+xml', '.jpg': 'image/jpg',
54
+ }[ext];
55
+ reply.headers({ 'Cache-control': 'max-age=3600, public' });
56
+ const stream = fs.createReadStream(filePath);
57
+ return mime ? reply.type(mime).send(stream) : stream;
58
+ }
59
+
60
+ fastify.get('/assets/*', staticFile);
61
+ fastify.get('/public/*', staticFile);
62
+ }
63
+
64
+ export default plugin;
@@ -0,0 +1,30 @@
1
+ import getTemplate from "../../templates/funcs/getTemplate.js";
2
+ // import yaml from 'js-yaml';
3
+ import getTableData from "./tableData.js";
4
+
5
+ export default async function getCardData({
6
+ pg, funcs, params = {}, session = {},
7
+ }) {
8
+ const { table, id } = params;
9
+ const time = Date.now();
10
+
11
+ const template = await getTemplate('card', table);
12
+
13
+ const tableTemplate = await getTemplate('table', table);
14
+ const index = template.find(el => el[0] === 'index.yml')?.[1] || {}
15
+ //return template
16
+
17
+ const data = {};
18
+ const { message, rows = [] } = await getTableData({ pg, funcs, params: { table, id }, session });
19
+
20
+ if (message) return { message };
21
+
22
+ await Promise.all(template.filter(el => el[0].includes('.hbs')).map(async (el) => {
23
+ const htmlContent = await funcs.handlebars.compile(el[1])(rows[0]);
24
+ const name = el[0].substring(0, el[0].lastIndexOf('.'))
25
+ data[name] = htmlContent;
26
+ }));
27
+
28
+ return { time: Date.now() - time, ...index, data, rows, columns: tableTemplate?.columns || tableTemplate?.colModel };
29
+
30
+ }
@@ -0,0 +1,19 @@
1
+ import getTemplate from "../../../templates/funcs/getTemplate.js";
2
+
3
+ export default async function getSelectVal({ pg, redis, name, values }) {
4
+ const cls = await getTemplate(['select', 'cls'], name);
5
+ if (!cls) return;
6
+ if ((Array.isArray(cls) && !cls?.length) && !cls?.sql) return null;
7
+ const key = `select:${name}`;
8
+ const cache = !Array.isArray(cls) ? (await redis.hmget(key, values)).reduce((p, el, i) => ({ ...p, [values[i]]: el }), {}) : {};
9
+
10
+ const data = Array.isArray(cls) ? cls : (values.filter(el => !cache[el]).length
11
+ ? await pg.query(`with c(id,text) as (${cls.sql}) select * from c where id = any('{${values.filter(el => !cache[el])}}')`).then(el => el.rows)
12
+ : []);
13
+
14
+ const clsAr = { ...cache, ...data.reduce((p, el) => ({ ...p, [el.id.toString()]: el.color ? el : el.text }), {}) };
15
+ if (!cls.arr && data.length) {
16
+ redis.hmset(key, clsAr);
17
+ }
18
+ return clsAr;
19
+ }
@@ -0,0 +1,29 @@
1
+ import getTemplate from "../../../templates/funcs/getTemplate.js";
2
+ import getSelectVal from './getSelectVal.js';
3
+
4
+ export default async function metaFormat({ funcs, rows, table }) {
5
+ const loadTable = await getTemplate('table', table);
6
+ const selectCols = loadTable?.colModel?.filter((e) => e.name && e.option).map((el) => ({ ...el, data: el.option }))
7
+ .concat(loadTable?.columns?.filter((e) => e.data) || []);
8
+ const metaCls = Object.keys(loadTable?.meta?.cls || {}).map((el) => ({ name: el, data: loadTable?.meta?.cls[el] }));
9
+ if (!selectCols?.length && !metaCls?.length) return rows;
10
+
11
+ const pg = funcs.getPG();
12
+ const redis = funcs.getRedis();
13
+
14
+ // cls & select format
15
+ await Promise.all(selectCols.concat(metaCls)?.map(async (attr) => {
16
+ const values = [...new Set(rows?.map((el) => el[attr.name]).flat())].filter((el) => el);
17
+ if (!values.length) return null;
18
+ const cls = await getSelectVal({ pg, redis, name: attr.data, values });
19
+ if (!cls) return null;
20
+ rows.forEach(el => {
21
+ const val = el[attr.name]?.map?.(c => cls[c] || c) || cls[el[attr.name]] || el[attr.name];
22
+ if (!val) return;
23
+ Object.assign(el, { [val?.color ? `${attr.name}_data` : `${attr.name}_text`]: (val.color ? val : val.text || val) });
24
+ });
25
+ return null;
26
+ }));
27
+
28
+ return rows;
29
+ }
@@ -0,0 +1,129 @@
1
+ import getTemplate from "../../templates/funcs/getTemplate.js";
2
+
3
+ import getMeta from "@opengis/fastify-table/pg/funcs/getMeta.js";
4
+ import getAccess from "@opengis/fastify-table/crud/funcs/getAccess.js";
5
+ import setToken from "@opengis/fastify-table/crud/funcs/setToken.js";
6
+ import getFilterSQL from "@opengis/fastify-table/table/funcs/getFilterSQL/index.js";
7
+ import gisIRColumn from "@opengis/fastify-table/table/controllers/utils/gisIRColumn.js";
8
+ import metaFormat from "./metaFormat/index.js";
9
+
10
+ const maxLimit = 100;
11
+
12
+ export default async function getTableData(req) {
13
+ const time = Date.now();
14
+
15
+ const {
16
+ pg, params, funcs = {}, query = {}, opt = {}, session = {},
17
+ } = req;
18
+ const { uid } = session.passport?.user || {};
19
+
20
+ try {
21
+ const loadTable = await getTemplate('table', params.table);
22
+
23
+ if (!loadTable) {
24
+ return { message: 'template not found', status: 404 };
25
+ }
26
+
27
+ const {
28
+ table, sql, cardSql, filter_list: filters, form, meta, sqlColumns, ispublic,
29
+ } = loadTable;
30
+ const columns = loadTable.columns || loadTable.colModel;
31
+
32
+ const { pk, columns: dbColumns = [] } = await getMeta(table);
33
+
34
+ if (!pk) {
35
+ return { message: `table not found: ${table}`, status: 404 };
36
+ }
37
+
38
+ const cols = columns.filter((el) => el.name !== 'geom').map((el) => el.name || el).join(',');
39
+
40
+ const metaCols = Object.keys(loadTable?.meta?.cls || {}).filter((el) => !cols.includes(el)).length
41
+ ? `,${Object.keys(loadTable?.meta?.cls || {})?.filter((el) => !cols.includes(el)).join(',')}`
42
+ : '';
43
+
44
+ const columnList = dbColumns.map((el) => el.name || el).join(',');
45
+
46
+ const sqlTable = sql?.filter?.((el) => !el?.disabled && el?.sql?.replace).map((el, i) => ` left join lateral (${el.sql.replace('{{uid}}', uid)}) ${el.name || `t${i}`} on 1=1 `)?.join('') || '';
47
+
48
+ const cardSqlFiltered = opt?.id || params.id ? (cardSql?.filter?.((el) => !el?.disabled && el?.name && el?.sql?.replace) || []) : [];
49
+ const cardSqlTable = cardSqlFiltered.length ? cardSqlFiltered.map((el, i) => ` left join lateral (select json_agg(row_to_json(q)) as ${el.name} from (${el.sql})q) ct${i} on 1=1 `).join('') || '' : '';
50
+
51
+ if (params.id && columnList.includes(params.id)) {
52
+ return gisIRColumn({
53
+ pg, funcs, layer: params.table, column: params.id, sql: query.sql,
54
+ });
55
+ }
56
+
57
+ const fData = query.filter || query.search ? await getFilterSQL({
58
+ filter: query.filter,
59
+ search: query.search,
60
+ table: params.table,
61
+ json: 1,
62
+ }) : {};
63
+
64
+ const keyQuery = query.key && loadTable.key && !(opt?.id || params.id) ? `${loadTable.key}=$1` : null;
65
+
66
+ const limit = Math.min(maxLimit, +(query.limit || 20));
67
+
68
+ const offset = query.page && query.page > 0 ? ` offset ${(query.page - 1) * limit}` : '';
69
+ // id, query, filter
70
+ const [orderColumn, orderDir] = (query.order || loadTable.order || '').split(/[- ]/);
71
+
72
+ const order = columnList.includes(orderColumn) && orderColumn?.length ? `order by ${orderColumn} ${query.desc || orderDir === 'desc' ? 'desc' : ''}` : '';
73
+
74
+ const state = loadTable.filterState && query.state ? loadTable.filterState[query.state]?.sql : null;
75
+
76
+ const custom = loadTable.filterCustom && query.custom ? loadTable.filterCustom[query.custom]?.sql : null;
77
+
78
+ const search = loadTable.meta?.search && query.search ? `(${loadTable.meta?.search.split(',').map(el => `${el} ilike '%${query.search}%'`).join(' or ')})` : null;
79
+
80
+ const queryBbox = query?.bbox ? query.bbox.replace(/ /g, ',').split(',')?.map((el) => el - 0) : [];
81
+
82
+ const queryPolyline = meta?.bbox && query?.polyline ? `ST_Contains(ST_MakePolygon(ST_LineFromEncodedPolyline('${query?.polyline}')),${meta.bbox})` : undefined;
83
+
84
+ const bbox = meta?.bbox && queryBbox.filter((el) => !Number.isNaN(el))?.length === 4 ? `${meta.bbox} && 'box(${queryBbox[0]} ${queryBbox[1]},${queryBbox[2]} ${queryBbox[3]})'::box2d ` : undefined;
85
+
86
+ const access = await getAccess(req, params.table);
87
+
88
+ const where = [(opt?.id || params.id ? ` "${pk}" = $1` : null), keyQuery, loadTable.query, fData.q, state, custom, search, access?.query || '1=1', bbox, queryPolyline].filter((el) => el);
89
+
90
+ const cardColumns = cardSqlFiltered.length ? `,${cardSqlFiltered.map((el) => el.name)}` : '';
91
+ const q = `select ${pk ? `"${pk}" as id,` : ''} ${columnList.includes('geom') ? 'st_asgeojson(geom)::json as geom,' : ''} ${query.id || query.key ? '*' : sqlColumns || cols || '*'} ${metaCols} ${cardColumns} from ${table} t ${sqlTable} ${cardSqlTable} where ${where.join(' and ') || 'true'} ${order} ${offset} limit ${limit}`;
92
+
93
+ if (query.sql === '1') { return { message: q }; }
94
+
95
+ const { rows } = await pg.query(q, (opt?.id || params.id ? [opt?.id || params.id] : null) || (query.key && loadTable.key ? [query.key] : []));
96
+
97
+ const total = keyQuery || opt?.id || params.id ? rows.length : await pg.queryCache(`select count(*) from ${table} t ${sqlTable} where ${where.join(' and ') || 'true'}`).then((el) => (el?.rows[0]?.count || 0) - 0);
98
+
99
+ await metaFormat({ funcs, rows, table: params.table });
100
+
101
+ const res = {
102
+ time: Date.now() - time, card: loadTable.card, actions: loadTable.actions, access, total, count: rows.length, pk, form, rows, meta, columns, filters,
103
+ };
104
+
105
+ if (!funcs.config?.security?.disableToken) {
106
+ const addTokens = setToken({
107
+ ids: [JSON.stringify({ add: loadTable.table, form: loadTable.form })],
108
+ mode: 'a',
109
+ uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
110
+ array: 1,
111
+ });
112
+ Object.assign(res, { addToken: addTokens[0] });
113
+
114
+ rows.forEach((row) => {
115
+ const editTokens = setToken({
116
+ ids: [JSON.stringify({ id: row.id, table: loadTable.table, form: loadTable.form })],
117
+ mode: 'w',
118
+ uid: funcs.config?.auth?.disable || ispublic ? '1' : uid,
119
+ array: 1,
120
+ });
121
+ Object.assign(row, { token: editTokens[0] });
122
+ });
123
+ }
124
+
125
+ return res;
126
+ } catch (err) {
127
+ return { message: err.toString(), status: 500 };
128
+ }
129
+ }
@@ -0,0 +1,28 @@
1
+ import getTemplate from "../../templates/funcs/getTemplate.js";
2
+ import getTableData from "./tableData.js";
3
+
4
+ export default async function tableDataId({
5
+ pg, funcs, params = {}, session = {},
6
+ }, reply) {
7
+ const { name, id } = params;
8
+
9
+ const data = await getTemplate('pt', `${name}.pt`);
10
+
11
+
12
+ if (!data) {
13
+ return { message: `invalid template: ${name}.pt`, status: 400 };
14
+ }
15
+
16
+ const { message, status = 500, rows = [] } = await getTableData({ pg, params: { name, id }, session });
17
+ if (message) {
18
+ return { message, status };
19
+ }
20
+
21
+ if (!rows.length) {
22
+ return { message: `data not found: ${name}/${id}`, status: 404 };
23
+ }
24
+
25
+ const htmlContent = await funcs.handlebars.compile(data)(rows[0]);
26
+
27
+ return reply.headers({ 'Content-Type': 'text/html; charset=utf-8' }).send(htmlContent);
28
+ }
@@ -0,0 +1,51 @@
1
+ import getTemplate from '../../templates/funcs/getTemplate.js';
2
+
3
+ export default async function tableFilter(req) {
4
+ const time = Date.now();
5
+ const { pg, funcs = {}, params = {}, query = {} } = req;
6
+
7
+ if (!params.name) {
8
+ return { message: 'not enouggh params: name', status: 400 };
9
+ }
10
+
11
+
12
+
13
+
14
+ const loadTable = await getTemplate('table', params.name);
15
+
16
+ if (!loadTable) {
17
+ return { message: 'table not found', status: 404 };
18
+ }
19
+
20
+ const { columns } = await funcs.getMeta({ table: loadTable.table });
21
+
22
+ const filters = loadTable?.filter_list || loadTable?.filters || loadTable?.filterList || [];
23
+ filters.forEach(el => {
24
+ Object.assign(el, { id: el.name });
25
+ })
26
+ await Promise.all(filters.filter((el) => el.data).map(async (el) => {
27
+
28
+ const cls = await getTemplate(['cls', 'select'], el.data)
29
+
30
+
31
+ if (!cls?.length || !Array.isArray(cls) || !loadTable.table) return;
32
+ const { dataTypeID } = columns.find((item) => item.name === el.name) || {};
33
+ const countArr = pg.pgType[dataTypeID]?.includes('[]')
34
+ ? await pg.queryCache(`select unnest(${el.name})::text as id,count(*) from ${loadTable.table} group by unnest(${el.name})`)
35
+ : await pg.queryCache(`select ${el.name}::text as id,count(*) from ${loadTable.table} group by ${el.name}`);
36
+
37
+ const options = countArr.rows.map(cel => {
38
+ const data = cls.find(c => c.id === cel.id);
39
+ return { ...cel, ...data };
40
+ });
41
+ Object.assign(el, { options });
42
+ }));
43
+
44
+ return {
45
+ time: Date.now() - time,
46
+ list: filters,
47
+ custom: loadTable?.filterCustom?.map(el => ({ label: el.label })),
48
+ inline: loadTable?.filterInline,
49
+ state: loadTable?.filterState?.map(el => ({ label: el.label })),
50
+ };
51
+ }
@@ -0,0 +1,10 @@
1
+ import tableData from "./controllers/tableData.js";
2
+ import tableDataId from "./controllers/tableDataId.js";
3
+ import cardData from "./controllers/cardData.js";
4
+ import tableFilter from "./controllers/tableFilter.js";
5
+
6
+ export default async function route(fastify) {
7
+ fastify.get(`/table-data/:table`, tableData);
8
+ fastify.get(`/table-data/:table/:id`, cardData);
9
+ fastify.get(`/table-filter/:name`, tableFilter);
10
+ }
@@ -0,0 +1,50 @@
1
+ import { readdir, readFile } from 'fs/promises';
2
+ import { join } from 'path';
3
+ import config from '../../../../config.js';
4
+
5
+ const findMenuJsons = async (startPath, filter) => {
6
+ const files = await readdir(startPath, { withFileTypes: true });
7
+
8
+ return await files.reduce(async (prevPromise, file) => {
9
+ const acc = await prevPromise;
10
+ const filePath = join(startPath, file.name);
11
+
12
+ if (file.isDirectory()) {
13
+ const nestedFiles = await findMenuJsons(filePath, filter);
14
+ return acc.concat(nestedFiles);
15
+ } else if (file.name === filter) {
16
+ acc.push(filePath);
17
+ }
18
+
19
+ return acc;
20
+ }, Promise.resolve([]));
21
+
22
+ }
23
+
24
+ const readJsonFiles = async (filePaths) => {
25
+ const menu = []
26
+ await Promise.all(
27
+ filePaths.map(async (filePath) => {
28
+ const content = JSON.parse(await readFile(filePath, 'utf-8'));
29
+ content.forEach(el => {
30
+ menu.push(el)
31
+ });
32
+
33
+
34
+ })
35
+ );
36
+ return menu;
37
+ }
38
+
39
+ export default async function adminMenu() {
40
+ const cwd = process.cwd();
41
+ //const typeDir = join(cwd, (config.templateDir || 'server/templates'));
42
+ const modulesDir = join(cwd, 'module');
43
+
44
+ // const menus = await findMenuJsons(typeDir, 'menu.json');
45
+ const menuModules = await findMenuJsons(modulesDir, 'menu.json');
46
+
47
+ const jsons = await readJsonFiles(menuModules);
48
+
49
+ return jsons;
50
+ }
@@ -0,0 +1,5 @@
1
+ import adminMenu from './controllers/getMenu.js';
2
+
3
+ export default async function (fastify, opts) {
4
+ fastify.get(`/user-menu`, adminMenu);
5
+ }
@@ -0,0 +1,3 @@
1
+ export default async function (fastify, opts) {
2
+ fastify.get(`/test`, () => { return { test: true } });
3
+ }
@@ -0,0 +1,9 @@
1
+ import getTemplate from "../funcs/getTemplate.js"
2
+
3
+ export default async function getTemplateApi({
4
+ params = {},
5
+ }) {
6
+ const { type, name } = params;
7
+ const data = await getTemplate(type, name);
8
+ return data || { message: `template not found "${name}"`, status: 404 };
9
+ }
@@ -0,0 +1,47 @@
1
+ import path from 'path';
2
+ import { statSync, existsSync } from 'node:fs';
3
+ import { readdir, readFile } from 'node:fs/promises';
4
+
5
+ import loadTemplate from './loadTemplate.js';
6
+
7
+ export default async function addTempateFolder(dir) {
8
+ if (!existsSync(dir)) {
9
+ throw new Error(`dir not found: ${dir}`);
10
+ }
11
+ const subdirs = (await readdir(dir, { withFileTypes: true }))?.filter((dirent) => statSync(path.join(dirent.path, dirent.name)).isDirectory());
12
+ await Promise.all(subdirs.map(async (dirent) => {
13
+ const content = dirent.name !== 'card' ? await readdir(path.join(dirent.path, dirent.name), { withFileTypes: true }) : [];
14
+ loadTemplate[dirent.name] = loadTemplate[dirent.name] || {};
15
+ const files = content?.filter((el) => !statSync(path.join(el.path, el.name)).isDirectory());
16
+ await Promise.all(files?.map(async (el) => {
17
+ const data = await readFile(path.join(el.path, el.name), 'utf-8');
18
+
19
+ if (['select', 'interface'].includes(dirent.name) && path.extname(el.name) === '.json') {
20
+ loadTemplate[dirent.name][el.name.replace(path.extname(el.name), '.setting')] = JSON.parse(data);
21
+ return;
22
+ }
23
+
24
+ loadTemplate[dirent.name][el.name.replace(path.extname(el.name), '')] = path.extname(el.name) === '.json' ? JSON.parse(data) : data;
25
+ }));
26
+ console.log(dir, dirent.name, files.map((el) => el.name));
27
+ }));
28
+ const carddir = subdirs.find((el) => el.name === 'card');
29
+ const cardSubdirs = carddir ? (await readdir(path.join(carddir.path, carddir.name), { withFileTypes: true }))?.filter((dirent) => statSync(path.join(dirent.path, dirent.name)).isDirectory()) : [];
30
+
31
+ await Promise.all(cardSubdirs.map(async (dirent) => {
32
+ const content = await readdir(path.join(dirent.path, dirent.name), { withFileTypes: true });
33
+ const files = content?.filter((el) => !statSync(path.join(el.path, el.name)).isDirectory());
34
+ loadTemplate['card'] = loadTemplate['card'] || {};
35
+ await Promise.all(files?.map(async (el) => {
36
+ loadTemplate['card'][dirent.name] = loadTemplate['card'][dirent.name] || {};
37
+ const data = await readFile(path.join(el.path, el.name), 'utf-8');
38
+ if (path.extname(el.name) === '.yml') {
39
+ loadTemplate['card'][dirent.name]['config'] = data;
40
+ } else {
41
+ loadTemplate['card'][dirent.name][path.parse(el.name).name] = data;
42
+ }
43
+ }));
44
+ console.log('card', dir, dirent.name, files.map((el) => el.name));
45
+ }));
46
+ console.log(dir, 'finish');
47
+ }
@@ -0,0 +1,70 @@
1
+ import { readdir, readFile } from 'fs/promises';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+
5
+ import getTemplatePath from './getTemplatePath.js';
6
+ import loadTemplate from './loadTemplate.js';
7
+
8
+
9
+ async function readFileData(file) {
10
+ const data = await readFile(file, 'utf-8');
11
+ const ext = file.substring(file.lastIndexOf('.') + 1);
12
+
13
+ if (ext === 'yml') {
14
+ return yaml.load(data);
15
+ }
16
+ if (ext === 'json') {
17
+ return JSON.parse(data);
18
+ }
19
+ return data;
20
+ };
21
+
22
+ const isProduction = process.env.NODE_ENV === 'production';
23
+
24
+ async function getTemplateData(template) {
25
+
26
+ // dir template: dashboard, card
27
+ if (template[0][3]) {
28
+ const files = await readdir(template[0][1]);
29
+ const data = await Promise.all(files.map(async el => readFileData(path.join(template[0][1], el))))
30
+ return files.map((el, i) => [el, data[i]])
31
+ }
32
+
33
+ // one file template: table, form
34
+ if (template.length === 1) {
35
+ const data = await readFileData(template[0][1])
36
+ return data
37
+ }
38
+
39
+ // multi file template: select, etc
40
+ if (template.length > 1) {
41
+ const data = await Promise.all(template.map(async el => await readFileData(el[1])))
42
+ const result = {}
43
+ template.forEach((el, i) => {
44
+ Object.assign(result, typeof data[i] === 'object' ? data[i] : { [el[2]]: data[i] })
45
+ })
46
+ return result
47
+ }
48
+ }
49
+ export default async function getTemplate(type, name) {
50
+ if (!type) return null;
51
+ if (!name) return null;
52
+
53
+ const key = type + ':' + name;
54
+ if (name === 'cache' && !isProduction) return loadTemplate; // all cache debug
55
+ if (loadTemplate[key] && isProduction) return loadTemplate[key]; // from cache
56
+
57
+ // type one or multi
58
+ const templateList = Array.isArray(type) ?
59
+ type.map(el => getTemplatePath(el)).filter(list => list?.filter(el => el[0] === name).length)[0] || [] :
60
+ getTemplatePath(type);
61
+
62
+ // find template
63
+ const template = templateList?.filter(el => el[0] === name)
64
+ if (name === 'list' && !isProduction) return templateList; // all template debug
65
+
66
+ if (!template.length) return null; // not found
67
+
68
+ loadTemplate[key] = await getTemplateData(template);
69
+ return loadTemplate[key];
70
+ }