prostgles-server 4.2.566 → 4.2.569

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 (227) hide show
  1. package/dist/Auth/AuthTypes.d.ts +5 -5
  2. package/dist/Auth/AuthTypes.d.ts.map +1 -1
  3. package/dist/Auth/AuthTypes.js.map +1 -1
  4. package/dist/Auth/endpoints/setCatchAllRequestHandler.d.ts.map +1 -1
  5. package/dist/Auth/endpoints/setCatchAllRequestHandler.js +2 -2
  6. package/dist/Auth/endpoints/setCatchAllRequestHandler.js.map +1 -1
  7. package/dist/DBSchemaBuilder/getColumnTypescriptDefinition.d.ts +1 -1
  8. package/dist/DBSchemaBuilder/getColumnTypescriptDefinition.d.ts.map +1 -1
  9. package/dist/DBSchemaBuilder/getDBGeneratedSchema.d.ts +1 -1
  10. package/dist/DBSchemaBuilder/getDBGeneratedSchema.d.ts.map +1 -1
  11. package/dist/DboBuilder/DboBuilder.js +4 -4
  12. package/dist/DboBuilder/DboBuilder.js.map +1 -1
  13. package/dist/DboBuilder/DboBuilderTypes.d.ts +2 -18
  14. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  15. package/dist/DboBuilder/DboBuilderTypes.js.map +1 -1
  16. package/dist/DboBuilder/TableHandler/TableHandler.d.ts +15 -5
  17. package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
  18. package/dist/DboBuilder/TableHandler/TableHandler.js +4 -3
  19. package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
  20. package/dist/DboBuilder/TableHandler/delete.d.ts.map +1 -1
  21. package/dist/DboBuilder/TableHandler/delete.js +17 -14
  22. package/dist/DboBuilder/TableHandler/delete.js.map +1 -1
  23. package/dist/DboBuilder/TableHandler/insert/insert.d.ts.map +1 -1
  24. package/dist/DboBuilder/TableHandler/insert/insert.js +3 -12
  25. package/dist/DboBuilder/TableHandler/insert/insert.js.map +1 -1
  26. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.d.ts.map +1 -1
  27. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.js +10 -15
  28. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.js.map +1 -1
  29. package/dist/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.js +1 -1
  30. package/dist/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.js.map +1 -1
  31. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts +8 -6
  32. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts.map +1 -1
  33. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js +30 -48
  34. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js.map +1 -1
  35. package/dist/DboBuilder/TableHandler/update.d.ts +1 -1
  36. package/dist/DboBuilder/TableHandler/update.d.ts.map +1 -1
  37. package/dist/DboBuilder/TableHandler/update.js +8 -18
  38. package/dist/DboBuilder/TableHandler/update.js.map +1 -1
  39. package/dist/DboBuilder/TableHandler/updateFile.d.ts +2 -3
  40. package/dist/DboBuilder/TableHandler/updateFile.d.ts.map +1 -1
  41. package/dist/DboBuilder/TableHandler/updateFile.js +8 -28
  42. package/dist/DboBuilder/TableHandler/updateFile.js.map +1 -1
  43. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +2 -2
  44. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
  45. package/dist/DboBuilder/ViewHandler/getInfo.d.ts +1 -1
  46. package/dist/DboBuilder/ViewHandler/getInfo.d.ts.map +1 -1
  47. package/dist/DboBuilder/ViewHandler/getInfo.js +12 -1
  48. package/dist/DboBuilder/ViewHandler/getInfo.js.map +1 -1
  49. package/dist/DboBuilder/getColumns.d.ts.map +1 -1
  50. package/dist/DboBuilder/getColumns.js +29 -4
  51. package/dist/DboBuilder/getColumns.js.map +1 -1
  52. package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts +2 -2
  53. package/dist/DboBuilder/uploadFile.d.ts +5 -6
  54. package/dist/DboBuilder/uploadFile.d.ts.map +1 -1
  55. package/dist/DboBuilder/uploadFile.js +28 -42
  56. package/dist/DboBuilder/uploadFile.js.map +1 -1
  57. package/dist/FileManager/FileManager.d.ts +22 -78
  58. package/dist/FileManager/FileManager.d.ts.map +1 -1
  59. package/dist/FileManager/FileManager.js +1 -219
  60. package/dist/FileManager/FileManager.js.map +1 -1
  61. package/dist/FileManager/getFileTableConfig.d.ts +26 -0
  62. package/dist/FileManager/getFileTableConfig.d.ts.map +1 -0
  63. package/dist/FileManager/getFileTableConfig.js +115 -0
  64. package/dist/FileManager/getFileTableConfig.js.map +1 -0
  65. package/dist/FileManager/getLocalStorageClient.d.ts +3 -0
  66. package/dist/FileManager/getLocalStorageClient.d.ts.map +1 -0
  67. package/dist/FileManager/getLocalStorageClient.js +105 -0
  68. package/dist/FileManager/getLocalStorageClient.js.map +1 -0
  69. package/dist/FileManager/getValidatedFileType.d.ts +2 -2
  70. package/dist/FileManager/getValidatedFileType.d.ts.map +1 -1
  71. package/dist/FileManager/getValidatedFileType.js +7 -9
  72. package/dist/FileManager/getValidatedFileType.js.map +1 -1
  73. package/dist/FileManager/setupFileServeHandler.d.ts +9 -0
  74. package/dist/FileManager/setupFileServeHandler.d.ts.map +1 -0
  75. package/dist/FileManager/setupFileServeHandler.js +114 -0
  76. package/dist/FileManager/setupFileServeHandler.js.map +1 -0
  77. package/dist/Filtering/Filtering.d.ts +1 -1
  78. package/dist/JSONBSchemaValidation/validateJSONBSchemaSQL.d.ts.map +1 -1
  79. package/dist/JSONBSchemaValidation/validateJSONBSchemaSQL.js +39 -0
  80. package/dist/JSONBSchemaValidation/validateJSONBSchemaSQL.js.map +1 -1
  81. package/dist/Logging.d.ts +1 -1
  82. package/dist/Logging.d.ts.map +1 -1
  83. package/dist/Prostgles.d.ts +52 -6
  84. package/dist/Prostgles.d.ts.map +1 -1
  85. package/dist/Prostgles.js +8 -47
  86. package/dist/Prostgles.js.map +1 -1
  87. package/dist/ProstglesTypes.d.ts +23 -21
  88. package/dist/ProstglesTypes.d.ts.map +1 -1
  89. package/dist/ProstglesTypes.js.map +1 -1
  90. package/dist/PubSubManager/notifListener.d.ts.map +1 -1
  91. package/dist/PubSubManager/notifListener.js +0 -1
  92. package/dist/PubSubManager/notifListener.js.map +1 -1
  93. package/dist/PublishParser/getDBSchemaTable.d.ts +2 -2
  94. package/dist/PublishParser/getDBSchemaTable.d.ts.map +1 -1
  95. package/dist/PublishParser/getDBSchemaTable.js +5 -3
  96. package/dist/PublishParser/getDBSchemaTable.js.map +1 -1
  97. package/dist/PublishParser/getSchemaFromPublish.d.ts.map +1 -1
  98. package/dist/PublishParser/getSchemaFromPublish.js +2 -2
  99. package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
  100. package/dist/PublishParser/publishTypesAndUtils.d.ts +1 -0
  101. package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
  102. package/dist/PublishParser/publishTypesAndUtils.js.map +1 -1
  103. package/dist/RestApi.d.ts +1 -5
  104. package/dist/RestApi.d.ts.map +1 -1
  105. package/dist/RestApi.js +10 -5
  106. package/dist/RestApi.js.map +1 -1
  107. package/dist/TableConfig/{TableConfig.d.ts → TableConfigTypes.d.ts} +19 -64
  108. package/dist/TableConfig/TableConfigTypes.d.ts.map +1 -0
  109. package/dist/TableConfig/TableConfigTypes.js +3 -0
  110. package/dist/TableConfig/TableConfigTypes.js.map +1 -0
  111. package/dist/TableConfig/TableConfigurator.d.ts +104 -0
  112. package/dist/TableConfig/TableConfigurator.d.ts.map +1 -0
  113. package/dist/TableConfig/{TableConfig.js → TableConfigurator.js} +31 -44
  114. package/dist/TableConfig/TableConfigurator.js.map +1 -0
  115. package/dist/TableConfig/applyTableConfig.d.ts.map +1 -1
  116. package/dist/TableConfig/applyTableConfig.js +0 -1
  117. package/dist/TableConfig/applyTableConfig.js.map +1 -1
  118. package/dist/TableConfig/getColumnConfig.d.ts +1 -1
  119. package/dist/TableConfig/getColumnConfig.d.ts.map +1 -1
  120. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts +1 -1
  121. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts.map +1 -1
  122. package/dist/TableConfig/getColumnSQLDefinitionQuery.js +1 -1
  123. package/dist/TableConfig/getColumnSQLDefinitionQuery.js.map +1 -1
  124. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts +1 -1
  125. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts.map +1 -1
  126. package/dist/TableConfig/getCreateSchemaQueries.d.ts +26 -8
  127. package/dist/TableConfig/getCreateSchemaQueries.d.ts.map +1 -1
  128. package/dist/TableConfig/getSchemaDiffQueries.d.ts.map +1 -1
  129. package/dist/TableConfig/getSchemaDiffQueries.js +12 -8
  130. package/dist/TableConfig/getSchemaDiffQueries.js.map +1 -1
  131. package/dist/TableConfig/getTableColumnQueries.d.ts +1 -1
  132. package/dist/TableConfig/getTableColumnQueries.d.ts.map +1 -1
  133. package/dist/TableConfig/getTableColumnQueries.js +19 -19
  134. package/dist/TableConfig/getTableColumnQueries.js.map +1 -1
  135. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts +26 -10
  136. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts.map +1 -1
  137. package/dist/TableConfig/getTableConfigSchemaQueries.js +8 -10
  138. package/dist/TableConfig/getTableConfigSchemaQueries.js.map +1 -1
  139. package/dist/TableConfig/indexes/getIndexesQueries.d.ts +1 -1
  140. package/dist/TableConfig/indexes/getIndexesQueries.d.ts.map +1 -1
  141. package/dist/TableConfig/initTableConfig.d.ts +1 -1
  142. package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
  143. package/dist/TableConfig/initTableConfig.js +6 -3
  144. package/dist/TableConfig/initTableConfig.js.map +1 -1
  145. package/dist/WebsocketAPI/getClientSchema.d.ts.map +1 -1
  146. package/dist/WebsocketAPI/getClientSchema.js +6 -1
  147. package/dist/WebsocketAPI/getClientSchema.js.map +1 -1
  148. package/dist/index.d.ts +3 -2
  149. package/dist/index.d.ts.map +1 -1
  150. package/dist/index.js +1 -0
  151. package/dist/index.js.map +1 -1
  152. package/dist/initProstgles.d.ts.map +1 -1
  153. package/dist/initProstgles.js +2 -5
  154. package/dist/initProstgles.js.map +1 -1
  155. package/dist/runClientRequest.js +1 -1
  156. package/dist/runClientRequest.js.map +1 -1
  157. package/dist/updateConfiguration.d.ts.map +1 -1
  158. package/dist/updateConfiguration.js +0 -3
  159. package/dist/updateConfiguration.js.map +1 -1
  160. package/eslint.config.mjs +0 -3
  161. package/lib/Auth/AuthTypes.ts +6 -7
  162. package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +6 -2
  163. package/lib/DBSchemaBuilder/getColumnTypescriptDefinition.ts +1 -1
  164. package/lib/DBSchemaBuilder/getDBGeneratedSchema.ts +1 -1
  165. package/lib/DboBuilder/DboBuilder.ts +4 -4
  166. package/lib/DboBuilder/DboBuilderTypes.ts +7 -20
  167. package/lib/DboBuilder/TableHandler/TableHandler.ts +7 -4
  168. package/lib/DboBuilder/TableHandler/delete.ts +23 -17
  169. package/lib/DboBuilder/TableHandler/insert/insert.ts +3 -12
  170. package/lib/DboBuilder/TableHandler/insert/insertNestedRecords.ts +13 -17
  171. package/lib/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.ts +1 -1
  172. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +41 -54
  173. package/lib/DboBuilder/TableHandler/update.ts +8 -19
  174. package/lib/DboBuilder/TableHandler/updateFile.ts +20 -42
  175. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +1 -1
  176. package/lib/DboBuilder/ViewHandler/getInfo.ts +19 -2
  177. package/lib/DboBuilder/getColumns.ts +40 -4
  178. package/lib/DboBuilder/uploadFile.ts +34 -50
  179. package/lib/FileManager/FileManager.ts +23 -249
  180. package/lib/FileManager/getFileTableConfig.ts +142 -0
  181. package/lib/FileManager/getLocalStorageClient.ts +118 -0
  182. package/lib/FileManager/getValidatedFileType.ts +14 -22
  183. package/lib/FileManager/setupFileServeHandler.ts +102 -0
  184. package/lib/JSONBSchemaValidation/validateJSONBSchemaSQL.ts +39 -0
  185. package/lib/Logging.ts +0 -3
  186. package/lib/Prostgles.ts +9 -44
  187. package/lib/ProstglesTypes.ts +34 -23
  188. package/lib/PubSubManager/notifListener.ts +0 -2
  189. package/lib/PublishParser/getDBSchemaTable.ts +6 -3
  190. package/lib/PublishParser/getSchemaFromPublish.ts +2 -1
  191. package/lib/PublishParser/publishTypesAndUtils.ts +1 -0
  192. package/lib/RestApi.ts +12 -12
  193. package/lib/TableConfig/{TableConfig.ts → TableConfigTypes.ts} +18 -217
  194. package/lib/TableConfig/TableConfigurator.ts +191 -0
  195. package/lib/TableConfig/applyTableConfig.ts +2 -3
  196. package/lib/TableConfig/getColumnConfig.ts +2 -2
  197. package/lib/TableConfig/getColumnSQLDefinitionQuery.ts +2 -2
  198. package/lib/TableConfig/getConstraintDefinitionQueries.ts +1 -1
  199. package/lib/TableConfig/getCreateSchemaQueries.ts +1 -1
  200. package/lib/TableConfig/getSchemaDiffQueries.ts +14 -11
  201. package/lib/TableConfig/getTableColumnQueries.ts +22 -20
  202. package/lib/TableConfig/getTableConfigSchemaQueries.ts +20 -22
  203. package/lib/TableConfig/indexes/getIndexesQueries.ts +1 -1
  204. package/lib/TableConfig/initTableConfig.ts +7 -6
  205. package/lib/WebsocketAPI/getClientSchema.ts +7 -1
  206. package/lib/index.ts +3 -2
  207. package/lib/initProstgles.ts +2 -6
  208. package/lib/runClientRequest.ts +1 -1
  209. package/lib/updateConfiguration.ts +0 -3
  210. package/package.json +2 -3
  211. package/dist/FileManager/initFileManager.d.ts +0 -18
  212. package/dist/FileManager/initFileManager.d.ts.map +0 -1
  213. package/dist/FileManager/initFileManager.js +0 -257
  214. package/dist/FileManager/initFileManager.js.map +0 -1
  215. package/dist/FileManager/upload.d.ts +0 -4
  216. package/dist/FileManager/upload.d.ts.map +0 -1
  217. package/dist/FileManager/upload.js +0 -90
  218. package/dist/FileManager/upload.js.map +0 -1
  219. package/dist/FileManager/uploadStream.d.ts +0 -4
  220. package/dist/FileManager/uploadStream.d.ts.map +0 -1
  221. package/dist/FileManager/uploadStream.js +0 -100
  222. package/dist/FileManager/uploadStream.js.map +0 -1
  223. package/dist/TableConfig/TableConfig.d.ts.map +0 -1
  224. package/dist/TableConfig/TableConfig.js.map +0 -1
  225. package/lib/FileManager/initFileManager.ts +0 -254
  226. package/lib/FileManager/upload.ts +0 -59
  227. package/lib/FileManager/uploadStream.ts +0 -76
@@ -0,0 +1,118 @@
1
+ import crypto from "crypto";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import stream from "stream";
5
+ import type {
6
+ FileUploadArgs,
7
+ LocalConfig,
8
+ LocalStorageClient,
9
+ UploadedCloudFile,
10
+ } from "./FileManager";
11
+
12
+ export const getLocalStorageClient = (localConfig: LocalConfig): LocalStorageClient => {
13
+ const { localFolderPath, minFreeBytes = 100 * 1024 * 1024 } = localConfig; // Default 100MB
14
+
15
+ // Helper to ensure we have enough free disk space (Requires Node >= 18.17.0)
16
+ const checkFreeSpace = async () => {
17
+ try {
18
+ const stats = await fs.promises.statfs(localFolderPath);
19
+ const freeSpace = stats.bavail * stats.bsize;
20
+
21
+ if (freeSpace < minFreeBytes) {
22
+ throw new Error(
23
+ `Insufficient disk space. Required: ${minFreeBytes} bytes, Available: ${freeSpace} bytes`,
24
+ );
25
+ }
26
+ } catch (error: any) {
27
+ // If the directory doesn't exist yet, statfs will throw. We'll ignore and let mkdir handle it.
28
+ if (error.code !== "ENOENT") {
29
+ throw error;
30
+ }
31
+ }
32
+ };
33
+
34
+ return {
35
+ type: "local",
36
+ localFolderPath: localConfig.localFolderPath,
37
+ upload: async (args: FileUploadArgs): Promise<UploadedCloudFile> => {
38
+ // Ensure the target directory exists
39
+ await fs.promises.mkdir(localFolderPath, { recursive: true });
40
+ await checkFreeSpace();
41
+
42
+ const filePath = path.join(localFolderPath, args.fileName);
43
+ const hash = crypto.createHash("md5");
44
+ let contentLength = 0;
45
+
46
+ return new Promise((resolve, reject) => {
47
+ const writeStream = fs.createWriteStream(filePath);
48
+
49
+ writeStream.on("error", reject);
50
+ writeStream.on("finish", () => {
51
+ resolve({
52
+ cloud_url: filePath,
53
+ etag: hash.digest("hex"),
54
+ content_length: contentLength,
55
+ });
56
+ });
57
+
58
+ // Handle raw string or Buffer
59
+ if (typeof args.file === "string" || Buffer.isBuffer(args.file)) {
60
+ const buffer = Buffer.isBuffer(args.file) ? args.file : Buffer.from(args.file);
61
+
62
+ contentLength = buffer.length;
63
+ hash.update(buffer);
64
+
65
+ if (args.onProgress) {
66
+ args.onProgress(contentLength);
67
+ }
68
+
69
+ writeStream.write(buffer);
70
+ writeStream.end();
71
+ } else if (
72
+ args.file instanceof stream.Readable ||
73
+ (args.file as unknown) instanceof stream.PassThrough
74
+ ) {
75
+ let lastProgress = Date.now();
76
+ const throttle = 1000; // 1 second
77
+ args.file.on("data", (chunk: Buffer) => {
78
+ contentLength += chunk.length;
79
+ hash.update(chunk);
80
+ if (args.onProgress && Date.now() - lastProgress > throttle) {
81
+ lastProgress = Date.now();
82
+ args.onProgress(contentLength);
83
+ }
84
+ });
85
+
86
+ args.file.on("error", reject);
87
+ args.file.pipe(writeStream);
88
+ } else {
89
+ reject(new Error("Unsupported file type provided to upload."));
90
+ }
91
+ });
92
+ },
93
+
94
+ downloadAsStream: async (name: string) => {
95
+ const filePath = path.join(localFolderPath, name);
96
+
97
+ try {
98
+ await fs.promises.access(filePath, fs.constants.R_OK);
99
+ } catch (err) {
100
+ throw new Error(`File not found or unreadable: ${name}`);
101
+ }
102
+
103
+ return fs.createReadStream(filePath);
104
+ },
105
+
106
+ delete: async (fileName: string) => {
107
+ const filePath = path.join(localFolderPath, fileName);
108
+ try {
109
+ await fs.promises.unlink(filePath);
110
+ } catch (err: any) {
111
+ // Ignore error if file doesn't exist
112
+ if (err.code !== "ENOENT") {
113
+ throw err;
114
+ }
115
+ }
116
+ },
117
+ };
118
+ };
@@ -1,13 +1,7 @@
1
- import type {
2
- ALLOWED_CONTENT_TYPE} from "prostgles-types";
3
- import {
4
- ALLOWED_EXTENSION,
5
- CONTENT_TYPE_TO_EXT,
6
- getKeys,
7
- isObject,
8
- } from "prostgles-types";
1
+ import type { ALLOWED_CONTENT_TYPE } from "prostgles-types";
2
+ import { CONTENT_TYPE_TO_EXT, getKeys, isObject } from "prostgles-types";
9
3
  import { parseFieldFilter } from "../DboBuilder/ViewHandler/parseFieldFilter";
10
- import type { FileManager} from "./FileManager";
4
+ import type { FileTableConfig } from "../ProstglesTypes";
11
5
  import { getFileType, getFileTypeFromFilename } from "./FileManager";
12
6
 
13
7
  type Args = {
@@ -16,19 +10,17 @@ type Args = {
16
10
  colName?: string;
17
11
  tableName?: string;
18
12
  };
19
- export async function getValidatedFileType(
20
- this: FileManager,
21
- args: Args
13
+ export const getValidatedFileType = async (
14
+ config: FileTableConfig,
15
+ args: Args,
22
16
  ): Promise<{
23
17
  mime: ALLOWED_CONTENT_TYPE;
24
- ext: string; //| ALLOWED_EXTENSION;
18
+ ext: string;
25
19
 
26
20
  /** File name is not returned because we fail if the extensions do not match */
27
21
  // fileName: string;
28
- }> {
22
+ }> => {
29
23
  const { file, fileName, tableName, colName } = args;
30
- const config = this.prostgles?.opts.fileTable;
31
- if (!config) throw new Error("File table config missing");
32
24
 
33
25
  const buffer = typeof file === "string" ? Buffer.from(file, "utf8") : file;
34
26
 
@@ -55,7 +47,7 @@ export async function getValidatedFileType(
55
47
  const allowedContent = parseFieldFilter(colConfig.acceptedContent, false, CONTENTS);
56
48
  if (!allowedContent.some((c) => mime.mime.startsWith(c))) {
57
49
  throw new Error(
58
- `Dissallowed content type provided: ${mime.mime.split("/")[0]}. Allowed content types: ${allowedContent} `
50
+ `Disallowed content type provided: ${mime.mime.split("/")[0]}. Allowed content types: ${allowedContent} `,
59
51
  );
60
52
  }
61
53
  } else if (
@@ -67,12 +59,12 @@ export async function getValidatedFileType(
67
59
  const allowedContentTypes = parseFieldFilter(
68
60
  colConfig.acceptedContentType,
69
61
  false,
70
- getKeys(CONTENT_TYPE_TO_EXT)
62
+ getKeys(CONTENT_TYPE_TO_EXT),
71
63
  );
72
64
 
73
65
  if (!allowedContentTypes.some((c) => c === mime.mime)) {
74
66
  throw new Error(
75
- `Dissallowed MIME provided: ${mime.mime}. Allowed MIME values: ${allowedContentTypes} `
67
+ `Disallowed MIME provided: ${mime.mime}. Allowed MIME values: ${allowedContentTypes} `,
76
68
  );
77
69
  }
78
70
  } else if (
@@ -84,12 +76,12 @@ export async function getValidatedFileType(
84
76
  const allowedExtensions = parseFieldFilter(
85
77
  colConfig.acceptedFileTypes,
86
78
  false,
87
- Object.values(CONTENT_TYPE_TO_EXT).flat()
79
+ Object.values(CONTENT_TYPE_TO_EXT).flat(),
88
80
  );
89
81
 
90
82
  if (!allowedExtensions.some((c) => c === mime.ext)) {
91
83
  throw new Error(
92
- `Dissallowed extension provided: ${mime.ext}. Allowed extension values: ${allowedExtensions} `
84
+ `Disallowed extension provided: ${mime.ext}. Allowed extension values: ${allowedExtensions} `,
93
85
  );
94
86
  }
95
87
  }
@@ -97,4 +89,4 @@ export async function getValidatedFileType(
97
89
  }
98
90
  if (!result?.mime) throw `File MIME type not found for the provided extension: ${result?.ext}`;
99
91
  return result;
100
- }
92
+ };
@@ -0,0 +1,102 @@
1
+ import * as fs from "fs";
2
+ import { join } from "path";
3
+ import { HTTP_FAIL_CODES, removeExpressRoute } from "../Auth/AuthHandler";
4
+ import type { DB } from "../initProstgles";
5
+ import type { Prostgles } from "../Prostgles";
6
+ import type { FileTableConfig } from "../ProstglesTypes";
7
+ import type { ExpressApp } from "../RestApi";
8
+ import { runClientRequest } from "../runClientRequest";
9
+ import type { StorageClient } from "./FileManager";
10
+ import { HOUR } from "./FileManager";
11
+ import type { FileTableRow } from "./getFileTableConfig";
12
+
13
+ export const setupFileServeHandler = (
14
+ db: DB,
15
+ config: FileTableConfig,
16
+ storageClient: StorageClient,
17
+ app: ExpressApp,
18
+ prg: Prostgles,
19
+ ) => {
20
+ const fileTableName = config.tableName;
21
+ const fileServeRoute = config.fileServePath ?? `/${fileTableName}`;
22
+ if (fileServeRoute.endsWith("/")) {
23
+ throw `fileServeRoute must not end with a '/'`;
24
+ }
25
+ const fileRouteExpress = fileServeRoute + "/:name";
26
+
27
+ app.get(fileRouteExpress, async (req, res) => {
28
+ try {
29
+ const { name } = req.params;
30
+ if (typeof name !== "string" || !name) {
31
+ throw "Invalid media name";
32
+ }
33
+ const id = name.slice(0, 36);
34
+ const selectParams = {
35
+ select: {
36
+ id: 1,
37
+ name: 1,
38
+ signed_url: 1,
39
+ signed_url_expires: 1,
40
+ content_type: 1,
41
+ },
42
+ };
43
+ const file = (await runClientRequest.bind(prg)(
44
+ {
45
+ command: "findOne",
46
+ tableName: fileTableName,
47
+ param1: { id },
48
+ param2: selectParams,
49
+ param3: undefined,
50
+ },
51
+ {
52
+ res,
53
+ httpReq: req,
54
+ },
55
+ undefined,
56
+ )) as FileTableRow | undefined;
57
+
58
+ if (!file) {
59
+ res.status(HTTP_FAIL_CODES.NOT_FOUND).send("File not found or not allowed");
60
+ return;
61
+ }
62
+
63
+ if (storageClient.type === "cloud") {
64
+ let url = file.signed_url;
65
+ const expires = +(file.signed_url_expires || 0);
66
+
67
+ const EXPIRES = Date.now() + HOUR;
68
+ if (!url || expires < EXPIRES) {
69
+ url = await storageClient.getSignedUrlForDownload(file.name, 60 * 60);
70
+
71
+ await db.any(
72
+ "UPDATE ${fileTableName:name} SET signed_url = ${signed_url}, signed_url_expires = ${signed_url_expires} WHERE id = ${id}",
73
+ {
74
+ fileTableName,
75
+ id: file.id,
76
+ signed_url: url,
77
+ signed_url_expires: EXPIRES,
78
+ },
79
+ );
80
+ }
81
+
82
+ res.redirect(url);
83
+ } else {
84
+ const localFilePath = join(storageClient.localFolderPath, file.name);
85
+ if (!fs.existsSync(localFilePath)) {
86
+ throw new Error("File not found");
87
+ }
88
+ res.contentType(file.content_type);
89
+ res.sendFile(localFilePath);
90
+ }
91
+ } catch (e) {
92
+ console.log(e);
93
+ res.status(HTTP_FAIL_CODES.BAD_REQUEST).send("Invalid/disallowed file");
94
+ }
95
+ });
96
+
97
+ const destroy = () => {
98
+ removeExpressRoute(app, [fileRouteExpress]);
99
+ };
100
+
101
+ return { destroy };
102
+ };
@@ -329,7 +329,46 @@ BEGIN
329
329
  END LOOP;
330
330
 
331
331
  ${raiseException(`E'No oneOf schemas matching:\n % ), %', v_one_of_errors, path`)}
332
+
333
+ /* tuple: [FieldType, FieldType, ...] */
334
+ ELSIF schema ? 'tuple' THEN
335
+
336
+ IF jsonb_typeof(schema->'tuple') != 'array' THEN
337
+ ${raiseException(`'Invalid tuple schema. Expected tuple to be an array, got: %. %', jsonb_typeof(schema->'tuple'), path`)}
338
+ END IF;
332
339
 
340
+ IF jsonb_typeof(data) != 'array' THEN
341
+ ${raiseException(`'Tuple data is not an array. %', path`)}
342
+ END IF;
343
+
344
+ IF jsonb_array_length(data) != jsonb_array_length(schema->'tuple') THEN
345
+ ${raiseException(`'Tuple length does not match schema. Expected %, got %. %',
346
+ jsonb_array_length(schema->'tuple'),
347
+ jsonb_array_length(data),
348
+ path`)}
349
+ END IF;
350
+
351
+ FOR array_element IN
352
+ SELECT
353
+ data_item.value AS data_value,
354
+ tuple_item.value AS schema_value,
355
+ data_item.ordinality - 1 AS idx
356
+ FROM jsonb_array_elements(data) WITH ORDINALITY AS data_item(value, ordinality)
357
+ INNER JOIN jsonb_array_elements(schema->'tuple') WITH ORDINALITY AS tuple_item(value, ordinality)
358
+ ON data_item.ordinality = tuple_item.ordinality
359
+ LOOP
360
+ IF NOT ${VALIDATE_SCHEMA_FUNCNAME}(
361
+ array_element.schema_value::TEXT,
362
+ array_element.data_value,
363
+ context,
364
+ checked_path || array_element.idx::TEXT
365
+ ) THEN
366
+ RETURN FALSE;
367
+ END IF;
368
+ END LOOP;
369
+
370
+ RETURN TRUE;
371
+
333
372
  /* arrayOfType: { key_name: { type: "string" } } */
334
373
  ELSIF (schema ? 'arrayOf' OR schema ? 'arrayOfType') THEN
335
374
 
package/lib/Logging.ts CHANGED
@@ -164,7 +164,6 @@ export namespace EventTypes {
164
164
  type: "debug";
165
165
  command:
166
166
  | "applyTableConfig"
167
- | "initFileTable"
168
167
  | "initTableConfig"
169
168
  | "runSQLFile"
170
169
  | "schemaChangeNotif"
@@ -174,8 +173,6 @@ export namespace EventTypes {
174
173
  | "refreshDBO.end"
175
174
  | "tableConfigurator.init.start"
176
175
  | "tableConfigurator.init.end"
177
- | "initFileTable.start"
178
- | "initFileTable.end"
179
176
  | "initFileManager.runQuery"
180
177
  | "DboBuilder.getTablesForSchemaPostgresSQL"
181
178
  | "PubSubManager.create";
package/lib/Prostgles.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import type pgPromise from "pg-promise";
2
2
  import { AuthHandler } from "./Auth/AuthHandler";
3
- import { FileManager } from "./FileManager/FileManager";
4
3
  import type { OnInitReason } from "./initProstgles";
5
4
  import { initProstgles } from "./initProstgles";
6
5
  import type { SchemaWatch } from "./SchemaWatch/SchemaWatch";
@@ -10,7 +9,7 @@ import pg = require("pg-promise/typescript/pg-subset");
10
9
 
11
10
  import type { ProstglesInitOptions } from "./ProstglesTypes";
12
11
  import { RestApi } from "./RestApi";
13
- import TableConfigurator from "./TableConfig/TableConfig";
12
+ import { TableConfigurator } from "./TableConfig/TableConfigurator";
14
13
 
15
14
  import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
16
15
  import { DBHandlerServer, DboBuilder } from "./DboBuilder/DboBuilder";
@@ -59,8 +58,9 @@ const DEFAULT_KEYWORDS = {
59
58
 
60
59
  import { randomUUID } from "crypto";
61
60
  import * as fs from "fs";
62
- import type { TableHandler } from "./DboBuilder/TableHandler/TableHandler";
63
61
  import type { getAdminClient } from "./DboBuilder/runSql/getAdminClient";
62
+ import type { TableHandler } from "./DboBuilder/TableHandler/TableHandler";
63
+ import { getFileTableConfig } from "./FileManager/getFileTableConfig";
64
64
 
65
65
  export class Prostgles {
66
66
  /**
@@ -108,11 +108,14 @@ export class Prostgles {
108
108
  dbEventsManager?: DBEventsManager;
109
109
  schemaAge = "0";
110
110
 
111
- fileManager?: FileManager;
112
111
  restApi?: RestApi;
113
112
 
114
113
  tableConfigurator?: TableConfigurator;
115
114
 
115
+ get mergedTableConfig() {
116
+ return getFileTableConfig(this);
117
+ }
118
+
116
119
  isMedia(tableName: string) {
117
120
  return this.opts.fileTable?.tableName === tableName;
118
121
  }
@@ -147,6 +150,8 @@ export class Prostgles {
147
150
  onLog: 1,
148
151
  restApi: 1,
149
152
  testRulesOnConnect: 1,
153
+ modifyClientSchema: 1,
154
+ expressApp: 1,
150
155
  };
151
156
  const unknownParams = Object.keys(params).filter(
152
157
  (key: string) => !Object.keys(config).includes(key),
@@ -157,10 +162,6 @@ export class Prostgles {
157
162
 
158
163
  this.opts = { ...this.opts, ...params };
159
164
 
160
- /* set defaults */
161
- if (this.opts.fileTable) {
162
- this.opts.fileTable.tableName ??= "media";
163
- }
164
165
  this.opts.schemaFilter ??= { public: 1 };
165
166
 
166
167
  this.keywords = {
@@ -195,10 +196,8 @@ export class Prostgles {
195
196
  if (this.opts.tsGeneratedTypesDir) {
196
197
  const { fullPath, fileName } = this.getTSFileName();
197
198
  const { tsSchema: fileContent } = this.dboBuilder.getTsDefinitions();
198
- // eslint-disable-next-line security/detect-non-literal-fs-filename
199
199
  fs.readFile(fullPath, "utf8", function (err, data) {
200
200
  if (err || force || data !== fileContent) {
201
- // eslint-disable-next-line security/detect-non-literal-fs-filename
202
201
  fs.writeFileSync(fullPath, fileContent);
203
202
  console.log("Prostgles: Created typescript schema definition file: \n " + fileName);
204
203
  }
@@ -285,39 +284,6 @@ export class Prostgles {
285
284
  return res.data;
286
285
  };
287
286
 
288
- /* Create media table if required */
289
- initFileTable = async () => {
290
- const res = await tryCatchV2(async () => {
291
- if (this.opts.fileTable) {
292
- const { cloudClient, localConfig, imageOptions } = this.opts.fileTable;
293
- await this.refreshDBO();
294
- if (!cloudClient && !localConfig)
295
- throw "fileTable missing param: Must provide awsS3Config OR localConfig";
296
-
297
- this.fileManager = new FileManager(cloudClient || localConfig!, imageOptions);
298
-
299
- try {
300
- await this.fileManager.init(this);
301
- } catch (e) {
302
- console.error("FileManager: ", e);
303
- this.fileManager = undefined;
304
- }
305
- } else {
306
- this.fileManager?.destroy();
307
- this.fileManager = undefined;
308
- }
309
- await this.refreshDBO();
310
- return { data: {} };
311
- });
312
- await this.opts.onLog?.({
313
- type: "debug",
314
- command: "initFileTable",
315
- ...res,
316
- });
317
- if (res.error !== undefined) throw res.error;
318
- return res.data;
319
- };
320
-
321
287
  isSuperUser = false;
322
288
 
323
289
  init = initProstgles.bind(this);
@@ -365,7 +331,6 @@ export async function getIsSuperUser(db: DBorTx): Promise<boolean> {
365
331
 
366
332
  export const getFileText = (fullPath: string, _format = "utf8"): Promise<string> => {
367
333
  return new Promise((resolve, reject) => {
368
- // eslint-disable-next-line security/detect-non-literal-fs-filename
369
334
  fs.readFile(fullPath, "utf8", function (err, data) {
370
335
  if (err) reject(err);
371
336
  else resolve(data);
@@ -1,14 +1,18 @@
1
- import type { FileColumnConfig } from "prostgles-types";
2
- import type { AuthConfig, AuthRequestParams, SessionUser } from "./Auth/AuthTypes";
1
+ import type { DBSchemaTable, FileColumnConfig, MaybePromise } from "prostgles-types";
2
+ import type {
3
+ AuthConfig,
4
+ AuthRequestParams,
5
+ AuthResultWithSID,
6
+ SessionUser,
7
+ } from "./Auth/AuthTypes";
3
8
  import type { EventTriggerTagFilter } from "./Event_Trigger_Tags";
4
- import type { CloudClient, ImageOptions, LocalConfig } from "./FileManager/FileManager";
9
+ import type { StorageClient } from "./FileManager/FileManager";
5
10
  import type { DbConnection, OnReadyCallback } from "./initProstgles";
6
11
  import type { EventInfo } from "./Logging";
7
12
  import type { ExpressApp, RestApiConfig } from "./RestApi";
8
13
  import type { OnSchemaChangeCallback } from "./SchemaWatch/SchemaWatch";
9
14
  import type { PGConstraint } from "./TableConfig/fetchTableConstraints";
10
- import type { TableConfig } from "./TableConfig/TableConfig";
11
- import type { Express } from "express";
15
+ import type { TableConfig } from "./TableConfig/TableConfigTypes";
12
16
 
13
17
  import type { PRGLIOSocket } from "./DboBuilder/DboBuilder";
14
18
 
@@ -16,8 +20,8 @@ import type pgPromise from "pg-promise";
16
20
  import type pg from "pg-promise/typescript/pg-subset";
17
21
  import type { AnyObject } from "prostgles-types";
18
22
  import type { Server } from "socket.io";
19
- import type { Awaitable, Publish, PublishParams } from "./PublishParser/PublishParser";
20
23
  import type { ServerFunctionDefinitions } from "./PublishParser/defineServerFunction";
24
+ import type { Awaitable, Publish, PublishParams } from "./PublishParser/PublishParser";
21
25
 
22
26
  /**
23
27
  * Allows uploading and downloading files.
@@ -37,9 +41,8 @@ import type { ServerFunctionDefinitions } from "./PublishParser/defineServerFunc
37
41
  export type FileTableConfig = {
38
42
  /**
39
43
  * Name of the table that will contain the file metadata.
40
- * Defaults to "files"
41
44
  */
42
- tableName?: string;
45
+ tableName: string;
43
46
 
44
47
  /**
45
48
  * GET path used in serving media. defaults to /${tableName}
@@ -57,16 +60,11 @@ export type FileTableConfig = {
57
60
  */
58
61
  deleteAfterNDays: number;
59
62
  /**
60
- * How freuquently the files will be checked for deletion delay
63
+ * How frequently the files will be checked for deletion delay
61
64
  */
62
65
  checkIntervalHours?: number;
63
66
  };
64
67
 
65
- /**
66
- * Express server instance
67
- */
68
- expressApp: ExpressApp | Express;
69
-
70
68
  /**
71
69
  * Specifying referencedTables with referenceColumns allows restricting the
72
70
  * allowed file types that can be inserted and referenced in the specified tables.
@@ -78,18 +76,12 @@ export type FileTableConfig = {
78
76
  * */
79
77
  { type: "column"; referenceColumns: Record<string, FileColumnConfig> };
80
78
  };
81
- imageOptions?: ImageOptions;
82
79
 
83
80
  /**
84
81
  * Callbacks for file upload and download.
85
82
  * Used for custom file handling.
86
83
  */
87
- cloudClient?: CloudClient;
88
-
89
- /**
90
- * Local file storage configuration.
91
- */
92
- localConfig?: LocalConfig;
84
+ storageClient: StorageClient;
93
85
  };
94
86
 
95
87
  export const JOIN_TYPES = ["one-many", "many-one", "one-one", "many-many"] as const;
@@ -100,6 +92,11 @@ export type Join = {
100
92
  };
101
93
  type Joins = Join[] | "inferred";
102
94
 
95
+ type ModifyClientSchema<SUser extends SessionUser = SessionUser> = (
96
+ tables: DBSchemaTable,
97
+ userData: AuthResultWithSID<SUser> | undefined,
98
+ ) => MaybePromise<DBSchemaTable>;
99
+
103
100
  export type ProstglesInitOptions<S = void, SUser extends SessionUser = SessionUser> = {
104
101
  /**
105
102
  * Database connection details and options
@@ -129,6 +126,14 @@ export type ProstglesInitOptions<S = void, SUser extends SessionUser = SessionUs
129
126
  */
130
127
  io?: Server;
131
128
 
129
+ /**
130
+ * Express server instance used for:
131
+ * - Auth
132
+ * - REST API
133
+ * - Serving files through the file manager
134
+ */
135
+ expressApp?: ExpressApp;
136
+
132
137
  /**
133
138
  * Rest API configuration.
134
139
  * The REST API allows interacting with the database similarly to the websocket connection.
@@ -189,7 +194,7 @@ export type ProstglesInitOptions<S = void, SUser extends SessionUser = SessionUs
189
194
 
190
195
  /**
191
196
  * Allows defining table relationships that can then be used in filters and data inserts:
192
- * - `infered` - uses the foreign keys to infer the joins
197
+ * - `inferred` - uses the foreign keys to infer the joins
193
198
  * - `Join[]` - specifies the joins manually
194
199
  */
195
200
  joins?: Joins;
@@ -307,9 +312,15 @@ export type ProstglesInitOptions<S = void, SUser extends SessionUser = SessionUs
307
312
  tableConfigMigrations?: TableConfigMigrations;
308
313
 
309
314
  /**
310
- * Usefull for logging or debugging
315
+ * Useful for logging or debugging
311
316
  */
312
317
  onLog?: (evt: EventInfo) => Promise<void> | void;
318
+
319
+ /**
320
+ * Used to add custom metadata to the table and column schemas.
321
+ * This metadata is then available in the client schema and can be used for custom logic on the client side.
322
+ */
323
+ modifyClientSchema?: ModifyClientSchema<SUser>;
313
324
  };
314
325
 
315
326
  export type TableConfigMigrations = {
@@ -15,8 +15,6 @@ export async function notifListener(this: PubSubManager, data: { payload: string
15
15
  const dataArr = str.split(DELIMITER);
16
16
  const notifType = dataArr[0] as NotifTypeName;
17
17
 
18
- log(str);
19
-
20
18
  const commonLog = {
21
19
  triggers: this._triggers,
22
20
  sid: undefined,
@@ -1,5 +1,5 @@
1
1
  import { getObjectEntries, includes, type DBSchemaTable } from "prostgles-types";
2
- import type { AuthClientRequest } from "../Auth/AuthTypes";
2
+ import type { AuthClientRequest, AuthResultWithSID } from "../Auth/AuthTypes";
3
3
  import { getErrorAsObject } from "../DboBuilder/dboBuilderUtils";
4
4
  import type { TableHandler } from "../DboBuilder/TableHandler/TableHandler";
5
5
  import { TABLE_METHODS } from "../Prostgles";
@@ -11,6 +11,7 @@ export const getDBSchemaTable = async (
11
11
  tableHandler: TableHandler,
12
12
  parsedTableRule: ParsedTableRule,
13
13
  clientReq: AuthClientRequest,
14
+ clientInfo: AuthResultWithSID | undefined,
14
15
  scope: PermissionScope | undefined,
15
16
  ): Promise<DBSchemaTable> => {
16
17
  const tableName = tableHandler.name;
@@ -20,11 +21,13 @@ export const getDBSchemaTable = async (
20
21
  }
21
22
  const info = await tableHandler.getInfo(undefined, undefined, undefined, parsedTableRule, {
22
23
  ...clientReq,
23
- isRemoteRequest: {},
24
+ isRemoteRequest: {
25
+ clientInfo,
26
+ },
24
27
  });
25
28
  const columns = await tableHandler.getColumns(undefined, undefined, undefined, parsedTableRule, {
26
29
  ...clientReq,
27
- isRemoteRequest: {},
30
+ isRemoteRequest: { clientInfo },
28
31
  });
29
32
  const allowedCommands = getAllowedTableMethods(info);
30
33
  for (const method of allowedCommands) {
@@ -45,7 +45,7 @@ export async function getSchemaFromPublish(
45
45
  * Add file table to the list of published tables if it's referenced by other published tables.
46
46
  * Access to the file table is controlled through the publish rules of the tables referencing it.
47
47
  */
48
- const fileTableName = this.prostgles.fileManager?.tableName;
48
+ const fileTableName = this.prostgles.opts.fileTable?.tableName;
49
49
  if (fileTableName && this.dbo[fileTableName]?.is_media && !tableNames.includes(fileTableName)) {
50
50
  const isReferenced = this.prostgles.dboBuilder.tablesOrViews?.some((t) =>
51
51
  t.columns.some((c) => c.references?.some((r) => r.ftable === fileTableName)),
@@ -83,6 +83,7 @@ export async function getSchemaFromPublish(
83
83
  tableHandler,
84
84
  parsedTableRule,
85
85
  clientReq,
86
+ clientInfo,
86
87
  scope,
87
88
  );
88
89
  tables.push(tableSchema);