prostgles-server 4.2.567 → 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 (223) 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/Logging.d.ts +1 -1
  79. package/dist/Logging.d.ts.map +1 -1
  80. package/dist/Prostgles.d.ts +52 -6
  81. package/dist/Prostgles.d.ts.map +1 -1
  82. package/dist/Prostgles.js +8 -47
  83. package/dist/Prostgles.js.map +1 -1
  84. package/dist/ProstglesTypes.d.ts +23 -21
  85. package/dist/ProstglesTypes.d.ts.map +1 -1
  86. package/dist/ProstglesTypes.js.map +1 -1
  87. package/dist/PubSubManager/notifListener.d.ts.map +1 -1
  88. package/dist/PubSubManager/notifListener.js +0 -1
  89. package/dist/PubSubManager/notifListener.js.map +1 -1
  90. package/dist/PublishParser/getDBSchemaTable.d.ts +2 -2
  91. package/dist/PublishParser/getDBSchemaTable.d.ts.map +1 -1
  92. package/dist/PublishParser/getDBSchemaTable.js +5 -3
  93. package/dist/PublishParser/getDBSchemaTable.js.map +1 -1
  94. package/dist/PublishParser/getSchemaFromPublish.d.ts.map +1 -1
  95. package/dist/PublishParser/getSchemaFromPublish.js +2 -2
  96. package/dist/PublishParser/getSchemaFromPublish.js.map +1 -1
  97. package/dist/PublishParser/publishTypesAndUtils.d.ts +1 -0
  98. package/dist/PublishParser/publishTypesAndUtils.d.ts.map +1 -1
  99. package/dist/PublishParser/publishTypesAndUtils.js.map +1 -1
  100. package/dist/RestApi.d.ts +1 -5
  101. package/dist/RestApi.d.ts.map +1 -1
  102. package/dist/RestApi.js +10 -5
  103. package/dist/RestApi.js.map +1 -1
  104. package/dist/TableConfig/{TableConfig.d.ts → TableConfigTypes.d.ts} +19 -64
  105. package/dist/TableConfig/TableConfigTypes.d.ts.map +1 -0
  106. package/dist/TableConfig/TableConfigTypes.js +3 -0
  107. package/dist/TableConfig/TableConfigTypes.js.map +1 -0
  108. package/dist/TableConfig/TableConfigurator.d.ts +104 -0
  109. package/dist/TableConfig/TableConfigurator.d.ts.map +1 -0
  110. package/dist/TableConfig/{TableConfig.js → TableConfigurator.js} +31 -44
  111. package/dist/TableConfig/TableConfigurator.js.map +1 -0
  112. package/dist/TableConfig/applyTableConfig.d.ts.map +1 -1
  113. package/dist/TableConfig/applyTableConfig.js +0 -1
  114. package/dist/TableConfig/applyTableConfig.js.map +1 -1
  115. package/dist/TableConfig/getColumnConfig.d.ts +1 -1
  116. package/dist/TableConfig/getColumnConfig.d.ts.map +1 -1
  117. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts +1 -1
  118. package/dist/TableConfig/getColumnSQLDefinitionQuery.d.ts.map +1 -1
  119. package/dist/TableConfig/getColumnSQLDefinitionQuery.js +1 -1
  120. package/dist/TableConfig/getColumnSQLDefinitionQuery.js.map +1 -1
  121. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts +1 -1
  122. package/dist/TableConfig/getConstraintDefinitionQueries.d.ts.map +1 -1
  123. package/dist/TableConfig/getCreateSchemaQueries.d.ts +26 -8
  124. package/dist/TableConfig/getCreateSchemaQueries.d.ts.map +1 -1
  125. package/dist/TableConfig/getSchemaDiffQueries.d.ts.map +1 -1
  126. package/dist/TableConfig/getSchemaDiffQueries.js +12 -8
  127. package/dist/TableConfig/getSchemaDiffQueries.js.map +1 -1
  128. package/dist/TableConfig/getTableColumnQueries.d.ts +1 -1
  129. package/dist/TableConfig/getTableColumnQueries.d.ts.map +1 -1
  130. package/dist/TableConfig/getTableColumnQueries.js +19 -19
  131. package/dist/TableConfig/getTableColumnQueries.js.map +1 -1
  132. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts +26 -10
  133. package/dist/TableConfig/getTableConfigSchemaQueries.d.ts.map +1 -1
  134. package/dist/TableConfig/getTableConfigSchemaQueries.js +8 -10
  135. package/dist/TableConfig/getTableConfigSchemaQueries.js.map +1 -1
  136. package/dist/TableConfig/indexes/getIndexesQueries.d.ts +1 -1
  137. package/dist/TableConfig/indexes/getIndexesQueries.d.ts.map +1 -1
  138. package/dist/TableConfig/initTableConfig.d.ts +1 -1
  139. package/dist/TableConfig/initTableConfig.d.ts.map +1 -1
  140. package/dist/TableConfig/initTableConfig.js +6 -3
  141. package/dist/TableConfig/initTableConfig.js.map +1 -1
  142. package/dist/WebsocketAPI/getClientSchema.d.ts.map +1 -1
  143. package/dist/WebsocketAPI/getClientSchema.js +6 -1
  144. package/dist/WebsocketAPI/getClientSchema.js.map +1 -1
  145. package/dist/index.d.ts +3 -2
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +1 -0
  148. package/dist/index.js.map +1 -1
  149. package/dist/initProstgles.d.ts.map +1 -1
  150. package/dist/initProstgles.js +2 -5
  151. package/dist/initProstgles.js.map +1 -1
  152. package/dist/runClientRequest.js +1 -1
  153. package/dist/runClientRequest.js.map +1 -1
  154. package/dist/updateConfiguration.d.ts.map +1 -1
  155. package/dist/updateConfiguration.js +0 -3
  156. package/dist/updateConfiguration.js.map +1 -1
  157. package/eslint.config.mjs +0 -3
  158. package/lib/Auth/AuthTypes.ts +6 -7
  159. package/lib/Auth/endpoints/setCatchAllRequestHandler.ts +6 -2
  160. package/lib/DBSchemaBuilder/getColumnTypescriptDefinition.ts +1 -1
  161. package/lib/DBSchemaBuilder/getDBGeneratedSchema.ts +1 -1
  162. package/lib/DboBuilder/DboBuilder.ts +4 -4
  163. package/lib/DboBuilder/DboBuilderTypes.ts +7 -20
  164. package/lib/DboBuilder/TableHandler/TableHandler.ts +7 -4
  165. package/lib/DboBuilder/TableHandler/delete.ts +23 -17
  166. package/lib/DboBuilder/TableHandler/insert/insert.ts +3 -12
  167. package/lib/DboBuilder/TableHandler/insert/insertNestedRecords.ts +13 -17
  168. package/lib/DboBuilder/TableHandler/insert/insertRowWithNestedRecords.ts +1 -1
  169. package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +41 -54
  170. package/lib/DboBuilder/TableHandler/update.ts +8 -19
  171. package/lib/DboBuilder/TableHandler/updateFile.ts +20 -42
  172. package/lib/DboBuilder/ViewHandler/ViewHandler.ts +1 -1
  173. package/lib/DboBuilder/ViewHandler/getInfo.ts +19 -2
  174. package/lib/DboBuilder/getColumns.ts +40 -4
  175. package/lib/DboBuilder/uploadFile.ts +34 -50
  176. package/lib/FileManager/FileManager.ts +23 -249
  177. package/lib/FileManager/getFileTableConfig.ts +142 -0
  178. package/lib/FileManager/getLocalStorageClient.ts +118 -0
  179. package/lib/FileManager/getValidatedFileType.ts +14 -22
  180. package/lib/FileManager/setupFileServeHandler.ts +102 -0
  181. package/lib/Logging.ts +0 -3
  182. package/lib/Prostgles.ts +9 -44
  183. package/lib/ProstglesTypes.ts +34 -23
  184. package/lib/PubSubManager/notifListener.ts +0 -2
  185. package/lib/PublishParser/getDBSchemaTable.ts +6 -3
  186. package/lib/PublishParser/getSchemaFromPublish.ts +2 -1
  187. package/lib/PublishParser/publishTypesAndUtils.ts +1 -0
  188. package/lib/RestApi.ts +12 -12
  189. package/lib/TableConfig/{TableConfig.ts → TableConfigTypes.ts} +18 -217
  190. package/lib/TableConfig/TableConfigurator.ts +191 -0
  191. package/lib/TableConfig/applyTableConfig.ts +2 -3
  192. package/lib/TableConfig/getColumnConfig.ts +2 -2
  193. package/lib/TableConfig/getColumnSQLDefinitionQuery.ts +2 -2
  194. package/lib/TableConfig/getConstraintDefinitionQueries.ts +1 -1
  195. package/lib/TableConfig/getCreateSchemaQueries.ts +1 -1
  196. package/lib/TableConfig/getSchemaDiffQueries.ts +14 -11
  197. package/lib/TableConfig/getTableColumnQueries.ts +22 -20
  198. package/lib/TableConfig/getTableConfigSchemaQueries.ts +20 -22
  199. package/lib/TableConfig/indexes/getIndexesQueries.ts +1 -1
  200. package/lib/TableConfig/initTableConfig.ts +7 -6
  201. package/lib/WebsocketAPI/getClientSchema.ts +7 -1
  202. package/lib/index.ts +3 -2
  203. package/lib/initProstgles.ts +2 -6
  204. package/lib/runClientRequest.ts +1 -1
  205. package/lib/updateConfiguration.ts +0 -3
  206. package/package.json +2 -3
  207. package/dist/FileManager/initFileManager.d.ts +0 -18
  208. package/dist/FileManager/initFileManager.d.ts.map +0 -1
  209. package/dist/FileManager/initFileManager.js +0 -257
  210. package/dist/FileManager/initFileManager.js.map +0 -1
  211. package/dist/FileManager/upload.d.ts +0 -4
  212. package/dist/FileManager/upload.d.ts.map +0 -1
  213. package/dist/FileManager/upload.js +0 -90
  214. package/dist/FileManager/upload.js.map +0 -1
  215. package/dist/FileManager/uploadStream.d.ts +0 -4
  216. package/dist/FileManager/uploadStream.d.ts.map +0 -1
  217. package/dist/FileManager/uploadStream.js +0 -100
  218. package/dist/FileManager/uploadStream.js.map +0 -1
  219. package/dist/TableConfig/TableConfig.d.ts.map +0 -1
  220. package/dist/TableConfig/TableConfig.js.map +0 -1
  221. package/lib/FileManager/initFileManager.ts +0 -254
  222. package/lib/FileManager/upload.ts +0 -59
  223. package/lib/FileManager/uploadStream.ts +0 -76
@@ -44,7 +44,7 @@ export async function getColumns(
44
44
  }
45
45
  }
46
46
 
47
- const columns: ValidatedColumnInfo[] = this.columns
47
+ const rawColumns: ValidatedColumnInfo[] = this.columns
48
48
  .filter((c) => {
49
49
  const { insert, select, update } = rules;
50
50
 
@@ -64,13 +64,13 @@ export async function getColumns(
64
64
  let update = !!c.privileges.UPDATE;
65
65
 
66
66
  const prostgles = this.dboBuilder.prostgles;
67
- const fileConfig = prostgles.fileManager?.getColInfo({
67
+ const fileConfig = getFileColumnInfo({
68
68
  colName: c.name,
69
- tableName: this.name,
69
+ tableHandler: this,
70
70
  });
71
71
 
72
72
  /** Do not allow updates to file table unless it's to delete fields */
73
- if (prostgles.fileManager?.config && prostgles.fileManager.tableName === this.name) {
73
+ if (prostgles.opts.fileTable?.tableName === this.name) {
74
74
  update = false;
75
75
  }
76
76
 
@@ -110,6 +110,17 @@ export async function getColumns(
110
110
  })
111
111
  .filter((c) => c.select || c.update || c.delete || c.insert);
112
112
 
113
+ const modifiedTableSchema = await this.dboBuilder.prostgles.opts.modifyClientSchema?.(
114
+ {
115
+ name: this.name,
116
+ ...(await this.getInfo()),
117
+ columns: rawColumns,
118
+ },
119
+ localParams?.isRemoteRequest?.clientInfo,
120
+ );
121
+
122
+ const columns = modifiedTableSchema?.columns ?? rawColumns;
123
+
113
124
  await this._log({
114
125
  command: "getColumns",
115
126
  localParams,
@@ -141,3 +152,28 @@ function capitalizeFirstLetter(string: string, nonalpha_replacement?: string): s
141
152
  const str = replaceNonAlphaNumeric(string, nonalpha_replacement);
142
153
  return str.charAt(0).toUpperCase() + str.slice(1);
143
154
  }
155
+
156
+ const getFileColumnInfo = (args: {
157
+ tableHandler: ViewHandler;
158
+ colName: string;
159
+ }): ValidatedColumnInfo["file"] | undefined => {
160
+ const fileTableConfig = args.tableHandler.dboBuilder.prostgles.opts.fileTable;
161
+ if (!fileTableConfig) return undefined;
162
+ const { colName, tableHandler } = args;
163
+ const tableName = tableHandler.name;
164
+ const tableConfig = fileTableConfig.referencedTables?.[tableName];
165
+ const isReferencingFileTable = tableHandler.columns.some(
166
+ (c) =>
167
+ c.name === colName &&
168
+ c.references &&
169
+ c.references.some(({ ftable }) => ftable === fileTableConfig.tableName),
170
+ );
171
+ const allowAllFiles = { acceptedContent: "*" } as const;
172
+ if (isReferencingFileTable) {
173
+ if (tableConfig && typeof tableConfig !== "string") {
174
+ return tableConfig.referenceColumns[colName] ?? allowAllFiles;
175
+ }
176
+ return allowAllFiles;
177
+ }
178
+ return undefined;
179
+ };
@@ -1,8 +1,10 @@
1
+ import { randomUUID } from "crypto";
1
2
  import type { AnyObject } from "prostgles-types";
2
3
  import { getKeys, isObject } from "prostgles-types";
3
- import type { LocalParams, Media } from "./DboBuilder";
4
- import type { ValidateRowBasic } from "../PublishParser/PublishParser";
5
- import type { TableHandler } from "./TableHandler/TableHandler";
4
+ import type { FileTableRow } from "../FileManager/getFileTableConfig";
5
+ import { getValidatedFileType } from "../FileManager/getValidatedFileType";
6
+ import type { FileTableConfig } from "../ProstglesTypes";
7
+ import type { LocalParams } from "./DboBuilder";
6
8
 
7
9
  export const isFile = (row: any): row is { data: Buffer; name: string } => {
8
10
  return Boolean(
@@ -17,41 +19,39 @@ export const isFile = (row: any): row is { data: Buffer; name: string } => {
17
19
 
18
20
  type UploadFileArgs = {
19
21
  row: AnyObject;
20
- validate: ValidateRowBasic | undefined;
21
22
  localParams: LocalParams | undefined;
22
23
  /**
23
24
  * Used to update an existing file
24
25
  */
25
- mediaId?: string;
26
+ mediaId: string | undefined;
26
27
  };
27
28
 
28
- export async function uploadFile(
29
- this: TableHandler,
30
- { row, localParams, validate, mediaId }: UploadFileArgs,
31
- ): Promise<Media> {
32
- if (!this.dboBuilder.prostgles.fileManager) throw "fileManager not set up";
33
-
34
- if (!isFile(row))
29
+ export const uploadFile = async (
30
+ config: FileTableConfig,
31
+ { row, localParams, mediaId }: UploadFileArgs,
32
+ ): Promise<FileTableRow> => {
33
+ if (!isFile(row)) {
35
34
  throw (
36
35
  "Expecting only two properties for file upload: { name: string; data: File | string | Buffer }; but got: " +
37
36
  Object.entries(row)
38
37
  .map(([k, v]) => `${k}: ${typeof v}`)
39
38
  .join(", ")
40
39
  );
40
+ }
41
+ const storageClient = config.storageClient;
41
42
  const { data, name } = row;
42
43
 
43
- const media_id =
44
- mediaId ?? (await this.db.one<{ name: string }>("SELECT gen_random_uuid() as name")).name;
44
+ const media_id = mediaId ?? randomUUID();
45
45
  const nestedInsert = localParams?.nestedInsert;
46
- const type = await this.dboBuilder.prostgles.fileManager.getValidatedFileType({
46
+ const type = await getValidatedFileType(config, {
47
47
  file: data,
48
48
  fileName: name,
49
49
  tableName: nestedInsert?.previousTable,
50
50
  colName: nestedInsert?.referencingColumn,
51
51
  });
52
52
  const media_name = `${media_id}.${type.ext}`;
53
- const parsedMediaKeys = ["id", "name", "original_name", "extension", "content_type"] as const;
54
- const media: Required<Pick<Media, (typeof parsedMediaKeys)[number]>> = {
53
+ const _parsedMediaKeys = ["id", "name", "original_name", "extension", "content_type"] as const;
54
+ const coreInfo: Required<Pick<FileTableRow, (typeof _parsedMediaKeys)[number]>> = {
55
55
  id: media_id,
56
56
  name: media_name,
57
57
  original_name: name,
@@ -59,41 +59,25 @@ export async function uploadFile(
59
59
  content_type: type.mime,
60
60
  };
61
61
 
62
- if (validate) {
63
- if (!localParams) throw "localParams missing";
64
- const parsedMedia = await validate({
65
- tx: localParams.tx?.t || this.tx?.t || this.db,
66
- row: media,
67
- dbx: this.getFinalDbo(localParams),
68
- localParams,
69
- command: "insert",
70
- data: media,
71
- });
72
- const missingKeys = parsedMediaKeys.filter((k) => !parsedMedia[k]);
73
- if (missingKeys.length) {
74
- throw `Some keys are missing from file insert validation: ${missingKeys.join(", ")}`;
75
- }
76
- }
77
-
78
- const _media: Media = await this.dboBuilder.prostgles.fileManager.uploadAsMedia({
79
- item: {
80
- data,
81
- name: media.name,
82
- content_type: media.content_type,
83
- extension: media.extension,
84
- },
85
- // imageCompression: {
86
- // inside: {
87
- // width: 1100,
88
- // height: 630
89
- // }
90
- // }
62
+ const uploadedInfo = await storageClient.upload({
63
+ file: data,
64
+ fileName: coreInfo.name,
65
+ contentType: coreInfo.content_type,
91
66
  });
92
67
 
93
- const mediaRow = {
94
- ...media,
95
- ..._media,
68
+ const mediaRow: FileTableRow = {
69
+ signed_url: null,
70
+ signed_url_expires: null,
71
+ added: new Date().toISOString(),
72
+ deleted: null,
73
+ deleted_from_storage: null,
74
+ description: "",
75
+ ...coreInfo,
76
+ ...uploadedInfo,
77
+ extension: type.ext,
78
+ url: uploadedInfo.cloud_url,
79
+ content_length: String(uploadedInfo.content_length),
96
80
  };
97
81
 
98
82
  return mediaRow;
99
- }
83
+ };
@@ -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,142 @@
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, expressApp } = prg.opts;
40
+ if (!fileTable) {
41
+ return tableConfig;
42
+ }
43
+
44
+ if (!expressApp) {
45
+ throw new Error(`FileManager requires an expressApp to be provided in prostgles.opts.`);
46
+ }
47
+
48
+ const { tableName: fileTableName, storageClient } = fileTable;
49
+
50
+ const userFileTableConfig = tableConfig?.[fileTableName];
51
+ if (userFileTableConfig) {
52
+ if ("isLookupTable" in userFileTableConfig) {
53
+ throw new Error(
54
+ `FileManager table name (${fileTableName}) cannot have isLookupTable set in tableConfig`,
55
+ );
56
+ }
57
+ if (userFileTableConfig.dropIfExists || userFileTableConfig.dropIfExistsCascade) {
58
+ throw new Error(
59
+ `FileManager table name (${fileTableName}) cannot have dropIfExists or dropIfExistsCascade set in tableConfig`,
60
+ );
61
+ }
62
+ if ("columns" in userFileTableConfig && userFileTableConfig.columns) {
63
+ const userCols = new Set(Object.keys(userFileTableConfig.columns));
64
+ const clashingColumns = Object.keys(FILE_TABLE_COLUMN_DEFINITIONS).filter((col) =>
65
+ userCols.has(col),
66
+ );
67
+ if (clashingColumns.length) {
68
+ throw new Error(
69
+ `FileManager table name (${fileTableName}) has clashing column names in tableConfig: ${clashingColumns}`,
70
+ );
71
+ }
72
+ }
73
+ }
74
+
75
+ return {
76
+ [fileTableName]: {
77
+ ...userFileTableConfig,
78
+ columns: {
79
+ ...FILE_TABLE_COLUMN_DEFINITIONS,
80
+ ...userFileTableConfig?.columns,
81
+ },
82
+ onMount: ({ _db }) => {
83
+ const { destroy } = setupFileServeHandler(_db, fileTable, storageClient, expressApp, prg);
84
+
85
+ const maxBfSizeMB = (prg.opts.io?.engine.opts.maxHttpBufferSize || 1e6) / 1e6;
86
+ console.log(
87
+ `Prostgles: Initiated file manager. Max allowed file size: ${maxBfSizeMB}MB (maxHttpBufferSize = 1e6). To increase this set maxHttpBufferSize in socket.io server init options`,
88
+ );
89
+ return {
90
+ onUnmount: destroy,
91
+ };
92
+ },
93
+ hooks: {
94
+ ...userFileTableConfig?.hooks,
95
+ beforeEach: [
96
+ {
97
+ commands: {
98
+ insert: 1,
99
+ update: 1,
100
+ },
101
+ validate: async ({ data, localParams, command, filter }) => {
102
+ const tableHandler = prg.dboBuilder.dboMap.get(fileTableName);
103
+ if (!tableHandler) throw "FileManager tableHandler not found";
104
+
105
+ if (command === "update") {
106
+ const { newData } = await updateFile(tableHandler, fileTable, {
107
+ filter: filter ?? {},
108
+ localParams,
109
+ newData: data,
110
+ });
111
+ return {
112
+ row: newData,
113
+ };
114
+ }
115
+
116
+ const media = await uploadFile(fileTable, {
117
+ row: data,
118
+ localParams,
119
+ mediaId: data.id,
120
+ });
121
+
122
+ return {
123
+ row: media,
124
+ };
125
+ },
126
+ } satisfies BeforeEachTsTrigger<FileTableRow, {}>,
127
+ ...(userFileTableConfig?.hooks?.beforeEach || []),
128
+ ],
129
+ onInsteadOfDelete: async ({ dbx, tx, returningQuery, isOneOrNone, filterOpts }) => {
130
+ return onDeleteFromFileTable(fileTable, {
131
+ dbTX: dbx,
132
+ t: tx,
133
+ returningQuery,
134
+ isOneOrNone,
135
+ filterOpts,
136
+ });
137
+ },
138
+ },
139
+ },
140
+ ...omitKeys(tableConfig ?? {}, [fileTableName]),
141
+ };
142
+ };