@opengis/fastify-table 2.4.17 → 2.4.19

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":"getMeta.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/pg/funcs/getMeta.ts"],"names":[],"mappings":"AAMA,wBAA8B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,gBAyE5D"}
1
+ {"version":3,"file":"getMeta.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/pg/funcs/getMeta.ts"],"names":[],"mappings":"AAMA,wBAA8B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,gBAmG5D"}
@@ -3,7 +3,7 @@ import getPG from "./getPG.js";
3
3
  import { BadRequestError } from "../../../../errors.js";
4
4
  const data = {};
5
5
  export default async function getMeta(opt, nocache) {
6
- const pg = opt?.pg || getPG({ name: "client" });
6
+ const pg = opt?.pg || getPG();
7
7
  if (!pg) {
8
8
  throw BadRequestError("pg client not found");
9
9
  }
@@ -11,24 +11,42 @@ export default async function getMeta(opt, nocache) {
11
11
  if (!pg.pk || nocache) {
12
12
  await init(pg);
13
13
  }
14
- const table1 = opt?.table || opt;
15
- const table = pg.pk?.[table1] ? table1 : table1?.replace?.(/"/g, "");
16
- if (pg?.options?.database && data[pg.options.database]?.[table] && !nocache)
17
- return data[pg.options.database][table];
18
- if (!pg?.tlist?.includes(table) &&
19
- !pg?.tlist?.includes(table?.replace?.(/"/g, ""))) {
20
- throw BadRequestError(`table not found: ${table}`);
14
+ const tableOriginal = opt?.table || opt;
15
+ const [schemaName, tableName] = tableOriginal?.replace?.(/"/g, "").split(".");
16
+ const tableSafe = `${schemaName}."${tableName}"`;
17
+ const tableUnsafe = `${schemaName}.${tableName}`;
18
+ if (pg?.options?.database &&
19
+ data[pg.options.database]?.[tableUnsafe] &&
20
+ !nocache) {
21
+ return data[pg.options.database][tableUnsafe];
21
22
  }
22
- const { fields = [] } = await pg.query(`select * from ${table} limit 0`);
23
+ const tableRows = await pg
24
+ .query(`SELECT
25
+ nspname || '.' || relname as name,
26
+ relkind
27
+ FROM pg_class a
28
+ JOIN pg_namespace b
29
+ ON b.oid = a.relnamespace
30
+ WHERE relkind IN ('r', 'v', 'm')`)
31
+ .then((r) => r.rows || []);
32
+ const tlist = tableRows.map((el) => el.name);
33
+ if (!tlist.includes(tableUnsafe)) {
34
+ throw BadRequestError(`table not found: ${tableOriginal}`);
35
+ }
36
+ const relkinds = tableRows.reduce((acc, curr) => Object.assign(acc, { [curr.name]: curr.relkind }), {});
37
+ const fields = await pg
38
+ .query(`select * from ${tableSafe} limit 0`)
39
+ .then((r) => r.fields || []);
40
+ // remove quotes from table names
23
41
  const pks1 = await pg
24
- .query(`SELECT json_object_agg(c.conrelid::regclass, a.attname) as pks1
42
+ .query(`SELECT json_object_agg(replace(c.conrelid::regclass::text,'"',''), a.attname)
25
43
  FROM pg_constraint c
26
44
  left join pg_attribute a on c.conrelid=a.attrelid and a.attnum = c.conkey[1]
27
- WHERE c.contype='p'::"char" and c.conrelid::regclass = $1::regclass`, [table])
28
- .then((el) => el.rows[0].pks1 || {});
29
- const pk = table.startsWith("public.")
30
- ? pks1[table.replace("public.", "")]
31
- : pks1[table] || pg.pk[table];
45
+ WHERE c.contype='p'::"char"`)
46
+ .then((r) => r.rows[0].json_object_agg || {});
47
+ const pk = tableUnsafe.startsWith("public.")
48
+ ? pks1[tableUnsafe.replace("public.", "")]
49
+ : pks1[tableUnsafe];
32
50
  const geomColumns = fields.filter((el) => pg.pgType?.[el.dataTypeID] === "geometry");
33
51
  const geomAttr = geomColumns.find((el) => el.name === "geom_4326") || geomColumns[0];
34
52
  const dbColumns = await pg
@@ -36,21 +54,20 @@ export default async function getMeta(opt, nocache) {
36
54
  attname,
37
55
  json_build_object(
38
56
  'title', pg_catalog.col_description(attrelid,attnum)
39
- /*,'type', atttypid::regtype */
40
57
  )
41
58
  ) from pg_catalog.pg_attribute a
42
- where attrelid=$1::regclass and attnum>0`, [table])
59
+ where attrelid=$1::regclass and attnum>0`, [tableSafe])
43
60
  .then((el) => el.rows?.[0]?.json_object_agg || {});
44
61
  fields.forEach((el) => Object.assign(el, { ...(dbColumns[el.name] || {}) }));
45
62
  const res = {
46
63
  pk,
47
64
  columns: fields,
48
65
  geom: geomAttr?.name,
49
- view: ["v", "m"].includes(pg.relkinds?.[table]),
66
+ view: ["v", "m"].includes(relkinds?.[tableSafe]),
50
67
  };
51
68
  if (!data[pg.options.database]) {
52
69
  data[pg.options.database] = {};
53
70
  }
54
- data[pg.options.database][table] = res;
71
+ data[pg.options.database][tableUnsafe] = res;
55
72
  return res;
56
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/pg/funcs/init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,iBAAe,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,iBA6NtC;AAGD,eAAe,IAAI,CAAC"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../../../../server/plugins/pg/funcs/init.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAIpD,iBAAe,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,iBA8NtC;AAGD,eAAe,IAAI,CAAC"}
@@ -78,6 +78,8 @@ async function init(client) {
78
78
  throw new Error("empty pg client");
79
79
  try {
80
80
  const data = await client.query(q, args);
81
+ if (config.trace)
82
+ console.log(q, args);
81
83
  return data;
82
84
  }
83
85
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/file/index.ts"],"names":[],"mappings":"AAgBA,iBAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,GAAE,GAAQ,QA+BtC;AAED,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/file/index.ts"],"names":[],"mappings":"AAoBA,iBAAS,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,GAAE,GAAQ,QA8BtC;AAED,eAAe,MAAM,CAAC"}
@@ -9,14 +9,15 @@ import uploadImage from "./controllers/uploadImage.js";
9
9
  import deleteFileAPI from "./controllers/delete.js";
10
10
  import { resizeSchema } from "./schema.js";
11
11
  const tags = ["core", "file"];
12
+ const { uploadPolicy = "L1" } = config;
12
13
  const publicParams = { config: { tags, policy: "L0" } };
14
+ const privateParams = { config: { tags, policy: uploadPolicy } };
13
15
  function plugin(app, opt = {}) {
14
- app.get("/files/*", { config: { tags, policy: "L0" } }, files);
16
+ app.get("/files/*", publicParams, files);
15
17
  if (!app.hasRoute({ method: "GET", url: "/file/resize" })) {
16
18
  app.get("/file/resize", {
17
19
  config: {
18
- tags,
19
- policy: "L0",
20
+ ...publicParams,
20
21
  rateLimit: config.rateLimit
21
22
  ? {
22
23
  max: +(config.rateLimit?.resize?.max || 1000),
@@ -28,8 +29,8 @@ function plugin(app, opt = {}) {
28
29
  }, resize);
29
30
  }
30
31
  app.get("/api/resize-all", { config: { tags, role: "admin" }, schema: resizeSchema }, resizeAll);
31
- app.post("/file/upload/*", publicParams, upload);
32
- app.post("/file/upload-image/*", publicParams, uploadImage);
32
+ app.post("/file/upload/*", privateParams, upload);
33
+ app.post("/file/upload-image/*", privateParams, uploadImage);
33
34
  app.get("/file/download/*", publicParams, download);
34
35
  app.get("/file/delete/*", publicParams, deleteFileAPI);
35
36
  app.get(`/api/export`, publicParams, exportTable);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/upload/index.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,GAAG,EAAE,GAAG,QAmGtC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/upload/index.ts"],"names":[],"mappings":"AAYA,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,GAAG,EAAE,GAAG,QA2GtC"}
@@ -1,17 +1,20 @@
1
1
  import config from "../../../config.js";
2
- const { chunkSize = 5242880 } = config;
2
+ const { chunkSize = 5242880, uploadPolicy = "L1" } = config;
3
3
  import { prefix } from "../../plugins/upload/index.js";
4
4
  import startUpload from "../../plugins/upload/startUpload.js";
5
5
  import uploadChunk from "../../plugins/upload/uploadChunk.js";
6
6
  import getUploadStatus from "../../plugins/upload/getUploadStatus.js";
7
7
  import { BadRequestError, LengthRequiredError } from "../../../errors.js";
8
- const policy = "L0"; // public
9
8
  const tags = ["core", "upload"];
10
9
  export default function plugin(app) {
11
10
  // provide server with metadata for chunked upload
12
11
  app.post(`/${prefix}/start`, {
13
- config: { tags, description: "Init chunked upload", policy },
14
- }, async (req, reply) => {
12
+ config: {
13
+ tags,
14
+ description: "Init chunked upload",
15
+ policy: uploadPolicy,
16
+ },
17
+ }, async (req) => {
15
18
  const { fileName, size, subdir, id } = req.body || {};
16
19
  if (!fileName || !size) {
17
20
  throw BadRequestError("fileName and size are required");
@@ -33,7 +36,7 @@ export default function plugin(app) {
33
36
  config: {
34
37
  tags,
35
38
  description: "Upload chunk of data",
36
- policy: config.uploadChunkPolicy || policy,
39
+ policy: uploadPolicy,
37
40
  rateLimit: config.rateLimit
38
41
  ? {
39
42
  max: +(config.rateLimit?.uploadChunk?.max || 1000),
@@ -67,7 +70,11 @@ export default function plugin(app) {
67
70
  });
68
71
  // check upload status
69
72
  app.get(`/${prefix}/:id`, {
70
- config: { tags, description: "Check chunked upload status", policy },
73
+ config: {
74
+ tags,
75
+ description: "Check chunked upload status",
76
+ policy: uploadPolicy,
77
+ },
71
78
  }, async (req) => {
72
79
  const { id } = req.params;
73
80
  const status = await getUploadStatus({ id });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/widget/index.ts"],"names":[],"mappings":"AAkDA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,GAAG,EAAE,GAAG,QAmHrC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../server/routes/widget/index.ts"],"names":[],"mappings":"AAoDA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,GAAG,EAAE,GAAG,QAiIrC"}
@@ -1,4 +1,5 @@
1
1
  import { addHook } from "../../../utils.js";
2
+ import config from "../../../config.js";
2
3
  import widgetDel from "./controllers/widget.del.js";
3
4
  import widgetSet from "./controllers/widget.set.js";
4
5
  import widgetGet from "./controllers/widget.get.js";
@@ -8,7 +9,9 @@ import widgetEntityGet from "./services/widget.entity.get.js";
8
9
  import widgetEntitySet from "./services/widget.entity.set.js";
9
10
  import widgetEntityDel from "./services/widget.entity.del.js";
10
11
  import uploadMultiPart from "../../plugins/file/uploadMultiPart.js";
12
+ import { UnauthorizedError } from "../../../errors.js";
11
13
  const tags = ["core", "widget"];
14
+ const { uploadPolicy = "L1" } = config;
12
15
  const tableSchema = {
13
16
  params: {
14
17
  type: "object",
@@ -37,10 +40,9 @@ const tableSchema = {
37
40
  },
38
41
  };
39
42
  addHook("onWidgetSet", onWidgetSet);
40
- const policy = "L0";
41
- const params = { config: { tags, policy }, schema: tableSchema };
43
+ const publicParams = { config: { tags, policy: "L0" }, schema: tableSchema };
42
44
  export default function route(app) {
43
- app.get("/v2/widget/:type/:entityType/:objectid", params, async (req) => {
45
+ app.get("/v2/widget/:type/:entityType/:objectid", publicParams, async (req) => {
44
46
  const result = await widgetEntityGet({
45
47
  type: req.params.type,
46
48
  entityType: req.params.entityType,
@@ -51,9 +53,12 @@ export default function route(app) {
51
53
  }, req.pg);
52
54
  return result;
53
55
  });
54
- app.post("/v2/widget/:type/:entityType/:objectid", params, async (req) => {
56
+ app.post("/v2/widget/:type/:entityType/:objectid", publicParams, async (req) => {
55
57
  if (["gallery", "file"].includes(req.params.type) &&
56
58
  req.headers["content-type"]?.startsWith("multipart/form-data")) {
59
+ if (!req.user?.uid && uploadPolicy !== "L0") {
60
+ throw UnauthorizedError("unauthorized");
61
+ }
57
62
  const file = await uploadMultiPart(req);
58
63
  const result = await widgetEntitySet({
59
64
  type: req.params.type,
@@ -74,9 +79,12 @@ export default function route(app) {
74
79
  }, req.pg);
75
80
  return result;
76
81
  });
77
- app.put("/v2/widget/:type/:entityType/:objectid/:id", params, async (req) => {
82
+ app.put("/v2/widget/:type/:entityType/:objectid/:id", publicParams, async (req) => {
78
83
  if (["gallery", "file"].includes(req.params.type) &&
79
84
  req.headers["content-type"]?.startsWith("multipart/form-data")) {
85
+ if (!req.user?.uid && uploadPolicy !== "L0") {
86
+ throw UnauthorizedError("unauthorized");
87
+ }
80
88
  const file = await uploadMultiPart(req);
81
89
  const result = await widgetEntitySet({
82
90
  type: req.params.type,
@@ -99,7 +107,7 @@ export default function route(app) {
99
107
  }, req.pg);
100
108
  return result;
101
109
  });
102
- app.delete("/v2/widget/:type/:entityType/:objectid/:id", params, async (req) => {
110
+ app.delete("/v2/widget/:type/:entityType/:objectid/:id", publicParams, async (req) => {
103
111
  const result = await widgetEntityDel({
104
112
  type: req.params.type,
105
113
  entityType: req.params.entityType,
@@ -110,9 +118,9 @@ export default function route(app) {
110
118
  }, req.pg);
111
119
  return result;
112
120
  });
113
- app.get("/widget/:type/:objectid", params, widgetGet);
114
- app.post("/widget/:type/:objectid/:id?", params, widgetSet);
115
- // app.put("/widget/:type/:objectid/:id", params, widgetSet);
116
- app.delete("/widget/:type/:objectid/:id", params, widgetDel);
117
- app.put("/file-edit/:id", params, fileEdit);
121
+ app.get("/widget/:type/:objectid", publicParams, widgetGet);
122
+ app.post("/widget/:type/:objectid/:id?", publicParams, widgetSet);
123
+ // app.put("/widget/:type/:objectid/:id", publicParams, widgetSet);
124
+ app.delete("/widget/:type/:objectid/:id", publicParams, widgetDel);
125
+ app.put("/file-edit/:id", publicParams, fileEdit);
118
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengis/fastify-table",
3
- "version": "2.4.17",
3
+ "version": "2.4.19",
4
4
  "type": "module",
5
5
  "description": "core-plugins",
6
6
  "keywords": [