prostgles-server 4.2.159 → 4.2.161
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.
- package/dist/Auth/setEmailProvider.js +2 -2
- package/dist/Auth/setEmailProvider.js.map +1 -1
- package/lib/Auth/AuthHandler.ts +436 -0
- package/lib/Auth/AuthTypes.ts +280 -0
- package/lib/Auth/getSafeReturnURL.ts +35 -0
- package/lib/Auth/sendEmail.ts +83 -0
- package/lib/Auth/setAuthProviders.ts +128 -0
- package/lib/Auth/setEmailProvider.ts +85 -0
- package/lib/Auth/setupAuthRoutes.ts +161 -0
- package/lib/DBEventsManager.ts +178 -0
- package/lib/DBSchemaBuilder.ts +225 -0
- package/lib/DboBuilder/DboBuilder.ts +319 -0
- package/lib/DboBuilder/DboBuilderTypes.ts +361 -0
- package/lib/DboBuilder/QueryBuilder/Functions.ts +1153 -0
- package/lib/DboBuilder/QueryBuilder/QueryBuilder.ts +288 -0
- package/lib/DboBuilder/QueryBuilder/getJoinQuery.ts +263 -0
- package/lib/DboBuilder/QueryBuilder/getNewQuery.ts +271 -0
- package/lib/DboBuilder/QueryBuilder/getSelectQuery.ts +136 -0
- package/lib/DboBuilder/QueryBuilder/prepareHaving.ts +22 -0
- package/lib/DboBuilder/QueryStreamer.ts +250 -0
- package/lib/DboBuilder/TableHandler/DataValidator.ts +428 -0
- package/lib/DboBuilder/TableHandler/TableHandler.ts +205 -0
- package/lib/DboBuilder/TableHandler/delete.ts +115 -0
- package/lib/DboBuilder/TableHandler/insert.ts +183 -0
- package/lib/DboBuilder/TableHandler/insertTest.ts +78 -0
- package/lib/DboBuilder/TableHandler/onDeleteFromFileTable.ts +62 -0
- package/lib/DboBuilder/TableHandler/runInsertUpdateQuery.ts +134 -0
- package/lib/DboBuilder/TableHandler/update.ts +126 -0
- package/lib/DboBuilder/TableHandler/updateBatch.ts +49 -0
- package/lib/DboBuilder/TableHandler/updateFile.ts +48 -0
- package/lib/DboBuilder/TableHandler/upsert.ts +34 -0
- package/lib/DboBuilder/ViewHandler/ViewHandler.ts +393 -0
- package/lib/DboBuilder/ViewHandler/count.ts +38 -0
- package/lib/DboBuilder/ViewHandler/find.ts +153 -0
- package/lib/DboBuilder/ViewHandler/getExistsCondition.ts +73 -0
- package/lib/DboBuilder/ViewHandler/getExistsFilters.ts +74 -0
- package/lib/DboBuilder/ViewHandler/getInfo.ts +32 -0
- package/lib/DboBuilder/ViewHandler/getTableJoinQuery.ts +84 -0
- package/lib/DboBuilder/ViewHandler/parseComplexFilter.ts +96 -0
- package/lib/DboBuilder/ViewHandler/parseFieldFilter.ts +105 -0
- package/lib/DboBuilder/ViewHandler/parseJoinPath.ts +208 -0
- package/lib/DboBuilder/ViewHandler/prepareSortItems.ts +163 -0
- package/lib/DboBuilder/ViewHandler/prepareWhere.ts +90 -0
- package/lib/DboBuilder/ViewHandler/size.ts +37 -0
- package/lib/DboBuilder/ViewHandler/subscribe.ts +118 -0
- package/lib/DboBuilder/ViewHandler/validateViewRules.ts +70 -0
- package/lib/DboBuilder/dboBuilderUtils.ts +222 -0
- package/lib/DboBuilder/getColumns.ts +114 -0
- package/lib/DboBuilder/getCondition.ts +201 -0
- package/lib/DboBuilder/getSubscribeRelatedTables.ts +190 -0
- package/lib/DboBuilder/getTablesForSchemaPostgresSQL.ts +426 -0
- package/lib/DboBuilder/insertNestedRecords.ts +355 -0
- package/lib/DboBuilder/parseUpdateRules.ts +187 -0
- package/lib/DboBuilder/prepareShortestJoinPaths.ts +186 -0
- package/lib/DboBuilder/runSQL.ts +182 -0
- package/lib/DboBuilder/runTransaction.ts +50 -0
- package/lib/DboBuilder/sqlErrCodeToMsg.ts +254 -0
- package/lib/DboBuilder/uploadFile.ts +69 -0
- package/lib/Event_Trigger_Tags.ts +118 -0
- package/lib/FileManager/FileManager.ts +358 -0
- package/lib/FileManager/getValidatedFileType.ts +69 -0
- package/lib/FileManager/initFileManager.ts +187 -0
- package/lib/FileManager/upload.ts +62 -0
- package/lib/FileManager/uploadStream.ts +79 -0
- package/lib/Filtering.ts +463 -0
- package/lib/JSONBValidation/validate_jsonb_schema_sql.ts +502 -0
- package/lib/JSONBValidation/validation.ts +143 -0
- package/lib/Logging.ts +127 -0
- package/lib/PostgresNotifListenManager.ts +143 -0
- package/lib/Prostgles.ts +485 -0
- package/lib/ProstglesTypes.ts +196 -0
- package/lib/PubSubManager/PubSubManager.ts +609 -0
- package/lib/PubSubManager/addSub.ts +138 -0
- package/lib/PubSubManager/addSync.ts +141 -0
- package/lib/PubSubManager/getCreatePubSubManagerError.ts +72 -0
- package/lib/PubSubManager/getPubSubManagerInitQuery.ts +662 -0
- package/lib/PubSubManager/initPubSubManager.ts +79 -0
- package/lib/PubSubManager/notifListener.ts +173 -0
- package/lib/PubSubManager/orphanTriggerCheck.ts +70 -0
- package/lib/PubSubManager/pushSubData.ts +55 -0
- package/lib/PublishParser/PublishParser.ts +162 -0
- package/lib/PublishParser/getFileTableRules.ts +124 -0
- package/lib/PublishParser/getSchemaFromPublish.ts +141 -0
- package/lib/PublishParser/getTableRulesWithoutFileTable.ts +177 -0
- package/lib/PublishParser/publishTypesAndUtils.ts +399 -0
- package/lib/RestApi.ts +127 -0
- package/lib/SchemaWatch/SchemaWatch.ts +90 -0
- package/lib/SchemaWatch/createSchemaWatchEventTrigger.ts +3 -0
- package/lib/SchemaWatch/getValidatedWatchSchemaType.ts +45 -0
- package/lib/SchemaWatch/getWatchSchemaTagList.ts +27 -0
- package/lib/SyncReplication.ts +557 -0
- package/lib/TableConfig/TableConfig.ts +468 -0
- package/lib/TableConfig/getColumnDefinitionQuery.ts +111 -0
- package/lib/TableConfig/getConstraintDefinitionQueries.ts +95 -0
- package/lib/TableConfig/getFutureTableSchema.ts +64 -0
- package/lib/TableConfig/getPGIndexes.ts +53 -0
- package/lib/TableConfig/getTableColumnQueries.ts +129 -0
- package/lib/TableConfig/initTableConfig.ts +326 -0
- package/lib/index.ts +13 -0
- package/lib/initProstgles.ts +319 -0
- package/lib/onSocketConnected.ts +102 -0
- package/lib/runClientRequest.ts +129 -0
- package/lib/shortestPath.ts +122 -0
- package/lib/typeTests/DBoGenerated.d.ts +320 -0
- package/lib/typeTests/dboTypeCheck.ts +81 -0
- package/lib/utils.ts +15 -0
- package/package.json +1 -1
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
import { AnyObject, DBSchema, FullFilter, Method, RULE_METHODS } from "prostgles-types";
|
|
2
|
+
import type { DBOFullyTyped, PublishFullyTyped } from "../DBSchemaBuilder";
|
|
3
|
+
import { CommonTableRules, Filter, LocalParams, PRGLIOSocket, TableOrViewInfo } from "../DboBuilder/DboBuilder";
|
|
4
|
+
import { DB, DBHandlerServer } from "../Prostgles";
|
|
5
|
+
|
|
6
|
+
export type PublishMethods<S = void, SUser extends SessionUser = SessionUser> = (params: PublishParams<S, SUser>) => { [key: string]: Method } | Promise<{ [key: string]: Method } | null>;
|
|
7
|
+
|
|
8
|
+
export type Awaitable<T> = T | Promise<T>;
|
|
9
|
+
|
|
10
|
+
type Request = {
|
|
11
|
+
socket?: any;
|
|
12
|
+
httpReq?: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type DboTable = Request & {
|
|
16
|
+
tableName: string;
|
|
17
|
+
localParams: LocalParams;
|
|
18
|
+
}
|
|
19
|
+
export type DboTableCommand = Request & DboTable & {
|
|
20
|
+
command: string;
|
|
21
|
+
localParams: LocalParams;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const RULE_TO_METHODS = [
|
|
25
|
+
{
|
|
26
|
+
rule: "getColumns",
|
|
27
|
+
sqlRule: "select",
|
|
28
|
+
methods: RULE_METHODS.getColumns,
|
|
29
|
+
no_limits: true,
|
|
30
|
+
allowed_params: [],
|
|
31
|
+
table_only: false,
|
|
32
|
+
hint: ` expecting false | true | undefined`
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
rule: "getInfo",
|
|
36
|
+
sqlRule: "select",
|
|
37
|
+
methods: RULE_METHODS.getInfo,
|
|
38
|
+
no_limits: true,
|
|
39
|
+
allowed_params: [],
|
|
40
|
+
table_only: false,
|
|
41
|
+
hint: ` expecting false | true | undefined`
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
rule: "insert",
|
|
45
|
+
sqlRule: "insert",
|
|
46
|
+
methods: RULE_METHODS.insert,
|
|
47
|
+
no_limits: <SelectRule>{ fields: "*" },
|
|
48
|
+
table_only: true,
|
|
49
|
+
allowed_params: { checkFilter: 1, fields: 1, forcedData: 1, postValidate: 1, preValidate: 1, returningFields: 1, validate: 1, allowedNestedInserts: 1 } satisfies Record<keyof InsertRule, 1>,
|
|
50
|
+
hint: ` expecting "*" | true | { fields: string | string[] | {} }`
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
rule: "update",
|
|
54
|
+
sqlRule: "update",
|
|
55
|
+
methods: RULE_METHODS.update,
|
|
56
|
+
no_limits: <UpdateRule>{ fields: "*", filterFields: "*", returningFields: "*" },
|
|
57
|
+
table_only: true,
|
|
58
|
+
allowed_params: { checkFilter: 1, dynamicFields: 1, fields: 1, filterFields: 1, forcedData: 1, forcedFilter: 1, postValidate: 1, returningFields: 1, validate: 1, } satisfies Record<keyof UpdateRule, 1>,
|
|
59
|
+
hint: ` expecting "*" | true | { fields: string | string[] | {} }`
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
rule: "select",
|
|
63
|
+
sqlRule: "select",
|
|
64
|
+
methods: RULE_METHODS.select,
|
|
65
|
+
no_limits: <SelectRule>{ fields: "*", filterFields: "*" },
|
|
66
|
+
table_only: false,
|
|
67
|
+
allowed_params: { fields: 1, filterFields: 1, forcedFilter: 1, maxLimit: 1, orderByFields: 1, validate: 1 } satisfies Record<keyof SelectRule, 1>,
|
|
68
|
+
hint: ` expecting "*" | true | { fields: ( string | string[] | {} ) }`
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
rule: "delete",
|
|
72
|
+
sqlRule: "delete",
|
|
73
|
+
methods: RULE_METHODS.delete,
|
|
74
|
+
no_limits: <DeleteRule>{ filterFields: "*" },
|
|
75
|
+
table_only: true,
|
|
76
|
+
allowed_params: { returningFields: 1, validate: 1, filterFields: 1, forcedFilter: 1 } satisfies Record<keyof DeleteRule, 1>,
|
|
77
|
+
hint: ` expecting "*" | true | { filterFields: ( string | string[] | {} ) } \n Will use "select", "update", "delete" and "insert" rules`
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
rule: "sync",
|
|
81
|
+
sqlRule: "select",
|
|
82
|
+
methods: RULE_METHODS.sync,
|
|
83
|
+
no_limits: null,
|
|
84
|
+
table_only: true,
|
|
85
|
+
allowed_params: { allow_delete: 1, batch_size: 1, id_fields: 1, synced_field: 1, throttle: 1 } satisfies Record<keyof SyncRule, 1>,
|
|
86
|
+
hint: ` expecting "*" | true | { id_fields: string[], synced_field: string }`
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
rule: "subscribe",
|
|
90
|
+
sqlRule: "select",
|
|
91
|
+
methods: RULE_METHODS.subscribe,
|
|
92
|
+
no_limits: <SubscribeRule>{ throttle: 0 },
|
|
93
|
+
table_only: false,
|
|
94
|
+
allowed_params: { throttle: 1 } satisfies Record<keyof SubscribeRule, 1>,
|
|
95
|
+
hint: ` expecting "*" | true | { throttle: number; throttleOpts?: { skipFirst?: boolean; } } \n Will use "select" rules`
|
|
96
|
+
}
|
|
97
|
+
] as const;
|
|
98
|
+
|
|
99
|
+
import { FieldFilter, SelectParams } from "prostgles-types";
|
|
100
|
+
import { TableSchemaColumn } from "../DboBuilder/DboBuilderTypes";
|
|
101
|
+
import { SessionUser } from "../Auth/AuthTypes";
|
|
102
|
+
|
|
103
|
+
export type InsertRequestData = {
|
|
104
|
+
data: object | object[]
|
|
105
|
+
returning: FieldFilter;
|
|
106
|
+
}
|
|
107
|
+
export type SelectRequestData = {
|
|
108
|
+
filter: object;
|
|
109
|
+
params: SelectParams;
|
|
110
|
+
}
|
|
111
|
+
export type DeleteRequestData = {
|
|
112
|
+
filter: object;
|
|
113
|
+
returning: FieldFilter;
|
|
114
|
+
}
|
|
115
|
+
export type UpdateRequestDataOne<R extends AnyObject, S extends DBSchema | void = void> = {
|
|
116
|
+
filter: FullFilter<R, S>
|
|
117
|
+
data: Partial<R>;
|
|
118
|
+
returning: FieldFilter<R>;
|
|
119
|
+
}
|
|
120
|
+
export type UpdateReq<R extends AnyObject, S extends DBSchema | void = void> = {
|
|
121
|
+
filter: FullFilter<R, S>
|
|
122
|
+
data: Partial<R>;
|
|
123
|
+
}
|
|
124
|
+
export type UpdateRequestDataBatch<R extends AnyObject> = {
|
|
125
|
+
data: UpdateReq<R>[];
|
|
126
|
+
}
|
|
127
|
+
export type UpdateRequestData<R extends AnyObject = AnyObject> = UpdateRequestDataOne<R> | UpdateRequestDataBatch<R>;
|
|
128
|
+
|
|
129
|
+
export type ValidateRowArgs<R = AnyObject, DBX = DBHandlerServer> = {
|
|
130
|
+
row: R;
|
|
131
|
+
dbx: DBX;
|
|
132
|
+
localParams: LocalParams;
|
|
133
|
+
}
|
|
134
|
+
export type ValidateUpdateRowArgs<U = Partial<AnyObject>, F = Filter, DBX = DBHandlerServer> = {
|
|
135
|
+
update: U;
|
|
136
|
+
filter: F;
|
|
137
|
+
dbx: DBX;
|
|
138
|
+
localParams: LocalParams;
|
|
139
|
+
}
|
|
140
|
+
export type ValidateRow<R extends AnyObject = AnyObject, S = void> = (args: ValidateRowArgs<R, DBOFullyTyped<S>>) => R | Promise<R>;
|
|
141
|
+
export type PostValidateRow<R extends AnyObject = AnyObject, S = void> = (args: ValidateRowArgs<R, DBOFullyTyped<S>>) => void | Promise<void>;
|
|
142
|
+
export type PostValidateRowBasic = (args: ValidateRowArgs) => void | Promise<void>;
|
|
143
|
+
export type ValidateRowBasic = (args: ValidateRowArgs) => AnyObject | Promise<AnyObject>;
|
|
144
|
+
export type ValidateUpdateRow<R extends AnyObject = AnyObject, S extends DBSchema | void = void> = (args: ValidateUpdateRowArgs<Partial<R>, FullFilter<R, S>, DBOFullyTyped<S>>) => Partial<R> | Promise<Partial<R>>;
|
|
145
|
+
export type ValidateUpdateRowBasic = (args: ValidateUpdateRowArgs) => AnyObject | Promise<AnyObject>;
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
export type SelectRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Fields allowed to be selected.
|
|
152
|
+
* Tip: Use false to exclude field
|
|
153
|
+
*/
|
|
154
|
+
fields: FieldFilter<Cols>;
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Fields allowed to sorted
|
|
158
|
+
* Defaults to the "fields". Use empty array/object to disallow sorting
|
|
159
|
+
*/
|
|
160
|
+
orderByFields?: FieldFilter<Cols>;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The maximum number of rows a user can get in a select query. null by default. Unless a null or higher limit is specified 100 rows will be returned by the default
|
|
164
|
+
*/
|
|
165
|
+
maxLimit?: number | null;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Filter added to every query (e.g. user_id) to restrict access
|
|
169
|
+
*/
|
|
170
|
+
forcedFilter?: FullFilter<Cols, S>;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Fields user can filter by
|
|
174
|
+
* */
|
|
175
|
+
filterFields?: FieldFilter<Cols>;
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Validation logic to check/update data for each request
|
|
179
|
+
*/
|
|
180
|
+
validate?(args: SelectRequestData): SelectRequestData | Promise<SelectRequestData>;
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export type CommonInsertUpdateRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Filter that the new records must match or the update/insert will fail
|
|
188
|
+
* Similar to a policy WITH CHECK clause
|
|
189
|
+
*/
|
|
190
|
+
checkFilter?: SelectRule<Cols, S>["forcedFilter"];
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Data to include and overwrite on each update/insert
|
|
194
|
+
* These fields cannot be updated by the user
|
|
195
|
+
*/
|
|
196
|
+
forcedData?: Partial<Cols>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export type InsertRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = CommonInsertUpdateRule<Cols, S> & {
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Fields allowed to be inserted. Tip: Use false to exclude field
|
|
203
|
+
*/
|
|
204
|
+
fields: SelectRule<Cols>["fields"]
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Fields user can view after inserting
|
|
208
|
+
*/
|
|
209
|
+
returningFields?: SelectRule<Cols>["fields"]
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Validation logic to check/update data for each request. Happens before publish rule checks (for fields, forcedData/forcedFilter)
|
|
213
|
+
*/
|
|
214
|
+
preValidate?: S extends DBSchema? ValidateRow<Cols, S> : ValidateRowBasic;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Validation logic to check/update data for each request. Happens after publish rule checks (for fields, forcedData/forcedFilter)
|
|
218
|
+
*/
|
|
219
|
+
validate?: S extends DBSchema? ValidateRow<Cols, S> : ValidateRowBasic;
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Validation logic to check/update data after the insert.
|
|
223
|
+
* Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
|
|
224
|
+
*/
|
|
225
|
+
postValidate?: S extends DBSchema? PostValidateRow<Required<Cols>, S> : PostValidateRowBasic;
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* If defined then only nested inserts from these tables are allowed
|
|
229
|
+
* Direct inserts will fail
|
|
230
|
+
*/
|
|
231
|
+
allowedNestedInserts?: {
|
|
232
|
+
table: string;
|
|
233
|
+
column: string;
|
|
234
|
+
}[];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
export type UpdateRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = CommonInsertUpdateRule<Cols, S> & {
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Fields allowed to be updated. Tip: Use false/0 to exclude field
|
|
242
|
+
*/
|
|
243
|
+
fields: SelectRule<Cols>["fields"]
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Row level FGAC
|
|
247
|
+
* Used when the editable fields change based on the updated row
|
|
248
|
+
* If specified then the fields from the first matching filter table.count({ ...filter, ...updateFilter }) > 0 will be used
|
|
249
|
+
* If none matching then the "fields" will be used
|
|
250
|
+
* Specify in decreasing order of specificity otherwise a more general filter will match first
|
|
251
|
+
*/
|
|
252
|
+
dynamicFields?: {
|
|
253
|
+
filter: FullFilter<Cols, S>;
|
|
254
|
+
fields: SelectRule<Cols>["fields"]
|
|
255
|
+
}[];
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Filter added to every query (e.g. user_id) to restrict access
|
|
259
|
+
* This filter cannot be updated
|
|
260
|
+
*/
|
|
261
|
+
forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Fields user can use to find the updates
|
|
265
|
+
*/
|
|
266
|
+
filterFields?: SelectRule<Cols>["fields"]
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Fields user can view after updating
|
|
270
|
+
*/
|
|
271
|
+
returningFields?: SelectRule<Cols>["fields"]
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Validation logic to check/update data for each request
|
|
275
|
+
*/
|
|
276
|
+
validate?: S extends DBSchema? ValidateUpdateRow<Cols, S> : ValidateUpdateRowBasic;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Validation logic to check/update data after the insert.
|
|
280
|
+
* Happens in the same transaction so upon throwing an error the record will be deleted (not committed)
|
|
281
|
+
*/
|
|
282
|
+
postValidate?: S extends DBSchema? PostValidateRow<Required<Cols>, S> : PostValidateRowBasic;
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
export type DeleteRule<Cols extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Filter added to every query (e.g. user_id) to restrict access
|
|
289
|
+
*/
|
|
290
|
+
forcedFilter?: SelectRule<Cols, S>["forcedFilter"]
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Fields user can filter by
|
|
294
|
+
*/
|
|
295
|
+
filterFields: FieldFilter<Cols>;
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Fields user can view after deleting
|
|
299
|
+
*/
|
|
300
|
+
returningFields?: SelectRule<Cols>["filterFields"]
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Validation logic to check/update data for each request
|
|
304
|
+
*/
|
|
305
|
+
validate?(...args: any[]): Awaitable<void>;// UpdateRequestData<Cols>;
|
|
306
|
+
}
|
|
307
|
+
export type SyncRule<Cols extends AnyObject = AnyObject> = {
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Primary keys used in updating data
|
|
311
|
+
*/
|
|
312
|
+
id_fields: (keyof Cols)[];
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Numerical incrementing fieldname (last updated timestamp) used to sync items
|
|
316
|
+
*/
|
|
317
|
+
synced_field: keyof Cols;
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* EXPERIMENTAL. Disabled by default. If true then server will attempt to delete any records missing from client.
|
|
321
|
+
*/
|
|
322
|
+
allow_delete?: boolean;
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Throttle replication transmission in milliseconds. Defaults to 100
|
|
326
|
+
*/
|
|
327
|
+
throttle?: number;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Number of rows to send per trip. Defaults to 50
|
|
331
|
+
*/
|
|
332
|
+
batch_size?: number;
|
|
333
|
+
}
|
|
334
|
+
export type SubscribeRule = {
|
|
335
|
+
throttle?: number;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export type ViewRule<S extends AnyObject = AnyObject> = CommonTableRules & {
|
|
339
|
+
/**
|
|
340
|
+
* What can be read from the table
|
|
341
|
+
*/
|
|
342
|
+
select?: SelectRule<S>;
|
|
343
|
+
};
|
|
344
|
+
export type TableRule<RowType extends AnyObject = AnyObject, S extends DBSchema | void = void> = ViewRule<RowType> & {
|
|
345
|
+
insert?: InsertRule<RowType, S>;
|
|
346
|
+
update?: UpdateRule<RowType, S>;
|
|
347
|
+
delete?: DeleteRule<RowType, S>;
|
|
348
|
+
sync?: SyncRule<RowType>;
|
|
349
|
+
subscribe?: SubscribeRule;
|
|
350
|
+
};
|
|
351
|
+
export type PublishViewRule<Col extends AnyObject = AnyObject, S extends DBSchema | void = void> = {
|
|
352
|
+
select?: SelectRule<Col, S> | PublishAllOrNothing
|
|
353
|
+
getColumns?: PublishAllOrNothing;
|
|
354
|
+
getInfo?: PublishAllOrNothing;
|
|
355
|
+
};
|
|
356
|
+
export type PublishTableRule<Col extends AnyObject = AnyObject, S extends DBSchema | void = void> = PublishViewRule<Col, S> & {
|
|
357
|
+
insert?: InsertRule<Col, S> | PublishAllOrNothing
|
|
358
|
+
update?: UpdateRule<Col, S> | PublishAllOrNothing
|
|
359
|
+
delete?: DeleteRule<Col, S> | PublishAllOrNothing
|
|
360
|
+
sync?: SyncRule<Col>;
|
|
361
|
+
subscribe?: SubscribeRule | PublishAllOrNothing;
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
export type ParsedPublishTable = {
|
|
366
|
+
select?: SelectRule
|
|
367
|
+
getColumns?: true;
|
|
368
|
+
getInfo?: true;
|
|
369
|
+
|
|
370
|
+
insert?: InsertRule;
|
|
371
|
+
update?: UpdateRule;
|
|
372
|
+
delete?: DeleteRule;
|
|
373
|
+
sync?: SyncRule;
|
|
374
|
+
subscribe?: SubscribeRule;
|
|
375
|
+
subscribeOne?: SubscribeRule;
|
|
376
|
+
}
|
|
377
|
+
export type DbTableInfo = {
|
|
378
|
+
name: string;
|
|
379
|
+
info: TableOrViewInfo;
|
|
380
|
+
columns: TableSchemaColumn[];
|
|
381
|
+
}
|
|
382
|
+
export type PublishParams<S = void, SUser extends SessionUser = SessionUser> = {
|
|
383
|
+
sid?: string;
|
|
384
|
+
dbo: DBOFullyTyped<S>;
|
|
385
|
+
db: DB;
|
|
386
|
+
user?: SUser["user"];
|
|
387
|
+
socket: PRGLIOSocket;
|
|
388
|
+
tables: DbTableInfo[];
|
|
389
|
+
}
|
|
390
|
+
export type RequestParams = { dbo?: DBHandlerServer, socket?: any };
|
|
391
|
+
export type PublishAllOrNothing = true | "*" | false | null;
|
|
392
|
+
export type PublishObject = {
|
|
393
|
+
[table_name: string]: (PublishTableRule | PublishViewRule | PublishAllOrNothing)
|
|
394
|
+
};
|
|
395
|
+
export type ParsedPublishTables = {
|
|
396
|
+
[table_name: string]: ParsedPublishTable
|
|
397
|
+
};
|
|
398
|
+
export type PublishedResult<Schema = void> = PublishAllOrNothing | PublishFullyTyped<Schema>;
|
|
399
|
+
export type Publish<Schema = void, SUser extends SessionUser = SessionUser> = PublishedResult<Schema> | ((params: PublishParams<Schema, SUser>) => Awaitable<PublishedResult<Schema>>);
|
package/lib/RestApi.ts
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import * as bodyParser from "body-parser";
|
|
2
|
+
import { Express } from "express";
|
|
3
|
+
import { HTTPCODES } from "./Auth/AuthHandler";
|
|
4
|
+
import { ExpressReq, ExpressRes } from "./Auth/AuthTypes";
|
|
5
|
+
import { getSerializedClientErrorFromPGError } from "./DboBuilder/DboBuilder";
|
|
6
|
+
import { Prostgles } from "./Prostgles";
|
|
7
|
+
import { runClientMethod, runClientRequest, runClientSqlRequest } from "./runClientRequest";
|
|
8
|
+
import { VoidFunction } from "./SchemaWatch/SchemaWatch";
|
|
9
|
+
const jsonParser = bodyParser.json();
|
|
10
|
+
|
|
11
|
+
export type ExpressApp = {
|
|
12
|
+
_router?: {
|
|
13
|
+
stack?: {
|
|
14
|
+
name: string;
|
|
15
|
+
handle: VoidFunction;
|
|
16
|
+
path: undefined,
|
|
17
|
+
keys?: any[];
|
|
18
|
+
route?: {
|
|
19
|
+
path?: string;
|
|
20
|
+
methods?: {
|
|
21
|
+
get?: boolean;
|
|
22
|
+
post?: boolean;
|
|
23
|
+
put?: boolean;
|
|
24
|
+
delete?: boolean;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
}[]
|
|
28
|
+
}
|
|
29
|
+
} & Omit<Express, "_router">;
|
|
30
|
+
|
|
31
|
+
export type RestApiConfig = {
|
|
32
|
+
expressApp: Express;
|
|
33
|
+
routePrefix: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class RestApi {
|
|
37
|
+
prostgles: Prostgles;
|
|
38
|
+
routes: {
|
|
39
|
+
db: string;
|
|
40
|
+
sql: string;
|
|
41
|
+
methods: string;
|
|
42
|
+
schema: string;
|
|
43
|
+
}
|
|
44
|
+
expressApp: Express;
|
|
45
|
+
constructor({ expressApp, routePrefix, prostgles }: RestApiConfig & { prostgles: Prostgles; }){
|
|
46
|
+
this.prostgles = prostgles;
|
|
47
|
+
this.routes = {
|
|
48
|
+
db: `${routePrefix}/db/:tableName/:command`,
|
|
49
|
+
sql: `${routePrefix}/db/sql`,
|
|
50
|
+
methods: `${routePrefix}/methods/:method`,
|
|
51
|
+
schema: `${routePrefix}/schema`,
|
|
52
|
+
};
|
|
53
|
+
this.expressApp = expressApp;
|
|
54
|
+
expressApp.post(this.routes.db, jsonParser, this.onPost);
|
|
55
|
+
expressApp.post(this.routes.sql, jsonParser, this.onPostSql);
|
|
56
|
+
expressApp.post(this.routes.methods, jsonParser, this.onPostMethod);
|
|
57
|
+
expressApp.post(this.routes.schema, jsonParser, this.onPostSchema);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
destroy = () => {
|
|
61
|
+
this.expressApp.removeListener(this.routes.db, this.onPost);
|
|
62
|
+
this.expressApp.removeListener(this.routes.sql, this.onPostSql);
|
|
63
|
+
this.expressApp.removeListener(this.routes.methods, this.onPostMethod);
|
|
64
|
+
}
|
|
65
|
+
onPostMethod = async (req: ExpressReq, res: ExpressRes) => {
|
|
66
|
+
const { method = "" } = req.params;
|
|
67
|
+
const params = req.body || [];
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const data = await runClientMethod.bind(this.prostgles)({
|
|
71
|
+
type: "http",
|
|
72
|
+
httpReq: req, method, params
|
|
73
|
+
});
|
|
74
|
+
res.json(data);
|
|
75
|
+
} catch(rawError){
|
|
76
|
+
const error = getSerializedClientErrorFromPGError(rawError, { type: "method", localParams: { httpReq: req } });
|
|
77
|
+
res.status(400).json({ error });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
onPostSchema = async (req: ExpressReq, res: ExpressRes) => {
|
|
81
|
+
try {
|
|
82
|
+
const data = await this.prostgles.getClientSchema({ httpReq: req });
|
|
83
|
+
res.json(data);
|
|
84
|
+
} catch(rawError){
|
|
85
|
+
const error = getSerializedClientErrorFromPGError(rawError, { type: "method", localParams: { httpReq: req } });
|
|
86
|
+
res.status(400).json({ error });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
onPostSql = async (req: ExpressReq, res: ExpressRes) => {
|
|
90
|
+
const [query, args, options] = req.body || [];
|
|
91
|
+
try {
|
|
92
|
+
const data = await runClientSqlRequest.bind(this.prostgles)({ type: "http", httpReq: req, query, args, options });
|
|
93
|
+
res.json(data);
|
|
94
|
+
} catch(rawError){
|
|
95
|
+
const error = getSerializedClientErrorFromPGError(rawError, { type: "sql" });
|
|
96
|
+
res.status(400).json({ error });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
onPost = async (req: ExpressReq, res: ExpressRes) => {
|
|
101
|
+
const { params } = req;
|
|
102
|
+
const { tableName, command } = params;
|
|
103
|
+
if(!tableName || typeof tableName !== "string"){
|
|
104
|
+
res.status(400).json({ error: "tableName missing" });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if(!command || typeof command !== "string"){
|
|
108
|
+
res.status(HTTPCODES.BAD_REQUEST).json({ error: "command missing" });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const [param1, param2, param3] = req.body || [];
|
|
114
|
+
const data = await runClientRequest.bind(this.prostgles)({
|
|
115
|
+
type: "http",
|
|
116
|
+
httpReq: req,
|
|
117
|
+
tableName,
|
|
118
|
+
command,
|
|
119
|
+
param1, param2, param3
|
|
120
|
+
});
|
|
121
|
+
res.json(data);
|
|
122
|
+
} catch(rawError){
|
|
123
|
+
const error = getSerializedClientErrorFromPGError(rawError, { type: "tableMethod", localParams: { httpReq: req }, view: this.prostgles.dboBuilder.dbo[tableName] });
|
|
124
|
+
res.status(400).json({ error });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { DboBuilder } from "../DboBuilder/DboBuilder";
|
|
2
|
+
import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
|
|
3
|
+
import { PubSubManager, log } from "../PubSubManager/PubSubManager";
|
|
4
|
+
import { ValidatedWatchSchemaType, getValidatedWatchSchemaType } from "./getValidatedWatchSchemaType";
|
|
5
|
+
const COMMAND_FIRST_KEYWORDS = EVENT_TRIGGER_TAGS
|
|
6
|
+
.map(tag => tag.split(" ")[0]!)
|
|
7
|
+
.filter(tag => tag !== "SELECT"); /** SELECT INTO is not easily detectable with pg-node (command = "SELECT") */
|
|
8
|
+
|
|
9
|
+
const DB_FALLBACK_COMMANDS = Array.from(new Set(COMMAND_FIRST_KEYWORDS))
|
|
10
|
+
.concat([
|
|
11
|
+
"DO", // Do statement
|
|
12
|
+
"COMMIT" // Transaction block
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
export type VoidFunction = () => void;
|
|
16
|
+
|
|
17
|
+
export type OnSchemaChangeCallback = ((event: { command: string; query: string }) => void);
|
|
18
|
+
|
|
19
|
+
export class SchemaWatch {
|
|
20
|
+
|
|
21
|
+
dboBuilder: DboBuilder;
|
|
22
|
+
type: ValidatedWatchSchemaType;
|
|
23
|
+
private constructor(dboBuilder: DboBuilder){
|
|
24
|
+
this.dboBuilder = dboBuilder;
|
|
25
|
+
this.type = getValidatedWatchSchemaType(dboBuilder);
|
|
26
|
+
if(this.type.watchType === "NONE") {
|
|
27
|
+
this.onSchemaChange = undefined;
|
|
28
|
+
this.onSchemaChangeFallback = undefined;
|
|
29
|
+
}
|
|
30
|
+
if(this.type.watchType === "DDL_trigger") {
|
|
31
|
+
this.onSchemaChangeFallback = undefined;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static create = async (dboBuilder: DboBuilder) => {
|
|
36
|
+
const instance = new SchemaWatch(dboBuilder);
|
|
37
|
+
if(instance.type.watchType === "DDL_trigger") {
|
|
38
|
+
await dboBuilder.getPubSubManager();
|
|
39
|
+
// TODO finish createSchemaWatchEventTrigger to ensure the query is not used in NOTIFY and exclude happens inside Postgres
|
|
40
|
+
}
|
|
41
|
+
return instance;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Fallback for watchSchema in case of not a superuser (cannot add db event listener)
|
|
46
|
+
*/
|
|
47
|
+
onSchemaChangeFallback: OnSchemaChangeCallback | undefined = async ({ command, query }) => {
|
|
48
|
+
|
|
49
|
+
if (typeof query === "string" && query.includes(PubSubManager.EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID)) {
|
|
50
|
+
log("Schema change event excluded from triggers due to EXCLUDE_QUERY_FROM_SCHEMA_WATCH_ID");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if(
|
|
54
|
+
this.type.watchType !== "prostgles_queries" ||
|
|
55
|
+
!this.onSchemaChange ||
|
|
56
|
+
!DB_FALLBACK_COMMANDS.includes(command)
|
|
57
|
+
) return;
|
|
58
|
+
|
|
59
|
+
this.onSchemaChange({ command, query })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
onSchemaChange: OnSchemaChangeCallback | undefined = async (event) => {
|
|
63
|
+
|
|
64
|
+
const { watchSchema, onReady, tsGeneratedTypesDir } = this.dboBuilder.prostgles.opts;
|
|
65
|
+
if (watchSchema && this.dboBuilder.prostgles.loaded) {
|
|
66
|
+
log("Schema changed");
|
|
67
|
+
const { query, command } = event;
|
|
68
|
+
|
|
69
|
+
this.dboBuilder.cacheDBTypes(true);
|
|
70
|
+
if (typeof watchSchema === "function") {
|
|
71
|
+
/* Only call the provided func */
|
|
72
|
+
watchSchema(event);
|
|
73
|
+
|
|
74
|
+
} else if (watchSchema === "hotReloadMode") {
|
|
75
|
+
if (tsGeneratedTypesDir) {
|
|
76
|
+
/* Hot reload integration. Will only touch tsGeneratedTypesDir */
|
|
77
|
+
console.log("watchSchema: Re-writing TS schema");
|
|
78
|
+
|
|
79
|
+
await this.dboBuilder.prostgles.refreshDBO();
|
|
80
|
+
this.dboBuilder.prostgles.writeDBSchema(true);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
} else if (watchSchema) {
|
|
84
|
+
/* Full re-init. Sockets must reconnect */
|
|
85
|
+
console.log("watchSchema: Full re-initialisation", { query })
|
|
86
|
+
this.dboBuilder.prostgles.init(onReady as any, { type: "schema change", query, command });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { DboBuilder } from "../DboBuilder/DboBuilder";
|
|
2
|
+
import { OnSchemaChangeCallback } from "./SchemaWatch";
|
|
3
|
+
|
|
4
|
+
export type ValidatedWatchSchemaType =
|
|
5
|
+
| { watchType: "NONE" }
|
|
6
|
+
| { watchType: "DDL_trigger"; onChange?: OnSchemaChangeCallback; }
|
|
7
|
+
| { watchType: "prostgles_queries"; onChange?: OnSchemaChangeCallback; isFallbackFromDDL: boolean; }
|
|
8
|
+
|
|
9
|
+
export const getValidatedWatchSchemaType = (dboBuilder: DboBuilder): ValidatedWatchSchemaType => {
|
|
10
|
+
const {watchSchema, watchSchemaType, tsGeneratedTypesDir, disableRealtime } = dboBuilder.prostgles.opts;
|
|
11
|
+
if(!watchSchema) return { watchType: "NONE" };
|
|
12
|
+
|
|
13
|
+
if (watchSchema === "hotReloadMode" && !tsGeneratedTypesDir) {
|
|
14
|
+
throw "tsGeneratedTypesDir option is needed for watchSchema: hotReloadMode to work ";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const onChange = typeof watchSchema === "function"? watchSchema : undefined;
|
|
18
|
+
|
|
19
|
+
if(watchSchemaType === "DDL_trigger" || !watchSchemaType){
|
|
20
|
+
if(!dboBuilder.prostgles.isSuperUser || disableRealtime){
|
|
21
|
+
|
|
22
|
+
if(watchSchemaType === "DDL_trigger"){
|
|
23
|
+
console.error(`watchSchemaType "DDL_trigger" cannot be used because db user is not a superuser. Will fallback to watchSchemaType "prostgles_queries" `)
|
|
24
|
+
} else {
|
|
25
|
+
console.warn(`watchSchema fallback to watchSchemaType "prostgles_queries" due to ${disableRealtime? "disableRealtime setting" : "non-superuser"}`)
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
watchType: "prostgles_queries",
|
|
29
|
+
onChange,
|
|
30
|
+
isFallbackFromDDL: true
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
watchType: "DDL_trigger",
|
|
36
|
+
onChange
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
watchType: watchSchemaType,
|
|
42
|
+
isFallbackFromDDL: false,
|
|
43
|
+
onChange
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { getKeys, isObject } from "prostgles-types";
|
|
2
|
+
import { EVENT_TRIGGER_TAGS } from "../Event_Trigger_Tags";
|
|
3
|
+
import { ProstglesInitOptions } from "../ProstglesTypes";
|
|
4
|
+
|
|
5
|
+
export const getWatchSchemaTagList = (watchSchema: ProstglesInitOptions["watchSchema"]) => {
|
|
6
|
+
if(!watchSchema) return undefined;
|
|
7
|
+
|
|
8
|
+
if(watchSchema === "*"){
|
|
9
|
+
return EVENT_TRIGGER_TAGS.slice(0);
|
|
10
|
+
}
|
|
11
|
+
if (isObject(watchSchema) && typeof watchSchema !== "function"){
|
|
12
|
+
const watchSchemaKeys = getKeys(watchSchema);
|
|
13
|
+
const isInclusive = Object.values(watchSchema).every(v => v);
|
|
14
|
+
return EVENT_TRIGGER_TAGS
|
|
15
|
+
.slice(0)
|
|
16
|
+
.filter(v => {
|
|
17
|
+
const matches = watchSchemaKeys.includes(v);
|
|
18
|
+
return isInclusive? matches : !matches;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const coreTags: typeof EVENT_TRIGGER_TAGS[number][] = [
|
|
23
|
+
'COMMENT', 'CREATE TABLE', 'ALTER TABLE', 'DROP TABLE', 'CREATE VIEW',
|
|
24
|
+
'DROP VIEW', 'ALTER VIEW', 'CREATE TABLE AS', 'SELECT INTO', 'CREATE POLICY'
|
|
25
|
+
];
|
|
26
|
+
return coreTags;
|
|
27
|
+
}
|