@opengis/fastify-table 2.0.117 → 2.0.119

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.
@@ -1 +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"}
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,QAWpB;AAED,OAAO,EAEL,MAAM,EACN,MAAM,EACN,SAAS,GACV,CAAC"}
package/dist/helper.js CHANGED
@@ -24,6 +24,9 @@ export function build() {
24
24
  if (!app) {
25
25
  app = Fastify({ logger: false });
26
26
  app.register(appService, config);
27
+ app.addHook("onRequest", async (req) => {
28
+ req.user = req.user || { uid: "1", user_type: "admin" };
29
+ });
27
30
  addTemplateDir(path.join(process.cwd(), "module/test"));
28
31
  // await app.ready(); // ? ensure plugins registered, can not add fastify hooks after app is ready
29
32
  }
@@ -1,2 +1,2 @@
1
- select uid, coalesce(sur_name,'')||coalesce(' '||user_name,'') as text, email from admin.users
1
+ select uid, coalesce(sur_name,'')||coalesce(' '||user_name,'') as text, email from admin.users
2
2
  where enabled order by coalesce(sur_name,'')||coalesce(' '||user_name,'')
@@ -10,7 +10,7 @@ const { prefix = "/api" } = config;
10
10
  export async function onRequest(req, reply) {
11
11
  const { hostname, headers, routeOptions } = req;
12
12
  const { config: routeConfig, method, handler, url } = routeOptions || {};
13
- const { policy } = routeConfig;
13
+ const { policy } = routeConfig || {};
14
14
  const isApi = method && url && typeof handler === "function" && url !== "*";
15
15
  // handle non-api at vite/vike
16
16
  if (!isApi) {
@@ -1,7 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  export default function fsStorage(): {
3
3
  deleteFile: (fp: string) => Promise<void>;
4
- downloadFile: (fp: string, options?: Record<string, any>) => Promise<NonSharedBuffer | fs.ReadStream | {
4
+ downloadFile: (fp: string, options?: Record<string, any>) => Promise<Buffer<ArrayBufferLike> | fs.ReadStream | {
5
5
  original: string;
6
6
  full: string | null;
7
7
  } | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAmC1C,iBAAS,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,GAAE,GAAQ,QAiFlD;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/auth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAkD1C,iBAAS,MAAM,CAAC,GAAG,EAAE,eAAe,EAAE,GAAG,GAAE,GAAQ,QAqFlD;AAED,eAAe,MAAM,CAAC"}
@@ -21,6 +21,19 @@ import oauthAuthorize from "./controllers/jwt/authorize.js";
21
21
  import oauthToken from "./controllers/jwt/token.js";
22
22
  import qrCode from "./controllers/2factor/qrcode.js";
23
23
  const params = { config: { policy: "L0" } };
24
+ const registrationSchema = {
25
+ body: {
26
+ type: "object",
27
+ required: ["password"],
28
+ properties: {
29
+ password: { type: "string", minLength: 6, maxLength: 100 },
30
+ email: { type: "string", format: "email" },
31
+ login: { type: "string" },
32
+ first_name: { type: "string" },
33
+ last_name: { type: "string" },
34
+ },
35
+ },
36
+ };
24
37
  function plugin(app, opt = {}) {
25
38
  if (opt.routes === false || config.auth?.customRoutes) {
26
39
  return;
@@ -35,7 +48,7 @@ function plugin(app, opt = {}) {
35
48
  app.post("/api/login", params, login);
36
49
  }
37
50
  if (!app.hasRoute({ method: "POST", url: "/api/registration" })) {
38
- app.post("/api/registration", params, registration);
51
+ app.post("/api/registration", { ...params, schema: registrationSchema }, registration);
39
52
  }
40
53
  if (!app.hasRoute({ method: "POST", url: "/api/recovery" })) {
41
54
  app.post("/api/recovery", params, passwordRecovery);
@@ -1 +1 @@
1
- {"version":3,"file":"resizeAll.d.ts","sourceRoot":"","sources":["../../../../../server/routes/file/controllers/resizeAll.ts"],"names":[],"mappings":"AAiHA,wBAA8B,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAoH3D"}
1
+ {"version":3,"file":"resizeAll.d.ts","sourceRoot":"","sources":["../../../../../server/routes/file/controllers/resizeAll.ts"],"names":[],"mappings":"AAkHA,wBAA8B,SAAS,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAsH3D"}
@@ -4,7 +4,7 @@ import { pgClients, eventStream, logger, isFileExists, downloadFile, uploadFile,
4
4
  import grpc from "../../../plugins/grpc/grpc.js";
5
5
  import { images } from "../../../plugins/file/utils/allowedExtensions.js";
6
6
  const { resizeImage } = grpc();
7
- async function resizeOne({ relpath: relpath1, resizeQuality = 80, maxWidth = 2048, errors = [], success = [], notfound = [], equalwidth = [], send = () => { }, unittest, }) {
7
+ async function resizeOne({ relpath: relpath1, resizeQuality = 80, maxWidth = 2048, errors = [], success = [], notfound = [], equalwidth = [], send = () => { }, }) {
8
8
  const exists = await isFileExists(relpath1);
9
9
  const existsOriginal = await isFileExists(relpath1.replace("files/", "files/original/"));
10
10
  const relpath = existsOriginal
@@ -42,6 +42,7 @@ async function resizeOne({ relpath: relpath1, resizeQuality = 80, maxWidth = 204
42
42
  const resizeHeight = resizeWidth / ratio;
43
43
  send(`original ${relpath} width/height/ratio: ${width} / ${height} / ${ratio}`);
44
44
  send(`resize ${relpath} width/height/ratio/quality: ${resizeWidth} / ${resizeHeight} / ${resizeWidth / resizeHeight} / ${resizeQuality}`);
45
+ const unittest = process.env.NODE_ENV === "test" || process.env.VITEST;
45
46
  // skip for resize with small unit test image
46
47
  if (resizeWidth === width && !unittest) {
47
48
  send(`resize ${relpath} skip: resize width equals to original`);
@@ -60,7 +61,7 @@ async function resizeOne({ relpath: relpath1, resizeQuality = 80, maxWidth = 204
60
61
  return null;
61
62
  }
62
63
  export default async function resizeAll(req, reply) {
63
- const { pg = pgClients.client, query = {}, unittest } = req;
64
+ const { pg = pgClients.client, query = {} } = req;
64
65
  const { nocache, filepath, quality = 80, limit, sql, w = 2048 } = query;
65
66
  if (nocache) {
66
67
  const clearQ = `update crm.files set resized=null
@@ -87,7 +88,9 @@ export default async function resizeAll(req, reply) {
87
88
  const errors = [];
88
89
  const notfound = [];
89
90
  const equalwidth = [];
90
- const send = unittest ? console.log : eventStream(reply);
91
+ const send = process.env.NODE_ENV === "test" || process.env.VITEST
92
+ ? console.log
93
+ : eventStream(reply);
91
94
  try {
92
95
  send(`file max resize width: ${w}`);
93
96
  send(`file max resize quality: ${resizeQuality}`);
@@ -104,7 +107,6 @@ export default async function resizeAll(req, reply) {
104
107
  notfound,
105
108
  equalwidth,
106
109
  send,
107
- unittest,
108
110
  })), Promise.resolve());
109
111
  if (success.length) {
110
112
  await pg.query("update crm.files set resized=true where file_path=any($1::text[])", [success]);
@@ -1 +1 @@
1
- {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../../../../server/routes/table/controllers/suggest.ts"],"names":[],"mappings":"AA+DA,wBAA8B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAqXzD"}
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../../../../server/routes/table/controllers/suggest.ts"],"names":[],"mappings":"AAmEA,wBAA8B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,gBAsWzD"}
@@ -1,17 +1,21 @@
1
1
  import path from "node:path";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
- import { config, getPG, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger, getSelect, metaFormat, getColumnCLS, } from "../../../../utils.js";
3
+ import { config, getPGAsync, getTemplate, getSelectMeta, getMeta, applyHook, getSelectVal, logger, getSelect, metaFormat, getColumnCLS, pgClients, } from "../../../../utils.js";
4
4
  const defaultLimit = 50;
5
- async function getTableColumnMeta({ table, column, filtered, startsWith, key, pg, }) {
5
+ async function getTableColumnMeta({ table, template, column, selectName, filtered, startsWith, key, pg = pgClients.client, }) {
6
6
  if (!table || !column) {
7
7
  return null;
8
8
  }
9
- const loadTable = await getTemplate("table", table);
10
- const { data: clsName } = loadTable?.columns?.find?.((el) => el.name === column) || {};
11
- const { arr } = (await getSelect(clsName, pg)) || {};
9
+ const { columns, table: tableName } = template
10
+ ? await getTemplate("table", template)
11
+ : { table, columns: [] };
12
+ const { data: clsName } = selectName
13
+ ? { data: selectName }
14
+ : columns.find((el) => el.name === column) || {};
15
+ const { arr } = (await getSelect(clsName || column, pg)) || {};
12
16
  const original = filtered
13
- ? `with c(id,text) as (select ${column} as id, ${column} as text from ${loadTable?.table || table} group by ${column}) select id, text from c`
14
- : `with c(id,text) as (select ${column} as id, ${column} as text, count(*) from ${loadTable?.table || table} group by ${column} limit ${defaultLimit}) select * from c`;
17
+ ? `with c(id,text) as (select ${column} as id, ${column} as text from ${tableName} group by ${column}) select id, text from c`
18
+ : `with c(id,text) as (select ${column} as id, ${column} as text, count(*) from ${tableName} group by ${column} limit ${defaultLimit}) select * from c`;
15
19
  return {
16
20
  arr,
17
21
  original,
@@ -22,10 +26,13 @@ async function getTableColumnMeta({ table, column, filtered, startsWith, key, pg
22
26
  };
23
27
  }
24
28
  export default async function suggest(req, reply) {
25
- const { params, query, pg: pg1, user } = req;
26
- const lang = query.lang || "ua";
27
29
  const time = Date.now();
28
- const parent = query.parent || "";
30
+ const { params, user, query = {}, pg: pg1 = pgClients.client } = req;
31
+ const { lang = "ua", parent = "" } = query;
32
+ const debugMode = config.local ||
33
+ user?.user_type?.includes?.("admin") ||
34
+ process.env.NODE_ENV === "test" ||
35
+ process.env.VITEST;
29
36
  if (params?.data && params.data?.startsWith?.("hash-")) {
30
37
  const filepath = path.join(process.cwd(), `/log/suggest/${params.data.replace("hash-", "")}.json`);
31
38
  if (existsSync(filepath)) {
@@ -33,40 +40,26 @@ export default async function suggest(req, reply) {
33
40
  params.data = `${table}:${column}`;
34
41
  }
35
42
  }
36
- const [table, column] = params.data?.includes(":")
43
+ const [table1, column1] = params.data?.includes(":")
37
44
  ? params.data.split(":")
38
45
  : [query.token, query.column];
39
46
  const selectName = query.sel || params.data;
47
+ const table = table1 || query.token;
48
+ const column = column1 || query.column;
40
49
  if (!selectName) {
41
50
  return reply.status(400).send({
42
51
  error: "name is required",
43
52
  code: 400,
44
53
  });
45
54
  }
46
- const { body: hookBody } = table || query.token
55
+ const { body: hookBody } = table
47
56
  ? (await applyHook("preSuggest", {
48
57
  pg: pg1,
49
- table: table || query.token,
58
+ table,
50
59
  })) || {}
51
60
  : {};
52
- const body = await getTemplate("table", table || query.token);
53
- const tableName = hookBody?.table || body?.table || table || query.token;
54
- if (table && !pg1.pk?.[tableName]) {
55
- return reply.status(400).send({
56
- error: "param name is invalid: 1",
57
- code: 400,
58
- });
59
- }
60
- const tableMeta = await getMeta({ pg: pg1, table: tableName });
61
- const columnExists = (hookBody?.columns ||
62
- body?.columns ||
63
- tableMeta?.columns)?.find((col) => col?.name === (column || query.column));
64
- if (table && (!column || !columnExists)) {
65
- return reply.status(400).send({
66
- error: "param name is invalid: 2",
67
- code: 400,
68
- });
69
- }
61
+ const body = await getTemplate("table", table);
62
+ const tableName = hookBody?.table || body?.table || table;
70
63
  if (query.limit && query.limit < 0) {
71
64
  return reply.status(400).send({
72
65
  error: "param limit is invalid",
@@ -75,20 +68,20 @@ export default async function suggest(req, reply) {
75
68
  }
76
69
  const meta = tableName && column
77
70
  ? await getTableColumnMeta({
78
- table,
71
+ table: tableName,
72
+ template: tableName && table && tableName !== table ? table : undefined,
79
73
  column,
80
- filtered: query?.key || query?.val,
74
+ selectName,
75
+ filtered: query.key || query.val,
81
76
  startsWith: !!query.start,
82
77
  key: query.key,
83
- pg: pg1,
84
78
  })
85
79
  : await getSelectMeta({
86
80
  name: selectName,
87
- nocache: query?.nocache,
81
+ nocache: query.nocache,
88
82
  startsWith: !!query.start,
89
83
  key: query.key,
90
84
  parent,
91
- pg: pg1,
92
85
  });
93
86
  if (meta?.minLength && query.key && query.key.length < meta?.minLength) {
94
87
  return reply.status(400).send({
@@ -97,7 +90,32 @@ export default async function suggest(req, reply) {
97
90
  });
98
91
  }
99
92
  const limit = meta?.limit || defaultLimit;
100
- const pg = meta?.db ? getPG(meta.db) : pg1;
93
+ const pg = meta?.db ? await getPGAsync(meta.db) : pg1;
94
+ if (!pg || !pg.pk || !pg.pgType) {
95
+ return reply.status(400).send({
96
+ error: "pg connection not established",
97
+ code: 400,
98
+ });
99
+ }
100
+ if (table && !pg?.pk?.[tableName]) {
101
+ return reply.status(400).send({
102
+ error: "param name is invalid: 1",
103
+ code: 400,
104
+ });
105
+ }
106
+ const columns = hookBody?.columns || body?.columns
107
+ ? hookBody?.columns || body?.columns
108
+ : await getMeta({
109
+ pg,
110
+ table: tableName,
111
+ }).then((el) => el?.columns || []);
112
+ const { name: columnName, dataTypeID } = (columns || []).find((col) => col?.name === column) || {};
113
+ if (table && (!column || !columnName)) {
114
+ return reply.status(400).send({
115
+ error: "param name is invalid: 2",
116
+ code: 400,
117
+ });
118
+ }
101
119
  if (!meta) {
102
120
  return reply.status(404).send({
103
121
  error: "Not found query select",
@@ -108,28 +126,19 @@ export default async function suggest(req, reply) {
108
126
  return meta;
109
127
  }
110
128
  const { arr, searchQuery } = meta;
111
- if (arr && query.token && query.column) {
112
- const loadTable = await getTemplate("table", query.token);
113
- const { columns = [] } = await getMeta({
114
- pg,
115
- table: loadTable?.table || tableName,
116
- });
117
- const column1 = columns.find((el) => el.name === query.column);
118
- const args = { table: tableName };
119
- if (!column1)
120
- return [];
129
+ if (arr && table && column) {
121
130
  const sqlCls = query.count
122
- ? `select value, count(*) from (select ${pg.pgType?.[column1.dataTypeID]?.includes("[]")
123
- ? `unnest(${column1.name})`
124
- : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || "1=1"})q group by value`
125
- : `select array_agg(distinct value)::text[] from (select ${pg.pgType?.[column1.dataTypeID]?.includes("[]")
126
- ? `unnest(${column1.name})`
127
- : `${column1.name}`} as value from ${(loadTable?.table || tableName).replace(/'/g, "''")} where ${hookBody?.query || loadTable?.query || "1=1"})q`;
128
- if (query.sql && (config.local || user?.user_type?.includes?.("admin"))) {
131
+ ? `select value, count(*) from (select ${pg.pgType?.[dataTypeID]?.includes("[]")
132
+ ? `unnest(${columnName})`
133
+ : `${columnName}`} as value from ${tableName.replace(/'/g, "''")} where ${hookBody?.query || body?.query || "1=1"})q group by value`
134
+ : `select array_agg(distinct value)::text[] from (select ${pg.pgType?.[dataTypeID]?.includes("[]")
135
+ ? `unnest(${columnName})`
136
+ : `${columnName}`} as value from ${tableName.replace(/'/g, "''")} where ${hookBody?.query || body?.query || "1=1"})q`;
137
+ if (query.sql && debugMode) {
129
138
  return sqlCls;
130
139
  }
131
- if (pg.pk?.[loadTable?.table || tableName] && column1?.name) {
132
- const qRes = await pg.queryCache(sqlCls, args);
140
+ if (tableName && pg?.pk?.[tableName] && columnName) {
141
+ const qRes = await pg.queryCache(sqlCls, { table: tableName });
133
142
  const vals = (query.count
134
143
  ? qRes.rows?.map?.((el) => el.value?.toString?.())
135
144
  : qRes.rows?.[0]?.array_agg) || [];
@@ -152,9 +161,9 @@ export default async function suggest(req, reply) {
152
161
  if (config.debug) {
153
162
  logger.file("suggest/debug", {
154
163
  type: 1,
155
- loadTable: loadTable?.table,
164
+ table,
156
165
  tableName,
157
- column: column?.name,
166
+ column: columnName,
158
167
  data,
159
168
  data1,
160
169
  data2,
@@ -167,9 +176,7 @@ export default async function suggest(req, reply) {
167
176
  count: data.length,
168
177
  total: arr.length,
169
178
  mode: "array",
170
- sql: config.local || user?.user_type?.includes?.("admin")
171
- ? sqlCls
172
- : undefined,
179
+ sql: debugMode ? sqlCls : undefined,
173
180
  data,
174
181
  };
175
182
  }
@@ -211,22 +218,13 @@ export default async function suggest(req, reply) {
211
218
  const where = [search, val, meta.pk ? `${meta.pk} is not null` : null]
212
219
  .filter(Boolean)
213
220
  .join(" and ") || "true";
214
- const loadTable = await getTemplate("table", query.token);
215
- const tableName1 = hookBody?.table || loadTable?.table || query.token;
216
- const { columns = [] } = await getMeta({ pg: pg1, table: tableName1 });
217
- const { name: filterColumn, dataTypeID } = query.column
218
- ? columns.find((el) => el.name === query.column) || {}
219
- : {};
220
- const filter = query.token &&
221
- pg.pk?.[loadTable?.table || query.token] &&
222
- filterColumn &&
223
- dataTypeID
221
+ const filter = table && pg.pk?.[table] && columnName && dataTypeID
224
222
  ? `id in (select ${pg.pgType[dataTypeID]?.includes("[]")
225
- ? `unnest(${filterColumn})`
226
- : filterColumn} from ${(loadTable?.table || query.token).replace(/'/g, "''")})`
223
+ ? `unnest(${columnName})`
224
+ : columnName} from ${table.replace(/'/g, "''")})`
227
225
  : "true";
228
226
  const sqlSuggest = `with c(id,text) as ( ${meta.original.replace(/{{parent}}/gi, parent)} where ${where} ${meta.original.includes("order by") ? "" : "order by 2"}) select * from c where ${filter} limit ${Math.min(query.limit || meta.limit || limit, limit)}`.replace(/{{uid}}/g, user?.uid || "0");
229
- if (query.sql && (config.local || user?.user_type?.includes?.("admin"))) {
227
+ if (query.sql && debugMode) {
230
228
  return sqlSuggest;
231
229
  }
232
230
  // query
@@ -248,9 +246,7 @@ export default async function suggest(req, reply) {
248
246
  total: meta.count - 0,
249
247
  mode: type === "cls" ? "array" : "sql",
250
248
  db: meta.db,
251
- sql: config.local || user?.user_type?.includes?.("admin")
252
- ? sqlSuggest
253
- : undefined,
249
+ sql: debugMode ? sqlSuggest : undefined,
254
250
  data: data.map((el) => ({
255
251
  count: el.count,
256
252
  id: el.id,
@@ -284,9 +280,7 @@ export default async function suggest(req, reply) {
284
280
  total: meta.count - 0,
285
281
  mode: "sql",
286
282
  db: meta.db,
287
- sql: config.local || user?.user_type?.includes?.("admin")
288
- ? sqlSuggest
289
- : undefined,
283
+ sql: debugMode ? sqlSuggest : undefined,
290
284
  data,
291
285
  };
292
286
  return message;
@@ -395,7 +395,7 @@ export default async function dataAPI({ pg = pgClients.client, params, table, id
395
395
  from (select * ${sql
396
396
  ?.filter((el) => el.inline)
397
397
  .map((el) => `,(${el.sql})`)
398
- .join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable})q
398
+ .join("") || ""} from ${viewSql ? `(${viewSql})` : table1} t ${sqlTable})t
399
399
  where ${[loadTable?.query, tokenData?.query, accessQuery, contextQuery]
400
400
  .filter(Boolean)
401
401
  .filter((el) => checkQuery(el))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "2.0.117",
3
+ "version": "2.0.119",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [
@@ -90,4 +90,4 @@
90
90
  },
91
91
  "author": "Softpro",
92
92
  "license": "ISC"
93
- }
93
+ }