prostgles-server 4.2.567 → 4.2.570

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 (224) 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/Auth/utils/removeExpressRoute.d.ts.map +1 -1
  8. package/dist/DBSchemaBuilder/getColumnTypescriptDefinition.d.ts +1 -1
  9. package/dist/DBSchemaBuilder/getColumnTypescriptDefinition.d.ts.map +1 -1
  10. package/dist/DBSchemaBuilder/getDBGeneratedSchema.d.ts +1 -1
  11. package/dist/DBSchemaBuilder/getDBGeneratedSchema.d.ts.map +1 -1
  12. package/dist/DboBuilder/DboBuilder.js +4 -4
  13. package/dist/DboBuilder/DboBuilder.js.map +1 -1
  14. package/dist/DboBuilder/DboBuilderTypes.d.ts +2 -18
  15. package/dist/DboBuilder/DboBuilderTypes.d.ts.map +1 -1
  16. package/dist/DboBuilder/DboBuilderTypes.js.map +1 -1
  17. package/dist/DboBuilder/TableHandler/TableHandler.d.ts +15 -5
  18. package/dist/DboBuilder/TableHandler/TableHandler.d.ts.map +1 -1
  19. package/dist/DboBuilder/TableHandler/TableHandler.js +4 -3
  20. package/dist/DboBuilder/TableHandler/TableHandler.js.map +1 -1
  21. package/dist/DboBuilder/TableHandler/delete.d.ts.map +1 -1
  22. package/dist/DboBuilder/TableHandler/delete.js +17 -14
  23. package/dist/DboBuilder/TableHandler/delete.js.map +1 -1
  24. package/dist/DboBuilder/TableHandler/insert/insert.d.ts.map +1 -1
  25. package/dist/DboBuilder/TableHandler/insert/insert.js +3 -12
  26. package/dist/DboBuilder/TableHandler/insert/insert.js.map +1 -1
  27. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.d.ts.map +1 -1
  28. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.js +10 -15
  29. package/dist/DboBuilder/TableHandler/insert/insertNestedRecords.js.map +1 -1
  30. package/dist/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.js +1 -1
  31. package/dist/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.js.map +1 -1
  32. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts +8 -6
  33. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.d.ts.map +1 -1
  34. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js +30 -48
  35. package/dist/DboBuilder/TableHandler/onDeleteFromFileTable.js.map +1 -1
  36. package/dist/DboBuilder/TableHandler/update.d.ts +1 -1
  37. package/dist/DboBuilder/TableHandler/update.d.ts.map +1 -1
  38. package/dist/DboBuilder/TableHandler/update.js +8 -18
  39. package/dist/DboBuilder/TableHandler/update.js.map +1 -1
  40. package/dist/DboBuilder/TableHandler/updateFile.d.ts +2 -3
  41. package/dist/DboBuilder/TableHandler/updateFile.d.ts.map +1 -1
  42. package/dist/DboBuilder/TableHandler/updateFile.js +8 -28
  43. package/dist/DboBuilder/TableHandler/updateFile.js.map +1 -1
  44. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts +2 -2
  45. package/dist/DboBuilder/ViewHandler/ViewHandler.d.ts.map +1 -1
  46. package/dist/DboBuilder/ViewHandler/getInfo.d.ts +1 -1
  47. package/dist/DboBuilder/ViewHandler/getInfo.d.ts.map +1 -1
  48. package/dist/DboBuilder/ViewHandler/getInfo.js +12 -1
  49. package/dist/DboBuilder/ViewHandler/getInfo.js.map +1 -1
  50. package/dist/DboBuilder/getColumns.d.ts.map +1 -1
  51. package/dist/DboBuilder/getColumns.js +29 -4
  52. package/dist/DboBuilder/getColumns.js.map +1 -1
  53. package/dist/DboBuilder/schema/getTablesForSchemaPostgresSQL.d.ts +2 -2
  54. package/dist/DboBuilder/uploadFile.d.ts +5 -6
  55. package/dist/DboBuilder/uploadFile.d.ts.map +1 -1
  56. package/dist/DboBuilder/uploadFile.js +28 -42
  57. package/dist/DboBuilder/uploadFile.js.map +1 -1
  58. package/dist/FileManager/FileManager.d.ts +22 -78
  59. package/dist/FileManager/FileManager.d.ts.map +1 -1
  60. package/dist/FileManager/FileManager.js +1 -219
  61. package/dist/FileManager/FileManager.js.map +1 -1
  62. package/dist/FileManager/getFileTableConfig.d.ts +26 -0
  63. package/dist/FileManager/getFileTableConfig.d.ts.map +1 -0
  64. package/dist/FileManager/getFileTableConfig.js +113 -0
  65. package/dist/FileManager/getFileTableConfig.js.map +1 -0
  66. package/dist/FileManager/getLocalStorageClient.d.ts +3 -0
  67. package/dist/FileManager/getLocalStorageClient.d.ts.map +1 -0
  68. package/dist/FileManager/getLocalStorageClient.js +105 -0
  69. package/dist/FileManager/getLocalStorageClient.js.map +1 -0
  70. package/dist/FileManager/getValidatedFileType.d.ts +2 -2
  71. package/dist/FileManager/getValidatedFileType.d.ts.map +1 -1
  72. package/dist/FileManager/getValidatedFileType.js +7 -9
  73. package/dist/FileManager/getValidatedFileType.js.map +1 -1
  74. package/dist/FileManager/setupFileServeHandler.d.ts +9 -0
  75. package/dist/FileManager/setupFileServeHandler.d.ts.map +1 -0
  76. package/dist/FileManager/setupFileServeHandler.js +114 -0
  77. package/dist/FileManager/setupFileServeHandler.js.map +1 -0
  78. package/dist/Filtering/Filtering.d.ts +1 -1
  79. package/dist/Logging.d.ts +1 -1
  80. package/dist/Logging.d.ts.map +1 -1
  81. package/dist/Prostgles.d.ts +52 -6
  82. package/dist/Prostgles.d.ts.map +1 -1
  83. package/dist/Prostgles.js +7 -47
  84. package/dist/Prostgles.js.map +1 -1
  85. package/dist/ProstglesTypes.d.ts +19 -22
  86. package/dist/ProstglesTypes.d.ts.map +1 -1
  87. package/dist/ProstglesTypes.js.map +1 -1
  88. package/dist/PubSubManager/notifListener.d.ts.map +1 -1
  89. package/dist/PubSubManager/notifListener.js +0 -1
  90. package/dist/PubSubManager/notifListener.js.map +1 -1
  91. package/dist/PublishParser/getDBSchemaTable.d.ts +2 -2
  92. package/dist/PublishParser/getDBSchemaTable.d.ts.map +1 -1
  93. package/dist/PublishParser/getDBSchemaTable.js +5 -3
  94. package/dist/PublishParser/getDBSchemaTable.js.map +1 -1
  95. package/dist/PublishParser/getSchemaFromPublish.d.ts.map +1 -1
  96. package/dist/PublishParser/getSchemaFromPublish.js +2 -2
  97. package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
  98. package/dist/PublishParser/publishTypesAndUtils.d.ts +1 -0
  99. package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
  100. package/dist/PublishParser/publishTypesAndUtils.js.map +1 -1
  101. package/dist/RestApi.d.ts +4 -6
  102. package/dist/RestApi.d.ts.map +1 -1
  103. package/dist/RestApi.js +6 -5
  104. package/dist/RestApi.js.map +1 -1
  105. package/dist/TableConfig/{TableConfig.d.ts → TableConfigTypes.d.ts} +19 -64
  106. package/dist/TableConfig/TableConfigTypes.d.ts.map +1 -0
  107. package/dist/TableConfig/TableConfigTypes.js +3 -0
  108. package/dist/TableConfig/TableConfigTypes.js.map +1 -0
  109. package/dist/TableConfig/TableConfigurator.d.ts +104 -0
  110. package/dist/TableConfig/TableConfigurator.d.ts.map +1 -0
  111. package/dist/TableConfig/{TableConfig.js → TableConfigurator.js} +31 -44
  112. package/dist/TableConfig/TableConfigurator.js.map +1 -0
  113. package/dist/TableConfig/applyTableConfig.d.ts.map +1 -1
  114. package/dist/TableConfig/applyTableConfig.js +0 -1
  115. package/dist/TableConfig/applyTableConfig.js.map +1 -1
  116. package/dist/TableConfig/getColumnConfig.d.ts +1 -1
  117. package/dist/TableConfig/getColumnConfig.d.ts.map +1 -1
  118. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts +1 -1
  119. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts.map +1 -1
  120. package/dist/TableConfig/getColumnSQLDefinitionQuery.js +1 -1
  121. package/dist/TableConfig/getColumnSQLDefinitionQuery.js.map +1 -1
  122. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts +1 -1
  123. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts.map +1 -1
  124. package/dist/TableConfig/getCreateSchemaQueries.d.ts +26 -8
  125. package/dist/TableConfig/getCreateSchemaQueries.d.ts.map +1 -1
  126. package/dist/TableConfig/getSchemaDiffQueries.d.ts.map +1 -1
  127. package/dist/TableConfig/getSchemaDiffQueries.js +12 -8
  128. package/dist/TableConfig/getSchemaDiffQueries.js.map +1 -1
  129. package/dist/TableConfig/getTableColumnQueries.d.ts +1 -1
  130. package/dist/TableConfig/getTableColumnQueries.d.ts.map +1 -1
  131. package/dist/TableConfig/getTableColumnQueries.js +19 -19
  132. package/dist/TableConfig/getTableColumnQueries.js.map +1 -1
  133. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts +26 -10
  134. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts.map +1 -1
  135. package/dist/TableConfig/getTableConfigSchemaQueries.js +8 -10
  136. package/dist/TableConfig/getTableConfigSchemaQueries.js.map +1 -1
  137. package/dist/TableConfig/indexes/getIndexesQueries.d.ts +1 -1
  138. package/dist/TableConfig/indexes/getIndexesQueries.d.ts.map +1 -1
  139. package/dist/TableConfig/initTableConfig.d.ts +1 -1
  140. package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
  141. package/dist/TableConfig/initTableConfig.js +6 -3
  142. package/dist/TableConfig/initTableConfig.js.map +1 -1
  143. package/dist/WebsocketAPI/getClientSchema.d.ts.map +1 -1
  144. package/dist/WebsocketAPI/getClientSchema.js +6 -1
  145. package/dist/WebsocketAPI/getClientSchema.js.map +1 -1
  146. package/dist/index.d.ts +3 -2
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +1 -0
  149. package/dist/index.js.map +1 -1
  150. package/dist/initProstgles.d.ts.map +1 -1
  151. package/dist/initProstgles.js +1 -2
  152. package/dist/initProstgles.js.map +1 -1
  153. package/dist/runClientRequest.js +1 -1
  154. package/dist/runClientRequest.js.map +1 -1
  155. package/dist/updateConfiguration.d.ts.map +1 -1
  156. package/dist/updateConfiguration.js +0 -3
  157. package/dist/updateConfiguration.js.map +1 -1
  158. package/eslint.config.mjs +0 -3
  159. package/lib/Auth/AuthTypes.ts +6 -7
  160. package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +6 -2
  161. package/lib/DBSchemaBuilder/getColumnTypescriptDefinition.ts +1 -1
  162. package/lib/DBSchemaBuilder/getDBGeneratedSchema.ts +1 -1
  163. package/lib/DboBuilder/DboBuilder.ts +4 -4
  164. package/lib/DboBuilder/DboBuilderTypes.ts +7 -20
  165. package/lib/DboBuilder/TableHandler/TableHandler.ts +7 -4
  166. package/lib/DboBuilder/TableHandler/delete.ts +23 -17
  167. package/lib/DboBuilder/TableHandler/insert/insert.ts +3 -12
  168. package/lib/DboBuilder/TableHandler/insert/insertNestedRecords.ts +13 -17
  169. package/lib/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.ts +1 -1
  170. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +41 -54
  171. package/lib/DboBuilder/TableHandler/update.ts +8 -19
  172. package/lib/DboBuilder/TableHandler/updateFile.ts +20 -42
  173. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +1 -1
  174. package/lib/DboBuilder/ViewHandler/getInfo.ts +19 -2
  175. package/lib/DboBuilder/getColumns.ts +40 -4
  176. package/lib/DboBuilder/uploadFile.ts +34 -50
  177. package/lib/FileManager/FileManager.ts +23 -249
  178. package/lib/FileManager/getFileTableConfig.ts +140 -0
  179. package/lib/FileManager/getLocalStorageClient.ts +118 -0
  180. package/lib/FileManager/getValidatedFileType.ts +14 -22
  181. package/lib/FileManager/setupFileServeHandler.ts +102 -0
  182. package/lib/Logging.ts +0 -3
  183. package/lib/Prostgles.ts +8 -44
  184. package/lib/ProstglesTypes.ts +30 -24
  185. package/lib/PubSubManager/notifListener.ts +0 -2
  186. package/lib/PublishParser/getDBSchemaTable.ts +6 -3
  187. package/lib/PublishParser/getSchemaFromPublish.ts +2 -1
  188. package/lib/PublishParser/publishTypesAndUtils.ts +1 -0
  189. package/lib/RestApi.ts +12 -12
  190. package/lib/TableConfig/{TableConfig.ts → TableConfigTypes.ts} +18 -217
  191. package/lib/TableConfig/TableConfigurator.ts +191 -0
  192. package/lib/TableConfig/applyTableConfig.ts +2 -3
  193. package/lib/TableConfig/getColumnConfig.ts +2 -2
  194. package/lib/TableConfig/getColumnSQLDefinitionQuery.ts +2 -2
  195. package/lib/TableConfig/getConstraintDefinitionQueries.ts +1 -1
  196. package/lib/TableConfig/getCreateSchemaQueries.ts +1 -1
  197. package/lib/TableConfig/getSchemaDiffQueries.ts +14 -11
  198. package/lib/TableConfig/getTableColumnQueries.ts +22 -20
  199. package/lib/TableConfig/getTableConfigSchemaQueries.ts +20 -22
  200. package/lib/TableConfig/indexes/getIndexesQueries.ts +1 -1
  201. package/lib/TableConfig/initTableConfig.ts +7 -6
  202. package/lib/WebsocketAPI/getClientSchema.ts +7 -1
  203. package/lib/index.ts +3 -2
  204. package/lib/initProstgles.ts +1 -2
  205. package/lib/runClientRequest.ts +1 -1
  206. package/lib/updateConfiguration.ts +0 -3
  207. package/package.json +2 -3
  208. package/dist/FileManager/initFileManager.d.ts +0 -18
  209. package/dist/FileManager/initFileManager.d.ts.map +0 -1
  210. package/dist/FileManager/initFileManager.js +0 -257
  211. package/dist/FileManager/initFileManager.js.map +0 -1
  212. package/dist/FileManager/upload.d.ts +0 -4
  213. package/dist/FileManager/upload.d.ts.map +0 -1
  214. package/dist/FileManager/upload.js +0 -90
  215. package/dist/FileManager/upload.js.map +0 -1
  216. package/dist/FileManager/uploadStream.d.ts +0 -4
  217. package/dist/FileManager/uploadStream.d.ts.map +0 -1
  218. package/dist/FileManager/uploadStream.js +0 -100
  219. package/dist/FileManager/uploadStream.js.map +0 -1
  220. package/dist/TableConfig/TableConfig.d.ts.map +0 -1
  221. package/dist/TableConfig/TableConfig.js.map +0 -1
  222. package/lib/FileManager/initFileManager.ts +0 -254
  223. package/lib/FileManager/upload.ts +0 -59
  224. package/lib/FileManager/uploadStream.ts +0 -76
@@ -1,40 +1,27 @@
1
- import * as fs from "fs";
2
1
  import type * as stream from "stream";
3
2
 
4
3
  // import * as sharp from "sharp";
5
- import checkDiskSpace from "check-disk-space";
6
4
 
7
- import type { ALLOWED_CONTENT_TYPE, ALLOWED_EXTENSION, ValidatedColumnInfo } from "prostgles-types";
5
+ import type { ALLOWED_CONTENT_TYPE, ALLOWED_EXTENSION } from "prostgles-types";
8
6
  import { CONTENT_TYPE_TO_EXT, getKeys } from "prostgles-types";
9
- import type { DB, DBHandlerServer, Prostgles } from "../Prostgles";
10
7
 
11
- import * as path from "path";
12
- import { removeExpressRoute } from "../Auth/utils/removeExpressRoute";
13
- import type { ExpressApp } from "../RestApi";
14
- import { getValidatedFileType } from "./getValidatedFileType";
15
- import { initFileManager } from "./initFileManager";
16
- import { upload } from "./upload";
17
- import { uploadStream } from "./uploadStream";
18
-
19
- export const HOUR = 3600 * 1000;
8
+ export type LocalConfig = {
9
+ /**
10
+ * example: path.join(__dirname+'/media')
11
+ * note that this location will be relative to the compiled file location
12
+ */
13
+ localFolderPath: string;
20
14
 
21
- export const asSQLIdentifier = async (name: string, db: DB): Promise<string> => {
22
- return (await db.one<{ name: string }>("select format('%I', $1) as name", [name])).name;
15
+ /**
16
+ * Minimum amount of free bytes available to allow saving files
17
+ * Defaults to 100MB
18
+ */
19
+ minFreeBytes?: number;
23
20
  };
24
21
 
25
- export type OnProgress = (progress: { total: number; loaded: number }) => void;
26
-
27
- type ImageCompressionOptions =
28
- | { inside: { width: number; height: number } }
29
- | { contain: { width: number } | { height: number } };
22
+ export const HOUR = 3600 * 1000;
30
23
 
31
- /**
32
- * Deprecated
33
- */
34
- export type ImageOptions = {
35
- keepMetadata?: boolean;
36
- compression?: ImageCompressionOptions;
37
- };
24
+ export type OnProgress = (progress: { total: number; loaded: number }) => void;
38
25
 
39
26
  export type UploadedCloudFile = {
40
27
  cloud_url: string;
@@ -45,31 +32,22 @@ export type FileUploadArgs = {
45
32
  fileName: string;
46
33
  contentType: string;
47
34
  file: string | Buffer | stream.PassThrough;
48
- onFinish: (
49
- ...args: [error: Error, result: undefined] | [error: undefined, result: UploadedCloudFile]
50
- ) => void;
51
35
  onProgress?: (bytesUploaded: number) => void;
52
36
  };
53
- export type CloudClient = {
54
- upload: (file: FileUploadArgs) => Promise<void>;
37
+ export type StorageClientBase = {
38
+ upload: (file: FileUploadArgs) => Promise<UploadedCloudFile>;
55
39
  downloadAsStream: (name: string) => Promise<stream.Readable>;
56
40
  delete: (fileName: string) => Promise<void>;
57
- getSignedUrlForDownload: (fileName: string, expiresInSeconds: number) => Promise<string>;
58
41
  };
59
-
60
- export type LocalConfig = {
61
- /**
62
- * example: path.join(__dirname+'/media')
63
- * note that this location will be relative to the compiled file location
64
- */
42
+ export type LocalStorageClient = StorageClientBase & {
43
+ type: "local";
65
44
  localFolderPath: string;
66
-
67
- /**
68
- * Minimum amount of free bytes available to allow saving files
69
- * Defaults to 100MB
70
- */
71
- minFreeBytes?: number;
72
45
  };
46
+ export type CloudStorageClient = StorageClientBase & {
47
+ type: "cloud";
48
+ getSignedUrlForDownload: (fileName: string, expiresInSeconds: number) => Promise<string>;
49
+ };
50
+ export type StorageClient = LocalStorageClient | CloudStorageClient;
73
51
 
74
52
  export type UploadItem = {
75
53
  name: string;
@@ -96,210 +74,6 @@ export type UploadedItem = {
96
74
  content_length: number;
97
75
  };
98
76
 
99
- export class FileManager {
100
- cloudClient?: CloudClient;
101
-
102
- config: CloudClient | LocalConfig;
103
- imageOptions?: ImageOptions;
104
-
105
- prostgles?: Prostgles;
106
- get dbo(): DBHandlerServer {
107
- if (!this.prostgles?.dbo) {
108
- throw "this.prostgles.dbo missing";
109
- }
110
- return this.prostgles.dbo;
111
- }
112
-
113
- get db(): DB {
114
- if (!this.prostgles?.db) throw "this.prostgles.db missing";
115
- return this.prostgles.db;
116
- }
117
-
118
- tableName?: string;
119
-
120
- path?: string;
121
- get fileRouteExpress() {
122
- return this.path + "/:name";
123
- }
124
-
125
- constructor(config: FileManager["config"], imageOptions?: ImageOptions) {
126
- this.config = config;
127
- this.imageOptions = imageOptions;
128
-
129
- if ("upload" in config) {
130
- this.cloudClient = config;
131
- }
132
-
133
- const fullConfig = this.prostgles?.opts.fileTable;
134
- if (fullConfig?.delayedDelete) {
135
- setInterval(
136
- () => {
137
- void (async () => {
138
- const fileTable = fullConfig.tableName;
139
- const daysDelay = fullConfig.delayedDelete?.deleteAfterNDays ?? 0;
140
- if (fileTable && this.dbo[fileTable]?.delete && daysDelay) {
141
- const filesToDelete = await this.dbo[fileTable].find({
142
- deleted_from_storage: null,
143
- deleted: { ">": Date.now() - daysDelay * HOUR * 24 },
144
- });
145
- for (const file of filesToDelete) {
146
- await this.deleteFile(file.name);
147
- }
148
- } else {
149
- console.error(
150
- "FileManager checkInterval delayedDelete FAIL: Could not access file table tableHandler.delete()",
151
- );
152
- }
153
- })();
154
- },
155
- Math.max(10000, (fullConfig.delayedDelete.checkIntervalHours || 0) * HOUR),
156
- );
157
- }
158
- }
159
-
160
- async getFileStream(name: string): Promise<stream.Readable> {
161
- if (this.cloudClient) {
162
- return this.cloudClient.downloadAsStream(name);
163
- } else if ("localFolderPath" in this.config) {
164
- const filePath = path.resolve(`${this.config.localFolderPath}/${name}`);
165
- if (!fs.existsSync(filePath)) {
166
- throw `File ${filePath} could not be found`;
167
- }
168
- return fs.createReadStream(filePath, { encoding: undefined });
169
- } else throw new Error("Not expected");
170
- }
171
-
172
- async deleteFile(name: string) {
173
- if (this.cloudClient) {
174
- const res = await this.cloudClient.delete(name);
175
- return res;
176
- } else if ("localFolderPath" in this.config) {
177
- const path = `${this.config.localFolderPath}/${name}`;
178
- if (!fs.existsSync(path)) {
179
- throw `File ${path} could not be found`;
180
- }
181
- fs.unlinkSync(path);
182
- if (fs.existsSync(path)) throw new Error("Could not delete file");
183
- }
184
- return true;
185
- }
186
-
187
- getValidatedFileType = getValidatedFileType.bind(this);
188
-
189
- getLocalFileUrl = (name: string) => (this.path ? `${this.path}/${name}` : "");
190
-
191
- checkFreeSpace = async (folderPath: string, fileSize = 0) => {
192
- if (!this.cloudClient && "localFolderPath" in this.config) {
193
- const { minFreeBytes = 1.048e6 } = this.config;
194
- const required = Math.max(fileSize, minFreeBytes);
195
- if (required) {
196
- const diskSpace = await checkDiskSpace(folderPath);
197
- if (diskSpace.free < required) {
198
- const err = `There is not enough space on the server to save files.\nTotal: ${bytesToSize(diskSpace.size)} \nRemaning: ${bytesToSize(diskSpace.free)} \nRequired: ${bytesToSize(required)}`;
199
- throw new Error(err);
200
- }
201
- }
202
- }
203
- };
204
-
205
- uploadStream = uploadStream.bind(this);
206
-
207
- upload = upload.bind(this);
208
-
209
- uploadAsMedia = async (params: {
210
- item: UploadItem;
211
- allowedExtensions?: Array<ALLOWED_EXTENSION>;
212
- dissallowedExtensions?: Array<ALLOWED_EXTENSION>;
213
- imageOptions?: ImageOptions;
214
- }): Promise<UploadedItem> => {
215
- const { item } = params;
216
- const { name, data, content_type } = item;
217
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
218
- if (!data) throw "No file provided";
219
- if (!name || typeof name !== "string") throw "Expecting a string name";
220
-
221
- // const type = await this.getMIME(data, name, allowedExtensions, dissallowedExtensions);
222
-
223
- const _data = data;
224
-
225
- /** Resize/compress/remove exif from photos */
226
- // if(content_type.startsWith("image") && extension.toLowerCase() !== "gif"){
227
-
228
- // const compression = imageOptions?.compression
229
- // if(compression){
230
- // console.log("Resizing image")
231
- // let opts;
232
- // if("contain" in compression){
233
- // opts = {
234
- // fit: sharp.fit.contain,
235
- // ...compression.contain
236
- // }
237
- // } else if("inside" in compression){
238
- // opts = {
239
- // fit: sharp.fit.inside,
240
- // ...compression.inside
241
- // }
242
- // }
243
- // _data = await sharp(data)
244
- // .resize(opts as any)
245
- // .withMetadata(Boolean(imageOptions?.keepMetadata) as any)
246
- // // .jpeg({ quality: 80 })
247
- // .toBuffer()
248
- // } else if(!imageOptions?.keepMetadata) {
249
- // /**
250
- // * Remove exif
251
- // */
252
- // // const metadata = await simg.metadata();
253
- // // const simg = await sharp(data);
254
-
255
- // _data = await sharp(data).clone().withMetadata({
256
- // exif: {}
257
- // })
258
- // .toBuffer()
259
- // }
260
- // }
261
-
262
- const res = await this.upload(_data, name, content_type);
263
-
264
- return res;
265
- };
266
-
267
- async getFileCloudDownloadURL(fileName: string, expiresInSecondsRaw = 30 * 60) {
268
- const expiresInSeconds = Math.max(1, Math.round(expiresInSecondsRaw));
269
- return await this.cloudClient!.getSignedUrlForDownload(fileName, expiresInSeconds);
270
- }
271
-
272
- parseSQLIdentifier = async (name: string) => asSQLIdentifier(name, this.prostgles!.db!); // this.prostgles.dbo.sql<"value">("select format('%I', $1)", [name], { returnType: "value" } )
273
-
274
- getColInfo = (args: {
275
- tableName: string;
276
- colName: string;
277
- }): ValidatedColumnInfo["file"] | undefined => {
278
- const { colName, tableName } = args;
279
- const tableConfig = this.prostgles?.opts.fileTable?.referencedTables?.[tableName];
280
- const isReferencingFileTable = this.dbo[tableName]?.columns.some(
281
- (c) =>
282
- c.name === colName &&
283
- c.references &&
284
- c.references.some(({ ftable }) => ftable === this.tableName),
285
- );
286
- const allowAllFiles = { acceptedContent: "*" } as const;
287
- if (isReferencingFileTable) {
288
- if (tableConfig && typeof tableConfig !== "string") {
289
- return tableConfig.referenceColumns[colName] ?? allowAllFiles;
290
- }
291
- return allowAllFiles;
292
- }
293
- return undefined;
294
- };
295
-
296
- init = initFileManager.bind(this);
297
-
298
- destroy = () => {
299
- removeExpressRoute(this.prostgles?.opts.fileTable?.expressApp, [this.fileRouteExpress]);
300
- };
301
- }
302
-
303
77
  export const getFileTypeFromFilename = (
304
78
  fileName: string,
305
79
  ): { mime: ALLOWED_CONTENT_TYPE; ext: ALLOWED_EXTENSION } | undefined => {
@@ -0,0 +1,140 @@
1
+ import { omitKeys } from "prostgles-types";
2
+ import { updateFile } from "../DboBuilder/TableHandler/updateFile";
3
+ import { uploadFile } from "../DboBuilder/uploadFile";
4
+ import type { Prostgles } from "../Prostgles";
5
+ import type { TableConfig } from "../TableConfig/TableConfigTypes";
6
+ import { setupFileServeHandler } from "./setupFileServeHandler";
7
+ import { onDeleteFromFileTable } from "../DboBuilder/TableHandler/onDeleteFromFileTable";
8
+ import type { BeforeEachTsTrigger } from "../PublishParser/publishTypesAndUtils";
9
+
10
+ const FILE_TABLE_COLUMN_DEFINITIONS = {
11
+ name: `TEXT NOT NULL UNIQUE`,
12
+ extension: `TEXT NOT NULL`,
13
+ content_type: `TEXT NOT NULL`,
14
+ content_length: `BIGINT NOT NULL DEFAULT 0`,
15
+ added: `TIMESTAMP NOT NULL DEFAULT NOW()`,
16
+ url: `TEXT NOT NULL`,
17
+ id: `UUID PRIMARY KEY DEFAULT gen_random_uuid()`,
18
+ original_name: `TEXT NOT NULL`,
19
+ description: `TEXT`,
20
+ cloud_url: `TEXT`,
21
+ signed_url: `TEXT`,
22
+ signed_url_expires: `BIGINT`,
23
+ etag: `TEXT`,
24
+ deleted: `BIGINT`,
25
+ deleted_from_storage: `BIGINT`,
26
+ } as const;
27
+
28
+ type FileTableColumnDefinitions = typeof FILE_TABLE_COLUMN_DEFINITIONS;
29
+
30
+ export type FileTableRow = {
31
+ [K in keyof FileTableColumnDefinitions as K]: FileTableColumnDefinitions[K] extends (
32
+ `${string} NOT NULL${string}` | `${string}PRIMARY KEY${string}`
33
+ ) ?
34
+ string
35
+ : string | null;
36
+ };
37
+
38
+ export const getFileTableConfig = (prg: Prostgles): TableConfig | undefined => {
39
+ const { fileTable, tableConfig } = prg.opts;
40
+ if (!fileTable) {
41
+ return tableConfig;
42
+ }
43
+
44
+ const { expressApp } = fileTable;
45
+
46
+ const { tableName: fileTableName, storageClient } = fileTable;
47
+
48
+ const userFileTableConfig = tableConfig?.[fileTableName];
49
+ if (userFileTableConfig) {
50
+ if ("isLookupTable" in userFileTableConfig) {
51
+ throw new Error(
52
+ `FileManager table name (${fileTableName}) cannot have isLookupTable set in tableConfig`,
53
+ );
54
+ }
55
+ if (userFileTableConfig.dropIfExists || userFileTableConfig.dropIfExistsCascade) {
56
+ throw new Error(
57
+ `FileManager table name (${fileTableName}) cannot have dropIfExists or dropIfExistsCascade set in tableConfig`,
58
+ );
59
+ }
60
+ if ("columns" in userFileTableConfig && userFileTableConfig.columns) {
61
+ const userCols = new Set(Object.keys(userFileTableConfig.columns));
62
+ const clashingColumns = Object.keys(FILE_TABLE_COLUMN_DEFINITIONS).filter((col) =>
63
+ userCols.has(col),
64
+ );
65
+ if (clashingColumns.length) {
66
+ throw new Error(
67
+ `FileManager table name (${fileTableName}) has clashing column names in tableConfig: ${clashingColumns}`,
68
+ );
69
+ }
70
+ }
71
+ }
72
+
73
+ return {
74
+ [fileTableName]: {
75
+ ...userFileTableConfig,
76
+ columns: {
77
+ ...FILE_TABLE_COLUMN_DEFINITIONS,
78
+ ...userFileTableConfig?.columns,
79
+ },
80
+ onMount: ({ _db }) => {
81
+ const { destroy } = setupFileServeHandler(_db, fileTable, storageClient, expressApp, prg);
82
+
83
+ const maxBfSizeMB = (prg.opts.io?.engine.opts.maxHttpBufferSize || 1e6) / 1e6;
84
+ console.log(
85
+ `Prostgles: Initiated file manager. Max allowed file size: ${maxBfSizeMB}MB (maxHttpBufferSize = 1e6). To increase this set maxHttpBufferSize in socket.io server init options`,
86
+ );
87
+ return {
88
+ onUnmount: destroy,
89
+ };
90
+ },
91
+ hooks: {
92
+ ...userFileTableConfig?.hooks,
93
+ beforeEach: [
94
+ {
95
+ commands: {
96
+ insert: 1,
97
+ update: 1,
98
+ },
99
+ validate: async ({ data, localParams, command, filter }) => {
100
+ const tableHandler = prg.dboBuilder.dboMap.get(fileTableName);
101
+ if (!tableHandler) throw "FileManager tableHandler not found";
102
+
103
+ if (command === "update") {
104
+ const { newData } = await updateFile(tableHandler, fileTable, {
105
+ filter: filter ?? {},
106
+ localParams,
107
+ newData: data,
108
+ });
109
+ return {
110
+ row: newData,
111
+ };
112
+ }
113
+
114
+ const media = await uploadFile(fileTable, {
115
+ row: data,
116
+ localParams,
117
+ mediaId: data.id,
118
+ });
119
+
120
+ return {
121
+ row: media,
122
+ };
123
+ },
124
+ } satisfies BeforeEachTsTrigger<FileTableRow, {}>,
125
+ ...(userFileTableConfig?.hooks?.beforeEach || []),
126
+ ],
127
+ onInsteadOfDelete: async ({ dbx, tx, returningQuery, isOneOrNone, filterOpts }) => {
128
+ return onDeleteFromFileTable(fileTable, {
129
+ dbTX: dbx,
130
+ t: tx,
131
+ returningQuery,
132
+ isOneOrNone,
133
+ filterOpts,
134
+ });
135
+ },
136
+ },
137
+ },
138
+ ...omitKeys(tableConfig ?? {}, [fileTableName]),
139
+ };
140
+ };
@@ -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
+ };