@saltcorn/data 1.6.0-alpha.9 → 1.6.0-beta.10
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/base-plugin/actions.d.ts +194 -65
- package/dist/base-plugin/actions.d.ts.map +1 -1
- package/dist/base-plugin/actions.js +220 -80
- package/dist/base-plugin/actions.js.map +1 -1
- package/dist/base-plugin/fieldviews.d.ts +1 -0
- package/dist/base-plugin/fieldviews.d.ts.map +1 -1
- package/dist/base-plugin/fieldviews.js +5 -2
- package/dist/base-plugin/fieldviews.js.map +1 -1
- package/dist/base-plugin/fileviews.d.ts +6 -0
- package/dist/base-plugin/fileviews.d.ts.map +1 -1
- package/dist/base-plugin/fileviews.js +14 -0
- package/dist/base-plugin/fileviews.js.map +1 -1
- package/dist/base-plugin/index.d.ts +220 -70
- package/dist/base-plugin/index.d.ts.map +1 -1
- package/dist/base-plugin/types.d.ts +26 -11
- package/dist/base-plugin/types.d.ts.map +1 -1
- package/dist/base-plugin/types.js +47 -13
- package/dist/base-plugin/types.js.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/edit.js +15 -11
- package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/feed.js +1 -1
- package/dist/base-plugin/viewtemplates/feed.js.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/filter.js +13 -5
- package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
- package/dist/base-plugin/viewtemplates/list.d.ts +2 -1
- package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/list.js +20 -2
- package/dist/base-plugin/viewtemplates/list.js.map +1 -1
- package/dist/base-plugin/viewtemplates/room.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/room.js +1 -1
- package/dist/base-plugin/viewtemplates/room.js.map +1 -1
- package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
- package/dist/base-plugin/viewtemplates/show.js +3 -0
- package/dist/base-plugin/viewtemplates/show.js.map +1 -1
- package/dist/db/fixtures.d.ts.map +1 -1
- package/dist/db/fixtures.js +31 -1
- package/dist/db/fixtures.js.map +1 -1
- package/dist/db/state.d.ts +9 -3
- package/dist/db/state.d.ts.map +1 -1
- package/dist/db/state.js +63 -16
- package/dist/db/state.js.map +1 -1
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +12 -5
- package/dist/migrate.js.map +1 -1
- package/dist/migrations/202603101553.d.ts +4 -0
- package/dist/migrations/202603101553.d.ts.map +1 -0
- package/dist/migrations/202603101553.js +22 -0
- package/dist/migrations/202603101553.js.map +1 -0
- package/dist/migrations/202604091531.d.ts +2 -0
- package/dist/migrations/202604091531.d.ts.map +1 -0
- package/dist/migrations/202604091531.js +9 -0
- package/dist/migrations/202604091531.js.map +1 -0
- package/dist/migrations/202604111200.d.ts +2 -0
- package/dist/migrations/202604111200.d.ts.map +1 -0
- package/dist/migrations/202604111200.js +15 -0
- package/dist/migrations/202604111200.js.map +1 -0
- package/dist/migrations/202604141200.d.ts +2 -0
- package/dist/migrations/202604141200.d.ts.map +1 -0
- package/dist/migrations/202604141200.js +14 -0
- package/dist/migrations/202604141200.js.map +1 -0
- package/dist/mobile-mocks/node/assert.d.ts +1 -0
- package/dist/mobile-mocks/node/assert.d.ts.map +1 -0
- package/dist/mobile-mocks/node/assert.js +2 -0
- package/dist/mobile-mocks/node/assert.js.map +1 -0
- package/dist/mobile-mocks/node/fs/promises.d.ts +1 -0
- package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -1
- package/dist/mobile-mocks/node/fs/promises.js +4 -0
- package/dist/mobile-mocks/node/fs/promises.js.map +1 -1
- package/dist/mobile-mocks/node/fs.d.ts +2 -0
- package/dist/mobile-mocks/node/fs.d.ts.map +1 -1
- package/dist/mobile-mocks/node/fs.js +36 -0
- package/dist/mobile-mocks/node/fs.js.map +1 -1
- package/dist/mobile-mocks/npm/npm-registry-fetch.d.ts +3 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.d.ts.map +1 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.js +3 -0
- package/dist/mobile-mocks/npm/npm-registry-fetch.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.js +2 -0
- package/dist/mobile-mocks/saltcorn/admin-models-tenant.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.js +2 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-plugin-installer.js.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.d.ts +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.d.ts.map +1 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.js +2 -0
- package/dist/mobile-mocks/saltcorn/plugins-loader-stable-versioning.js.map +1 -0
- package/dist/models/config.d.ts.map +1 -1
- package/dist/models/config.js +46 -1
- package/dist/models/config.js.map +1 -1
- package/dist/models/discovery.d.ts +14 -0
- package/dist/models/discovery.d.ts.map +1 -1
- package/dist/models/discovery.js +63 -0
- package/dist/models/discovery.js.map +1 -1
- package/dist/models/expression.d.ts +8 -5
- package/dist/models/expression.d.ts.map +1 -1
- package/dist/models/expression.js +77 -28
- package/dist/models/expression.js.map +1 -1
- package/dist/models/field.d.ts.map +1 -1
- package/dist/models/field.js +106 -19
- package/dist/models/field.js.map +1 -1
- package/dist/models/index.d.ts +13 -3
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/internal/push_message_helper.d.ts.map +1 -1
- package/dist/models/internal/push_message_helper.js +5 -2
- package/dist/models/internal/push_message_helper.js.map +1 -1
- package/dist/models/metadata.d.ts +2 -1
- package/dist/models/metadata.d.ts.map +1 -1
- package/dist/models/metadata.js +5 -0
- package/dist/models/metadata.js.map +1 -1
- package/dist/models/page.d.ts +1 -0
- package/dist/models/page.d.ts.map +1 -1
- package/dist/models/page.js +37 -19
- package/dist/models/page.js.map +1 -1
- package/dist/models/plugin.d.ts +59 -2
- package/dist/models/plugin.d.ts.map +1 -1
- package/dist/models/plugin.js +324 -3
- package/dist/models/plugin.js.map +1 -1
- package/dist/models/scheduler.d.ts.map +1 -1
- package/dist/models/scheduler.js +15 -5
- package/dist/models/scheduler.js.map +1 -1
- package/dist/models/table.d.ts +11 -1
- package/dist/models/table.d.ts.map +1 -1
- package/dist/models/table.js +344 -127
- package/dist/models/table.js.map +1 -1
- package/dist/models/trigger.d.ts +3 -1
- package/dist/models/trigger.d.ts.map +1 -1
- package/dist/models/trigger.js +38 -13
- package/dist/models/trigger.js.map +1 -1
- package/dist/models/user.d.ts.map +1 -1
- package/dist/models/user.js +5 -0
- package/dist/models/user.js.map +1 -1
- package/dist/models/view.d.ts +1 -0
- package/dist/models/view.d.ts.map +1 -1
- package/dist/models/view.js +74 -14
- package/dist/models/view.js.map +1 -1
- package/dist/models/workflow.d.ts.map +1 -1
- package/dist/models/workflow.js +6 -0
- package/dist/models/workflow.js.map +1 -1
- package/dist/models/workflow_run.d.ts.map +1 -1
- package/dist/models/workflow_run.js +3 -2
- package/dist/models/workflow_run.js.map +1 -1
- package/dist/models/workflow_step.d.ts +1 -1
- package/dist/models/workflow_step.d.ts.map +1 -1
- package/dist/models/workflow_step.js +2 -1
- package/dist/models/workflow_step.js.map +1 -1
- package/dist/plugin-helper.d.ts +3 -11
- package/dist/plugin-helper.d.ts.map +1 -1
- package/dist/plugin-helper.js +106 -60
- package/dist/plugin-helper.js.map +1 -1
- package/dist/standard-menu.d.ts.map +1 -1
- package/dist/standard-menu.js +19 -0
- package/dist/standard-menu.js.map +1 -1
- package/dist/tests/remote_query_helper.js +1 -1
- package/dist/tests/remote_query_helper.js.map +1 -1
- package/dist/utils.d.ts +15 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +102 -5
- package/dist/utils.js.map +1 -1
- package/dist/viewable_fields.d.ts +3 -3
- package/dist/viewable_fields.d.ts.map +1 -1
- package/dist/viewable_fields.js +60 -16
- package/dist/viewable_fields.js.map +1 -1
- package/dist/web-mobile-commons.d.ts.map +1 -1
- package/dist/web-mobile-commons.js +2 -1
- package/dist/web-mobile-commons.js.map +1 -1
- package/package.json +12 -9
- package/webpack.config.js +12 -0
- package/dist/tests/actions.test.d.ts +0 -2
- package/dist/tests/actions.test.d.ts.map +0 -1
- package/dist/tests/actions.test.js +0 -936
- package/dist/tests/actions.test.js.map +0 -1
- package/dist/tests/auth.test.d.ts +0 -2
- package/dist/tests/auth.test.d.ts.map +0 -1
- package/dist/tests/auth.test.js +0 -824
- package/dist/tests/auth.test.js.map +0 -1
- package/dist/tests/auxtest.test.d.ts +0 -2
- package/dist/tests/auxtest.test.d.ts.map +0 -1
- package/dist/tests/auxtest.test.js +0 -562
- package/dist/tests/auxtest.test.js.map +0 -1
- package/dist/tests/base.test.d.ts +0 -2
- package/dist/tests/base.test.d.ts.map +0 -1
- package/dist/tests/base.test.js +0 -30
- package/dist/tests/base.test.js.map +0 -1
- package/dist/tests/calc.test.d.ts +0 -2
- package/dist/tests/calc.test.d.ts.map +0 -1
- package/dist/tests/calc.test.js +0 -1081
- package/dist/tests/calc.test.js.map +0 -1
- package/dist/tests/composite_pk.test.d.ts +0 -2
- package/dist/tests/composite_pk.test.d.ts.map +0 -1
- package/dist/tests/composite_pk.test.js +0 -98
- package/dist/tests/composite_pk.test.js.map +0 -1
- package/dist/tests/config.test.d.ts +0 -2
- package/dist/tests/config.test.d.ts.map +0 -1
- package/dist/tests/config.test.js +0 -86
- package/dist/tests/config.test.js.map +0 -1
- package/dist/tests/db.test.d.ts +0 -2
- package/dist/tests/db.test.d.ts.map +0 -1
- package/dist/tests/db.test.js +0 -178
- package/dist/tests/db.test.js.map +0 -1
- package/dist/tests/discover.test.d.ts +0 -2
- package/dist/tests/discover.test.d.ts.map +0 -1
- package/dist/tests/discover.test.js +0 -245
- package/dist/tests/discover.test.js.map +0 -1
- package/dist/tests/edit.test.d.ts +0 -2
- package/dist/tests/edit.test.d.ts.map +0 -1
- package/dist/tests/edit.test.js +0 -1161
- package/dist/tests/edit.test.js.map +0 -1
- package/dist/tests/email.test.d.ts +0 -2
- package/dist/tests/email.test.d.ts.map +0 -1
- package/dist/tests/email.test.js +0 -255
- package/dist/tests/email.test.js.map +0 -1
- package/dist/tests/exact_views.test.d.ts +0 -2
- package/dist/tests/exact_views.test.d.ts.map +0 -1
- package/dist/tests/exact_views.test.js +0 -1363
- package/dist/tests/exact_views.test.js.map +0 -1
- package/dist/tests/field.test.d.ts +0 -2
- package/dist/tests/field.test.d.ts.map +0 -1
- package/dist/tests/field.test.js +0 -588
- package/dist/tests/field.test.js.map +0 -1
- package/dist/tests/fieldviews.test.d.ts +0 -2
- package/dist/tests/fieldviews.test.d.ts.map +0 -1
- package/dist/tests/fieldviews.test.js +0 -74
- package/dist/tests/fieldviews.test.js.map +0 -1
- package/dist/tests/file.test.d.ts +0 -2
- package/dist/tests/file.test.d.ts.map +0 -1
- package/dist/tests/file.test.js +0 -148
- package/dist/tests/file.test.js.map +0 -1
- package/dist/tests/filter.test.d.ts +0 -2
- package/dist/tests/filter.test.d.ts.map +0 -1
- package/dist/tests/filter.test.js +0 -496
- package/dist/tests/filter.test.js.map +0 -1
- package/dist/tests/form.test.d.ts +0 -2
- package/dist/tests/form.test.d.ts.map +0 -1
- package/dist/tests/form.test.js +0 -264
- package/dist/tests/form.test.js.map +0 -1
- package/dist/tests/list.test.d.ts +0 -2
- package/dist/tests/list.test.d.ts.map +0 -1
- package/dist/tests/list.test.js +0 -1037
- package/dist/tests/list.test.js.map +0 -1
- package/dist/tests/models.test.d.ts +0 -2
- package/dist/tests/models.test.d.ts.map +0 -1
- package/dist/tests/models.test.js +0 -417
- package/dist/tests/models.test.js.map +0 -1
- package/dist/tests/page.test.d.ts +0 -2
- package/dist/tests/page.test.d.ts.map +0 -1
- package/dist/tests/page.test.js +0 -26
- package/dist/tests/page.test.js.map +0 -1
- package/dist/tests/page_group.test.d.ts +0 -2
- package/dist/tests/page_group.test.d.ts.map +0 -1
- package/dist/tests/page_group.test.js +0 -51
- package/dist/tests/page_group.test.js.map +0 -1
- package/dist/tests/plugin.test.d.ts +0 -2
- package/dist/tests/plugin.test.d.ts.map +0 -1
- package/dist/tests/plugin.test.js +0 -60
- package/dist/tests/plugin.test.js.map +0 -1
- package/dist/tests/show.test.d.ts +0 -2
- package/dist/tests/show.test.d.ts.map +0 -1
- package/dist/tests/show.test.js +0 -561
- package/dist/tests/show.test.js.map +0 -1
- package/dist/tests/state.test.d.ts +0 -2
- package/dist/tests/state.test.d.ts.map +0 -1
- package/dist/tests/state.test.js +0 -82
- package/dist/tests/state.test.js.map +0 -1
- package/dist/tests/table.test.d.ts +0 -2
- package/dist/tests/table.test.d.ts.map +0 -1
- package/dist/tests/table.test.js +0 -2717
- package/dist/tests/table.test.js.map +0 -1
- package/dist/tests/table_history.test.d.ts +0 -2
- package/dist/tests/table_history.test.d.ts.map +0 -1
- package/dist/tests/table_history.test.js +0 -413
- package/dist/tests/table_history.test.js.map +0 -1
- package/dist/tests/tag.test.d.ts +0 -2
- package/dist/tests/tag.test.d.ts.map +0 -1
- package/dist/tests/tag.test.js +0 -97
- package/dist/tests/tag.test.js.map +0 -1
- package/dist/tests/user.test.d.ts +0 -2
- package/dist/tests/user.test.d.ts.map +0 -1
- package/dist/tests/user.test.js +0 -441
- package/dist/tests/user.test.js.map +0 -1
- package/dist/tests/view.test.d.ts +0 -2
- package/dist/tests/view.test.d.ts.map +0 -1
- package/dist/tests/view.test.js +0 -699
- package/dist/tests/view.test.js.map +0 -1
- package/dist/tests/workflow.test.d.ts +0 -2
- package/dist/tests/workflow.test.d.ts.map +0 -1
- package/dist/tests/workflow.test.js +0 -303
- package/dist/tests/workflow.test.js.map +0 -1
- package/dist/tests/workflow_run.test.d.ts +0 -2
- package/dist/tests/workflow_run.test.d.ts.map +0 -1
- package/dist/tests/workflow_run.test.js +0 -922
- package/dist/tests/workflow_run.test.js.map +0 -1
package/dist/models/table.js
CHANGED
|
@@ -47,7 +47,7 @@ const field_1 = __importDefault(require("./field"));
|
|
|
47
47
|
const common_types_1 = require("@saltcorn/types/common_types");
|
|
48
48
|
const trigger_1 = __importDefault(require("./trigger"));
|
|
49
49
|
const expression_1 = __importDefault(require("./expression"));
|
|
50
|
-
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, eval_expression, freeVariables, add_free_variables_to_joinfields, removeComments, jsexprToWhere, } = expression_1.default;
|
|
50
|
+
const { apply_calculated_fields, apply_calculated_fields_stored, recalculate_for_stored, get_expression_function, get_async_expression_function, eval_expression, freeVariables, add_free_variables_to_joinfields, removeComments, jsexprToWhere, } = expression_1.default;
|
|
51
51
|
const uuid_1 = require("uuid");
|
|
52
52
|
const csvtojson_1 = __importDefault(require("csvtojson"));
|
|
53
53
|
const moment_1 = __importDefault(require("moment"));
|
|
@@ -201,6 +201,17 @@ class Table {
|
|
|
201
201
|
this.provider_cfg = stringToJSON(o.provider_cfg);
|
|
202
202
|
this.provider_name = o.provider_name;
|
|
203
203
|
this.fields = o.fields.map((f) => new field_1.default(f));
|
|
204
|
+
this.updated_at = ["string", "number"].includes(typeof o.updated_at)
|
|
205
|
+
? new Date(o.updated_at)
|
|
206
|
+
: o.updated_at;
|
|
207
|
+
}
|
|
208
|
+
static subClass({ user, read_only, } = {}) {
|
|
209
|
+
var _a;
|
|
210
|
+
return _a = class extends this {
|
|
211
|
+
},
|
|
212
|
+
_a.fixed_user = user || undefined,
|
|
213
|
+
_a.read_only = !!read_only,
|
|
214
|
+
_a;
|
|
204
215
|
}
|
|
205
216
|
get to_json() {
|
|
206
217
|
return {
|
|
@@ -234,6 +245,7 @@ class Table {
|
|
|
234
245
|
const { fields, constraints, ...updDB } = upd_rec;
|
|
235
246
|
if (updDB.ownership_field_id === "")
|
|
236
247
|
delete updDB.ownership_field_id;
|
|
248
|
+
updDB.updated_at = new Date();
|
|
237
249
|
await db_1.default.update("_sc_tables", updDB, tbl.id);
|
|
238
250
|
//limited refresh if we do not have a client
|
|
239
251
|
if (!db_1.default.getRequestContext()?.client)
|
|
@@ -247,6 +259,7 @@ class Table {
|
|
|
247
259
|
if (!db_1.default.getRequestContext()?.client)
|
|
248
260
|
await Table.state_refresh(true);
|
|
249
261
|
};
|
|
262
|
+
//console.log({tbl});
|
|
250
263
|
return t;
|
|
251
264
|
}
|
|
252
265
|
/**
|
|
@@ -263,9 +276,9 @@ class Table {
|
|
|
263
276
|
return where;
|
|
264
277
|
// todo add string & number as possible types for where
|
|
265
278
|
if (typeof where === "string")
|
|
266
|
-
return
|
|
279
|
+
return this.findOne({ name: where });
|
|
267
280
|
if (typeof where === "number")
|
|
268
|
-
return
|
|
281
|
+
return this.findOne({ id: where });
|
|
269
282
|
if (typeof where === "undefined")
|
|
270
283
|
return null;
|
|
271
284
|
if (where === null)
|
|
@@ -283,10 +296,10 @@ class Table {
|
|
|
283
296
|
? (v) => v.name === where.name
|
|
284
297
|
: satisfies(where));
|
|
285
298
|
if (tbl?.provider_name) {
|
|
286
|
-
return new
|
|
299
|
+
return new this(structuredClone(tbl)).to_provided_table();
|
|
287
300
|
}
|
|
288
301
|
else
|
|
289
|
-
return tbl ? new
|
|
302
|
+
return tbl ? new this(structuredClone(tbl)) : null;
|
|
290
303
|
}
|
|
291
304
|
/**
|
|
292
305
|
* Find Tables
|
|
@@ -298,7 +311,7 @@ class Table {
|
|
|
298
311
|
if (selectopts.cached) {
|
|
299
312
|
const { getState } = require("../db/state");
|
|
300
313
|
return getState()
|
|
301
|
-
.tables.map((t) => new
|
|
314
|
+
.tables.map((t) => new this(structuredClone(t)))
|
|
302
315
|
.filter(satisfies(where || {}));
|
|
303
316
|
}
|
|
304
317
|
if (where?.name) {
|
|
@@ -316,7 +329,7 @@ class Table {
|
|
|
316
329
|
const { getState } = require("../db/state");
|
|
317
330
|
const provider = getState().table_providers[t.provider_name];
|
|
318
331
|
if (provider)
|
|
319
|
-
t.fields = await applyAsync(provider.fields, t.provider_cfg);
|
|
332
|
+
t.fields = await applyAsync(provider.fields, stringToJSON(t.provider_cfg));
|
|
320
333
|
else
|
|
321
334
|
t.fields = [];
|
|
322
335
|
}
|
|
@@ -327,7 +340,7 @@ class Table {
|
|
|
327
340
|
t.constraints = constraints
|
|
328
341
|
.filter((f) => f.table_id === t.id)
|
|
329
342
|
.map((f) => new _TableConstraint(f));
|
|
330
|
-
const tbl = new
|
|
343
|
+
const tbl = new this(t);
|
|
331
344
|
return tbl.to_provided_table();
|
|
332
345
|
});
|
|
333
346
|
}
|
|
@@ -355,7 +368,7 @@ class Table {
|
|
|
355
368
|
t.fields = flds
|
|
356
369
|
.filter((f) => f.table_id === t.id)
|
|
357
370
|
.map((f) => new field_1.default(f));
|
|
358
|
-
return new
|
|
371
|
+
return new this(t);
|
|
359
372
|
});
|
|
360
373
|
}
|
|
361
374
|
return [...dbs, ...externals];
|
|
@@ -401,18 +414,19 @@ class Table {
|
|
|
401
414
|
* @returns {boolean}
|
|
402
415
|
*/
|
|
403
416
|
is_owner(user, row) {
|
|
404
|
-
|
|
417
|
+
let use_user = this.constructor.fixed_user || user;
|
|
418
|
+
if (!use_user)
|
|
405
419
|
return false;
|
|
406
420
|
if (this.ownership_formula && this.fields) {
|
|
407
421
|
const f = get_expression_function(this.ownership_formula, this.fields);
|
|
408
|
-
return !!f(row,
|
|
422
|
+
return !!f(row, use_user);
|
|
409
423
|
}
|
|
410
424
|
const field_name = this.owner_fieldname();
|
|
411
425
|
// users are owners of their own row in users table
|
|
412
426
|
if (this.name === "users" && !field_name)
|
|
413
|
-
return !!
|
|
427
|
+
return !!use_user.id && `${row?.id}` === `${use_user.id}`;
|
|
414
428
|
return (typeof field_name === "string" &&
|
|
415
|
-
(row[field_name] ===
|
|
429
|
+
(row[field_name] === use_user.id || row[field_name]?.id === use_user.id));
|
|
416
430
|
}
|
|
417
431
|
/**
|
|
418
432
|
* get Ownership options
|
|
@@ -553,6 +567,8 @@ class Table {
|
|
|
553
567
|
if (pk_type !== "Integer") {
|
|
554
568
|
const { getState } = require("../db/state");
|
|
555
569
|
const type = getState().types[pk_type];
|
|
570
|
+
if (!type)
|
|
571
|
+
throw new Error(`Cannot find primary key type ${pk_type} in fields ${JSON.stringify(fields)}`);
|
|
556
572
|
pk_sql_type = type.sql_name;
|
|
557
573
|
if (type.primaryKey?.default_sql)
|
|
558
574
|
pk_sql_type = `${type.sql_name} default ${type.primaryKey?.default_sql}`;
|
|
@@ -568,7 +584,11 @@ class Table {
|
|
|
568
584
|
*/
|
|
569
585
|
static async create(name, options = {}, //TODO not selectoptions
|
|
570
586
|
id) {
|
|
571
|
-
|
|
587
|
+
if (this.constructor.read_only)
|
|
588
|
+
throw new Error("Read-only access");
|
|
589
|
+
const { pk_type, pk_sql_type } = options.provider_name
|
|
590
|
+
? {}
|
|
591
|
+
: Table.pkSqlType(options.fields);
|
|
572
592
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
573
593
|
// create table in database
|
|
574
594
|
if (!options.provider_name)
|
|
@@ -585,6 +605,7 @@ class Table {
|
|
|
585
605
|
description: options.description || "",
|
|
586
606
|
provider_name: options.provider_name,
|
|
587
607
|
provider_cfg: options.provider_cfg,
|
|
608
|
+
updated_at: new Date(),
|
|
588
609
|
};
|
|
589
610
|
let pk_fld_id;
|
|
590
611
|
if (!id) {
|
|
@@ -645,6 +666,8 @@ class Table {
|
|
|
645
666
|
* @param table
|
|
646
667
|
*/
|
|
647
668
|
static async createInDb(table) {
|
|
669
|
+
if (this.constructor.read_only)
|
|
670
|
+
throw new Error("Read-only access");
|
|
648
671
|
const is_sqlite = db_1.default.isSQLite;
|
|
649
672
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
650
673
|
const { pk_sql_type } = Table.pkSqlType(table.fields);
|
|
@@ -676,6 +699,8 @@ class Table {
|
|
|
676
699
|
*/
|
|
677
700
|
// tbd check all other tables related to table description
|
|
678
701
|
async delete(only_forget = false) {
|
|
702
|
+
if (this.constructor.read_only)
|
|
703
|
+
throw new Error("Read-only access");
|
|
679
704
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
680
705
|
const is_sqlite = db_1.default.isSQLite;
|
|
681
706
|
await this.update({ ownership_field_id: null });
|
|
@@ -708,6 +733,8 @@ class Table {
|
|
|
708
733
|
* Reset Sequence
|
|
709
734
|
*/
|
|
710
735
|
async resetSequence() {
|
|
736
|
+
if (this.constructor.read_only)
|
|
737
|
+
throw new Error("Read-only access");
|
|
711
738
|
const fields = this.fields;
|
|
712
739
|
const pk = fields.find((f) => f.primary_key);
|
|
713
740
|
if (!pk) {
|
|
@@ -726,13 +753,14 @@ class Table {
|
|
|
726
753
|
* @param forRead
|
|
727
754
|
*/
|
|
728
755
|
updateWhereWithOwnership(where, user, forRead) {
|
|
729
|
-
|
|
756
|
+
let use_user = this.constructor.fixed_user || user;
|
|
757
|
+
const role = use_user?.role_id;
|
|
730
758
|
const min_role = forRead ? this.min_role_read : this.min_role_write;
|
|
731
759
|
if (role &&
|
|
732
760
|
role > min_role &&
|
|
733
761
|
((!this.ownership_field_id && !this.ownership_formula) || role === 100))
|
|
734
762
|
return { notAuthorized: true };
|
|
735
|
-
if (
|
|
763
|
+
if (use_user &&
|
|
736
764
|
role &&
|
|
737
765
|
role < 100 &&
|
|
738
766
|
role > min_role &&
|
|
@@ -741,16 +769,16 @@ class Table {
|
|
|
741
769
|
if (!owner_field)
|
|
742
770
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
743
771
|
mergeIntoWhere(where, {
|
|
744
|
-
[owner_field.name]:
|
|
772
|
+
[owner_field.name]: use_user.id,
|
|
745
773
|
});
|
|
746
774
|
}
|
|
747
|
-
else if (
|
|
775
|
+
else if (use_user &&
|
|
748
776
|
role &&
|
|
749
777
|
role < 100 &&
|
|
750
778
|
role > min_role &&
|
|
751
779
|
this.ownership_formula) {
|
|
752
780
|
try {
|
|
753
|
-
mergeIntoWhere(where, this.ownership_formula_where(
|
|
781
|
+
mergeIntoWhere(where, this.ownership_formula_where(use_user));
|
|
754
782
|
}
|
|
755
783
|
catch (e) {
|
|
756
784
|
//ignore, ownership formula is too difficult to merge with where
|
|
@@ -758,23 +786,50 @@ class Table {
|
|
|
758
786
|
}
|
|
759
787
|
}
|
|
760
788
|
}
|
|
761
|
-
async addDeleteSyncInfo(ids, timestamp) {
|
|
789
|
+
async addDeleteSyncInfo(ids, timestamp, ownerFieldName, ownershipFormula) {
|
|
790
|
+
if (this.constructor.read_only)
|
|
791
|
+
throw new Error("Read-only access");
|
|
792
|
+
// Top-level keys referenced by the formula (strip join path suffixes,
|
|
793
|
+
// drop "user"). Stored in owner_fields so is_owner() can re-evaluate later.
|
|
794
|
+
const formulaTopKeys = ownershipFormula
|
|
795
|
+
? [
|
|
796
|
+
...new Set([...freeVariables(ownershipFormula)]
|
|
797
|
+
.map((v) => v.split(/[.Ⱶ]/)[0])
|
|
798
|
+
.filter((v) => v !== "user")),
|
|
799
|
+
]
|
|
800
|
+
: null;
|
|
762
801
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
763
802
|
if (ids.length > 0) {
|
|
764
803
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
765
804
|
const pkName = this.pk_name || "id";
|
|
766
805
|
if (isNode()) {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
806
|
+
const pkVals = ids.map((row) => String(row[pkName]));
|
|
807
|
+
await db_1.default.query(`delete from ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" where ref = ANY($1::text[])`, [pkVals]);
|
|
808
|
+
const tsParam = timestamp.valueOf() / 1000.0;
|
|
809
|
+
const insertParams = [tsParam];
|
|
810
|
+
const valueClauses = pkVals.map((pkVal, i) => {
|
|
811
|
+
const ownerVal = ownerFieldName
|
|
812
|
+
? (ids[i][ownerFieldName] ?? null)
|
|
813
|
+
: null;
|
|
814
|
+
const ownerFieldsVal = formulaTopKeys && formulaTopKeys.length > 0
|
|
815
|
+
? JSON.stringify(Object.fromEntries(formulaTopKeys.map((k) => [k, ids[i][k] ?? null])))
|
|
816
|
+
: null;
|
|
817
|
+
insertParams.push(pkVal);
|
|
818
|
+
insertParams.push(ownerVal);
|
|
819
|
+
insertParams.push(ownerFieldsVal);
|
|
820
|
+
return `($${insertParams.length - 2}::text, date_trunc('milliseconds', to_timestamp($1)), true, $${insertParams.length - 1}, $${insertParams.length})`;
|
|
821
|
+
});
|
|
822
|
+
await db_1.default.query(`insert into ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" (ref, last_modified, deleted, owner_id, owner_fields)
|
|
823
|
+
values ${valueClauses.join(",")}`, insertParams);
|
|
773
824
|
}
|
|
774
825
|
else {
|
|
826
|
+
const pkVals = ids.map((row) => String(row[pkName]));
|
|
827
|
+
const placeholders = pkVals
|
|
828
|
+
.map((_, i) => `$${i + 1}`)
|
|
829
|
+
.join(",");
|
|
775
830
|
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
776
831
|
set deleted = true, modified_local = true
|
|
777
|
-
where ref in (${
|
|
832
|
+
where ref in (${placeholders})`, pkVals);
|
|
778
833
|
}
|
|
779
834
|
}
|
|
780
835
|
}, (e) => {
|
|
@@ -797,9 +852,12 @@ class Table {
|
|
|
797
852
|
* @returns
|
|
798
853
|
*/
|
|
799
854
|
async deleteRows(where, user, noTrigger, resultCollector) {
|
|
855
|
+
let use_user = this.constructor.fixed_user || user;
|
|
856
|
+
if (this.constructor.read_only)
|
|
857
|
+
throw new Error("Read-only access");
|
|
800
858
|
//Fast truncate if user is admin and where is blank
|
|
801
859
|
const cfields = await field_1.default.find({ reftable_name: this.name }, { cached: true });
|
|
802
|
-
if ((!
|
|
860
|
+
if ((!use_user || use_user?.role_id === 1) &&
|
|
803
861
|
Object.keys(where).length == 0 &&
|
|
804
862
|
db_1.default.truncate &&
|
|
805
863
|
noTrigger &&
|
|
@@ -815,7 +873,13 @@ class Table {
|
|
|
815
873
|
// get triggers on delete
|
|
816
874
|
const triggers = await trigger_1.default.getTableTriggers("Delete", this);
|
|
817
875
|
const fields = this.fields;
|
|
818
|
-
|
|
876
|
+
const ownerFieldName = (() => {
|
|
877
|
+
if (!this.has_sync_info || !this.ownership_field_id)
|
|
878
|
+
return null;
|
|
879
|
+
const f = fields?.find((f) => f.id === this.ownership_field_id);
|
|
880
|
+
return f?.name ?? null;
|
|
881
|
+
})();
|
|
882
|
+
if (this.updateWhereWithOwnership(where, use_user)?.notAuthorized) {
|
|
819
883
|
const state = require("../db/state").getState();
|
|
820
884
|
state.log(4, `Not authorized to deleteRows in table ${this.name}.`);
|
|
821
885
|
return;
|
|
@@ -827,13 +891,23 @@ class Table {
|
|
|
827
891
|
expression: "__aggregation",
|
|
828
892
|
attributes: { json: { table: this.name } },
|
|
829
893
|
}, { cached: true });
|
|
894
|
+
// Join fields needed to snapshot ownership_formula values into sync_info.
|
|
895
|
+
// Computed once here; used either via the existing rows fetch (if it runs)
|
|
896
|
+
// or via a separate minimal fetch in the else branch below.
|
|
897
|
+
const ownershipJoinFields = {};
|
|
898
|
+
if (this.has_sync_info && this.ownership_formula) {
|
|
899
|
+
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), ownershipJoinFields, fields);
|
|
900
|
+
}
|
|
830
901
|
let rows;
|
|
831
902
|
if (calc_agg_fields.length ||
|
|
832
|
-
(
|
|
903
|
+
(use_user &&
|
|
904
|
+
use_user.role_id > this.min_role_write &&
|
|
905
|
+
this.ownership_formula)) {
|
|
833
906
|
rows = await this.getJoinedRows({
|
|
834
907
|
where,
|
|
835
|
-
forUser:
|
|
836
|
-
forPublic:
|
|
908
|
+
forUser: use_user,
|
|
909
|
+
forPublic: use_user?.role_id === 100,
|
|
910
|
+
joinFields: ownershipJoinFields,
|
|
837
911
|
});
|
|
838
912
|
}
|
|
839
913
|
const deleteFileFields = fields.filter((f) => f.type === "File" && f.attributes?.also_delete_file);
|
|
@@ -843,15 +917,15 @@ class Table {
|
|
|
843
917
|
if (!rows)
|
|
844
918
|
rows = await this.getJoinedRows({
|
|
845
919
|
where,
|
|
846
|
-
forUser:
|
|
847
|
-
forPublic:
|
|
920
|
+
forUser: use_user,
|
|
921
|
+
forPublic: use_user?.role_id === 100,
|
|
848
922
|
});
|
|
849
923
|
for (const trigger of triggers) {
|
|
850
924
|
for (const row of rows) {
|
|
851
925
|
// run triggers on delete
|
|
852
|
-
if (trigger.haltOnOnlyIf?.(row,
|
|
926
|
+
if (trigger.haltOnOnlyIf?.(row, use_user))
|
|
853
927
|
continue;
|
|
854
|
-
const runres = await trigger.run(row, { user });
|
|
928
|
+
const runres = await trigger.run(row, { user: use_user });
|
|
855
929
|
if (runres && resultCollector)
|
|
856
930
|
mergeActionResults(resultCollector, runres);
|
|
857
931
|
}
|
|
@@ -871,32 +945,47 @@ class Table {
|
|
|
871
945
|
}
|
|
872
946
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
873
947
|
if (rows) {
|
|
874
|
-
const
|
|
948
|
+
const pkIds = rows.map((r) => r[this.pk_name]);
|
|
875
949
|
if (!db_1.default.isSQLite) {
|
|
876
950
|
await db_1.default.deleteWhere(this.name, {
|
|
877
|
-
[this.pk_name]: { in:
|
|
951
|
+
[this.pk_name]: { in: pkIds },
|
|
878
952
|
});
|
|
879
953
|
}
|
|
880
954
|
else {
|
|
881
|
-
await db_1.default.query(`delete from "${db_1.default.sqlsanitize(this.name)}" where "${db_1.default.sqlsanitize(this.pk_name)}" in (${
|
|
955
|
+
await db_1.default.query(`delete from "${db_1.default.sqlsanitize(this.name)}" where "${db_1.default.sqlsanitize(this.pk_name)}" in (${pkIds.join(",")})`);
|
|
882
956
|
}
|
|
883
957
|
for (const row of rows)
|
|
884
958
|
await this.auto_update_calc_aggregations(row);
|
|
885
959
|
if (this.has_sync_info) {
|
|
886
960
|
const dbTime = await db_1.default.time();
|
|
887
|
-
await this.addDeleteSyncInfo(rows, dbTime);
|
|
961
|
+
await this.addDeleteSyncInfo(rows, dbTime, ownerFieldName, this.ownership_formula);
|
|
888
962
|
}
|
|
889
963
|
}
|
|
890
964
|
else {
|
|
891
|
-
|
|
892
|
-
? await db_1.default.select(this.name, where, {
|
|
893
|
-
fields: [this.pk_name],
|
|
894
|
-
})
|
|
895
|
-
: null;
|
|
896
|
-
await db_1.default.deleteWhere(this.name, where);
|
|
965
|
+
let preDeleteRows = null;
|
|
897
966
|
if (this.has_sync_info) {
|
|
967
|
+
if (this.ownership_formula) {
|
|
968
|
+
// ownership_formula: fetch all fields (including join fields) so
|
|
969
|
+
// addDeleteSyncInfo can snapshot them into owner_fields.
|
|
970
|
+
preDeleteRows = await this.getJoinedRows({
|
|
971
|
+
where,
|
|
972
|
+
joinFields: ownershipJoinFields,
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
else {
|
|
976
|
+
// Simple case: fetch only the pk (and owner field if present).
|
|
977
|
+
const selectFields = ownerFieldName
|
|
978
|
+
? [this.pk_name, ownerFieldName]
|
|
979
|
+
: [this.pk_name];
|
|
980
|
+
preDeleteRows = await db_1.default.select(this.name, where, {
|
|
981
|
+
fields: selectFields,
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
await db_1.default.deleteWhere(this.name, where);
|
|
986
|
+
if (this.has_sync_info && preDeleteRows) {
|
|
898
987
|
const dbTime = await db_1.default.time();
|
|
899
|
-
await this.addDeleteSyncInfo(
|
|
988
|
+
await this.addDeleteSyncInfo(preDeleteRows, dbTime, ownerFieldName, this.ownership_formula);
|
|
900
989
|
}
|
|
901
990
|
}
|
|
902
991
|
//if (fields.find((f) => f.primary_key)) await this.resetSequence();
|
|
@@ -932,7 +1021,7 @@ class Table {
|
|
|
932
1021
|
if (this.fields) {
|
|
933
1022
|
for (const f of this.fields) {
|
|
934
1023
|
if (f.type && (0, common_types_1.instanceOfType)(f.type) && f.type.readFromDB)
|
|
935
|
-
row[f.name] = f.type.readFromDB(row[f.name]);
|
|
1024
|
+
row[f.name] = f.type.readFromDB(row[f.name], f);
|
|
936
1025
|
}
|
|
937
1026
|
}
|
|
938
1027
|
return row;
|
|
@@ -972,7 +1061,8 @@ class Table {
|
|
|
972
1061
|
async getRow(where = {}, selopts = {}) {
|
|
973
1062
|
const fields = this.fields;
|
|
974
1063
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
975
|
-
const
|
|
1064
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
1065
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
976
1066
|
this.normalise_fkey_values(where);
|
|
977
1067
|
const row = await db_1.default.selectMaybeOne(this.name, where, this.processSelectOptions(selopts1));
|
|
978
1068
|
if (!row || !this.fields)
|
|
@@ -985,11 +1075,11 @@ class Table {
|
|
|
985
1075
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
986
1076
|
if (!owner_field)
|
|
987
1077
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
988
|
-
if (row[owner_field.name] !==
|
|
1078
|
+
if (row[owner_field.name] !== use_forUser.id)
|
|
989
1079
|
return null;
|
|
990
1080
|
}
|
|
991
1081
|
else if (this.ownership_formula || this.name === "users") {
|
|
992
|
-
if (!this.is_owner(
|
|
1082
|
+
if (!this.is_owner(use_forUser, row))
|
|
993
1083
|
return null;
|
|
994
1084
|
}
|
|
995
1085
|
else
|
|
@@ -1027,10 +1117,10 @@ class Table {
|
|
|
1027
1117
|
if (!this.fields)
|
|
1028
1118
|
return [];
|
|
1029
1119
|
const { forUser, forPublic, ...selopts1 } = selopts;
|
|
1030
|
-
const
|
|
1120
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
1121
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
1031
1122
|
if (role &&
|
|
1032
|
-
this.updateWhereWithOwnership(where,
|
|
1033
|
-
?.notAuthorized) {
|
|
1123
|
+
this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
|
|
1034
1124
|
return [];
|
|
1035
1125
|
}
|
|
1036
1126
|
this.normalise_fkey_values(where);
|
|
@@ -1043,7 +1133,8 @@ class Table {
|
|
|
1043
1133
|
//already dealt with by changing where
|
|
1044
1134
|
}
|
|
1045
1135
|
else if (this.ownership_formula || this.name === "users") {
|
|
1046
|
-
|
|
1136
|
+
if (!selopts?.disable_ownership_postqfilter)
|
|
1137
|
+
rows = rows.filter((row) => this.is_owner(use_forUser, row));
|
|
1047
1138
|
}
|
|
1048
1139
|
else
|
|
1049
1140
|
return []; //no ownership
|
|
@@ -1093,9 +1184,20 @@ class Table {
|
|
|
1093
1184
|
* @param fieldnm
|
|
1094
1185
|
* @returns {Promise<Object[]>}
|
|
1095
1186
|
*/
|
|
1096
|
-
async distinctValues(fieldnm, whereObj) {
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
async distinctValues(fieldnm, whereObj, user) {
|
|
1188
|
+
const useWhere = { ...(whereObj || {}) };
|
|
1189
|
+
if (user &&
|
|
1190
|
+
user.role_id > this.min_role_read &&
|
|
1191
|
+
!(this.ownership_field_id || this.ownership_formula)) {
|
|
1192
|
+
return [];
|
|
1193
|
+
}
|
|
1194
|
+
if (user &&
|
|
1195
|
+
user.role_id > this.min_role_read &&
|
|
1196
|
+
(this.ownership_field_id || this.ownership_formula)) {
|
|
1197
|
+
this.updateWhereWithOwnership(useWhere, user, true);
|
|
1198
|
+
}
|
|
1199
|
+
if (Object.keys(useWhere).length) {
|
|
1200
|
+
const { where, values } = (0, internal_1.mkWhere)(useWhere, db_1.default.isSQLite);
|
|
1099
1201
|
const res = await db_1.default.query(`select distinct "${db_1.default.sqlsanitize(fieldnm)}" from ${this.sql_name} ${where} order by "${db_1.default.sqlsanitize(fieldnm)}" limit 1000`, values);
|
|
1100
1202
|
return res.rows.map((r) => r[fieldnm]);
|
|
1101
1203
|
}
|
|
@@ -1155,6 +1257,9 @@ class Table {
|
|
|
1155
1257
|
* @returns
|
|
1156
1258
|
*/
|
|
1157
1259
|
async updateRow(v_in, id_in, user, noTrigger, resultCollector, restore_of_version, syncTimestamp, additionalTriggerValues, autoRecalcIterations, extraArgs) {
|
|
1260
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1261
|
+
if (this.constructor.read_only)
|
|
1262
|
+
throw new Error("Read-only access");
|
|
1158
1263
|
// migrating to options arg
|
|
1159
1264
|
if (typeof noTrigger === "object") {
|
|
1160
1265
|
const extraOptions = noTrigger;
|
|
@@ -1188,7 +1293,7 @@ class Table {
|
|
|
1188
1293
|
]);
|
|
1189
1294
|
const fields = this.fields;
|
|
1190
1295
|
const pk_name = this.pk_name;
|
|
1191
|
-
const role =
|
|
1296
|
+
const role = use_user?.role_id;
|
|
1192
1297
|
const state = require("../db/state").getState();
|
|
1193
1298
|
let stringified = false;
|
|
1194
1299
|
const sqliteJsonCols = !isNode()
|
|
@@ -1206,7 +1311,7 @@ class Table {
|
|
|
1206
1311
|
}
|
|
1207
1312
|
if (this.ownership_formula)
|
|
1208
1313
|
add_free_variables_to_joinfields(freeVariables(this.ownership_formula), joinFields, fields);
|
|
1209
|
-
if (
|
|
1314
|
+
if (use_user &&
|
|
1210
1315
|
role &&
|
|
1211
1316
|
(role > this.min_role_write || role > this.min_role_read)) {
|
|
1212
1317
|
if (role === 100)
|
|
@@ -1215,19 +1320,19 @@ class Table {
|
|
|
1215
1320
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1216
1321
|
if (!owner_field)
|
|
1217
1322
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1218
|
-
if (v[owner_field.name] && v[owner_field.name] !=
|
|
1219
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. ${
|
|
1323
|
+
if (v[owner_field.name] && v[owner_field.name] != use_user.id) {
|
|
1324
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in updates`);
|
|
1220
1325
|
return "Not authorized";
|
|
1221
1326
|
}
|
|
1222
1327
|
//need to check existing
|
|
1223
1328
|
if (!existing)
|
|
1224
1329
|
existing = await this.getJoinedRow({
|
|
1225
1330
|
where: { [pk_name]: id },
|
|
1226
|
-
forUser:
|
|
1331
|
+
forUser: use_user,
|
|
1227
1332
|
joinFields,
|
|
1228
1333
|
});
|
|
1229
|
-
if (!existing || existing?.[owner_field.name] !==
|
|
1230
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. ${
|
|
1334
|
+
if (!existing || existing?.[owner_field.name] !== use_user.id) {
|
|
1335
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in exisiting`);
|
|
1231
1336
|
return "Not authorized";
|
|
1232
1337
|
}
|
|
1233
1338
|
}
|
|
@@ -1235,11 +1340,11 @@ class Table {
|
|
|
1235
1340
|
if (!existing)
|
|
1236
1341
|
existing = await this.getJoinedRow({
|
|
1237
1342
|
where: { [pk_name]: id },
|
|
1238
|
-
forUser:
|
|
1343
|
+
forUser: use_user,
|
|
1239
1344
|
joinFields,
|
|
1240
1345
|
});
|
|
1241
|
-
if (!existing || !this.is_owner(
|
|
1242
|
-
state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(
|
|
1346
|
+
if (!existing || !this.is_owner(use_user, existing)) {
|
|
1347
|
+
state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
|
|
1243
1348
|
return "Not authorized";
|
|
1244
1349
|
}
|
|
1245
1350
|
}
|
|
@@ -1252,7 +1357,7 @@ class Table {
|
|
|
1252
1357
|
if (!existing)
|
|
1253
1358
|
existing = await this.getJoinedRow({
|
|
1254
1359
|
where: { [pk_name]: id },
|
|
1255
|
-
forUser:
|
|
1360
|
+
forUser: use_user,
|
|
1256
1361
|
joinFields,
|
|
1257
1362
|
});
|
|
1258
1363
|
const newRow = { ...existing, ...v };
|
|
@@ -1260,8 +1365,8 @@ class Table {
|
|
|
1260
1365
|
if (constraint_check)
|
|
1261
1366
|
return constraint_check;
|
|
1262
1367
|
}
|
|
1263
|
-
if (
|
|
1264
|
-
let field_write_check = this.check_field_write_role(v,
|
|
1368
|
+
if (use_user) {
|
|
1369
|
+
let field_write_check = this.check_field_write_role(v, use_user);
|
|
1265
1370
|
if (field_write_check)
|
|
1266
1371
|
return field_write_check;
|
|
1267
1372
|
}
|
|
@@ -1270,11 +1375,11 @@ class Table {
|
|
|
1270
1375
|
if (!existing)
|
|
1271
1376
|
existing = await this.getJoinedRow({
|
|
1272
1377
|
where: { [pk_name]: id },
|
|
1273
|
-
forUser:
|
|
1378
|
+
forUser: use_user,
|
|
1274
1379
|
joinFields,
|
|
1275
1380
|
});
|
|
1276
1381
|
const valResCollector = resultCollector || {};
|
|
1277
|
-
await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector,
|
|
1382
|
+
await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector, use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
|
|
1278
1383
|
if ("error" in valResCollector)
|
|
1279
1384
|
return valResCollector.error;
|
|
1280
1385
|
if ("set_fields" in valResCollector)
|
|
@@ -1286,7 +1391,7 @@ class Table {
|
|
|
1286
1391
|
let need_to_update = Object.keys(v_in).some((k) => freeVarFKFields.has(k));
|
|
1287
1392
|
existing = await this.getJoinedRow({
|
|
1288
1393
|
where: { [pk_name]: id },
|
|
1289
|
-
forUser:
|
|
1394
|
+
forUser: use_user,
|
|
1290
1395
|
joinFields,
|
|
1291
1396
|
});
|
|
1292
1397
|
let updated;
|
|
@@ -1300,11 +1405,11 @@ class Table {
|
|
|
1300
1405
|
});
|
|
1301
1406
|
updated = await this.getJoinedRow({
|
|
1302
1407
|
where: { [pk_name]: id },
|
|
1303
|
-
forUser:
|
|
1408
|
+
forUser: use_user,
|
|
1304
1409
|
joinFields,
|
|
1305
1410
|
});
|
|
1306
1411
|
}
|
|
1307
|
-
let calced = await apply_calculated_fields_stored(need_to_update ? updated || {} : { ...existing, ...v_in }, this.fields, this);
|
|
1412
|
+
let calced = await apply_calculated_fields_stored(need_to_update ? updated || {} : { ...existing, ...v_in }, this.fields, this, use_user);
|
|
1308
1413
|
for (const f of fields)
|
|
1309
1414
|
if (f.calculated && f.stored) {
|
|
1310
1415
|
if (typeof f.type !== "string" &&
|
|
@@ -1334,7 +1439,7 @@ class Table {
|
|
|
1334
1439
|
pk_name,
|
|
1335
1440
|
},
|
|
1336
1441
|
_time: new Date(),
|
|
1337
|
-
_userid:
|
|
1442
|
+
_userid: use_user?.id,
|
|
1338
1443
|
_restore_of_version: restore_of_version || null,
|
|
1339
1444
|
});
|
|
1340
1445
|
}
|
|
@@ -1343,7 +1448,7 @@ class Table {
|
|
|
1343
1448
|
if (triggers.length > 0)
|
|
1344
1449
|
existing = await this.getJoinedRow({
|
|
1345
1450
|
where: { [pk_name]: id },
|
|
1346
|
-
forUser:
|
|
1451
|
+
forUser: use_user,
|
|
1347
1452
|
joinFields,
|
|
1348
1453
|
});
|
|
1349
1454
|
}
|
|
@@ -1364,7 +1469,7 @@ class Table {
|
|
|
1364
1469
|
if (!existing && really_changed_field_names.size && keyChanged)
|
|
1365
1470
|
existing = await this.getJoinedRow({
|
|
1366
1471
|
where: { [pk_name]: id },
|
|
1367
|
-
forUser:
|
|
1472
|
+
forUser: use_user,
|
|
1368
1473
|
joinFields,
|
|
1369
1474
|
});
|
|
1370
1475
|
await db_1.default.update(this.name, v, id, {
|
|
@@ -1387,10 +1492,12 @@ class Table {
|
|
|
1387
1492
|
await this.auto_update_calc_aggregations(existing, !existing, (autoRecalcIterations || 0) + 1, really_changed_field_names, keyChanged);
|
|
1388
1493
|
}
|
|
1389
1494
|
if (!noTrigger) {
|
|
1390
|
-
await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined :
|
|
1495
|
+
await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined : use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
|
|
1391
1496
|
}
|
|
1392
1497
|
}
|
|
1393
1498
|
static async analyze_all_indexed_tables() {
|
|
1499
|
+
if (this.constructor.read_only)
|
|
1500
|
+
throw new Error("Read-only access");
|
|
1394
1501
|
const tables = await Table.find({}, { cached: true });
|
|
1395
1502
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1396
1503
|
for (const table of tables)
|
|
@@ -1398,6 +1505,8 @@ class Table {
|
|
|
1398
1505
|
await db_1.default.query(`analyze ${schemaPrefix}"${(0, internal_1.sqlsanitize)(table.name)}";`);
|
|
1399
1506
|
}
|
|
1400
1507
|
async insert_history_row(v0, retry = 0) {
|
|
1508
|
+
if (this.constructor.read_only)
|
|
1509
|
+
throw new Error("Read-only access");
|
|
1401
1510
|
// sometimes there is a race condition in history inserts
|
|
1402
1511
|
// https://dba.stackexchange.com/questions/212580/concurrent-transactions-result-in-race-condition-with-unique-constraint-on-inser
|
|
1403
1512
|
// solution: retry 3 times, if fails run with on conflict do nothing
|
|
@@ -1442,7 +1551,8 @@ class Table {
|
|
|
1442
1551
|
SELECT MAX(last_modified) "last_modified", ref
|
|
1443
1552
|
FROM ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1444
1553
|
GROUP BY ref HAVING ref = ($1)`;
|
|
1445
|
-
const
|
|
1554
|
+
const strIds = ids.map((id) => String(id));
|
|
1555
|
+
const result = await db_1.default.query(sql, db_1.default.isSQLite ? strIds : [strIds]);
|
|
1446
1556
|
return result.rows;
|
|
1447
1557
|
}, (e) => {
|
|
1448
1558
|
require("../db/state")
|
|
@@ -1452,6 +1562,8 @@ class Table {
|
|
|
1452
1562
|
});
|
|
1453
1563
|
}
|
|
1454
1564
|
async insertSyncInfo(id, updates, syncTimestamp) {
|
|
1565
|
+
if (this.constructor.read_only)
|
|
1566
|
+
throw new Error("Read-only access");
|
|
1455
1567
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1456
1568
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1457
1569
|
if (isNode()) {
|
|
@@ -1462,15 +1574,15 @@ class Table {
|
|
|
1462
1574
|
}
|
|
1463
1575
|
await db_1.default.query(`insert into ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info" (ref, last_modified, updated_fields)
|
|
1464
1576
|
values(
|
|
1465
|
-
$1,
|
|
1577
|
+
$1::text,
|
|
1466
1578
|
date_trunc('milliseconds', to_timestamp($2)),
|
|
1467
1579
|
$3::jsonb
|
|
1468
1580
|
)`, [id, timestamp.valueOf() / 1000.0, JSON.stringify(fieldTimestamps)]);
|
|
1469
1581
|
}
|
|
1470
1582
|
else {
|
|
1471
1583
|
await db_1.default.query(`insert into "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1472
|
-
(ref, modified_local, deleted)
|
|
1473
|
-
values(
|
|
1584
|
+
(ref, modified_local, deleted)
|
|
1585
|
+
values(CAST($1 AS TEXT), true, false)`, [id]);
|
|
1474
1586
|
}
|
|
1475
1587
|
}, (e) => {
|
|
1476
1588
|
require("../db/state")
|
|
@@ -1479,6 +1591,8 @@ class Table {
|
|
|
1479
1591
|
});
|
|
1480
1592
|
}
|
|
1481
1593
|
async updateSyncInfo(id, v, oldLastModified, syncTimestamp) {
|
|
1594
|
+
if (this.constructor.read_only)
|
|
1595
|
+
throw new Error("Read-only access");
|
|
1482
1596
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
1483
1597
|
const schema = db_1.default.getTenantSchemaPrefix();
|
|
1484
1598
|
if (!db_1.default.isSQLite) {
|
|
@@ -1489,13 +1603,13 @@ class Table {
|
|
|
1489
1603
|
for (const k of Object.keys(v).filter((key) => key !== this.pk_name)) {
|
|
1490
1604
|
fieldTimestamps[k] = timestamp;
|
|
1491
1605
|
}
|
|
1492
|
-
await db_1.default.query(`update ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1493
|
-
set
|
|
1606
|
+
await db_1.default.query(`update ${schema}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1607
|
+
set
|
|
1494
1608
|
last_modified=date_trunc('milliseconds', to_timestamp($1)),
|
|
1495
1609
|
updated_fields =
|
|
1496
1610
|
coalesce(updated_fields, '{}'::jsonb) || $4::jsonb
|
|
1497
|
-
where
|
|
1498
|
-
ref=$2 and last_modified = to_timestamp($3)`, [
|
|
1611
|
+
where
|
|
1612
|
+
ref=$2::text and last_modified = to_timestamp($3)`, [
|
|
1499
1613
|
timestamp.valueOf() / 1000.0,
|
|
1500
1614
|
id,
|
|
1501
1615
|
oldLastModified.valueOf() / 1000.0,
|
|
@@ -1503,8 +1617,8 @@ class Table {
|
|
|
1503
1617
|
]);
|
|
1504
1618
|
}
|
|
1505
1619
|
else {
|
|
1506
|
-
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info" set modified_local = true
|
|
1507
|
-
where ref = $
|
|
1620
|
+
await db_1.default.query(`update "${db_1.default.sqlsanitize(this.name)}_sync_info" set modified_local = true
|
|
1621
|
+
where ref = CAST($1 AS TEXT) and last_modified = $2`, [id, oldLastModified ? oldLastModified.valueOf() : null]);
|
|
1508
1622
|
}
|
|
1509
1623
|
}, (e) => {
|
|
1510
1624
|
require("../db/state")
|
|
@@ -1521,8 +1635,11 @@ class Table {
|
|
|
1521
1635
|
* @returns {Promise<{error}|{success: boolean}>}
|
|
1522
1636
|
*/
|
|
1523
1637
|
async tryUpdateRow(v, id, user, resultCollector, extraArgs) {
|
|
1638
|
+
if (this.constructor.read_only)
|
|
1639
|
+
throw new Error("Read-only access");
|
|
1640
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1524
1641
|
try {
|
|
1525
|
-
const maybe_err = await this.updateRow(v, id,
|
|
1642
|
+
const maybe_err = await this.updateRow(v, id, use_user, {
|
|
1526
1643
|
noTrigger: false,
|
|
1527
1644
|
resultCollector,
|
|
1528
1645
|
extraArgs,
|
|
@@ -1543,9 +1660,12 @@ class Table {
|
|
|
1543
1660
|
* @returns {Promise<void>}
|
|
1544
1661
|
*/
|
|
1545
1662
|
async toggleBool(id, field_name, user) {
|
|
1663
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1664
|
+
if (this.constructor.read_only)
|
|
1665
|
+
throw new Error("Read-only access");
|
|
1546
1666
|
const row = await this.getRow({ [this.pk_name]: id });
|
|
1547
1667
|
if (row)
|
|
1548
|
-
await this.updateRow({ [field_name]: !row[field_name] }, id,
|
|
1668
|
+
await this.updateRow({ [field_name]: !row[field_name] }, id, use_user);
|
|
1549
1669
|
}
|
|
1550
1670
|
delete_url(row, moreQuery) {
|
|
1551
1671
|
const comppk = this.composite_pk_names;
|
|
@@ -1608,10 +1728,11 @@ class Table {
|
|
|
1608
1728
|
* @param user
|
|
1609
1729
|
*/
|
|
1610
1730
|
check_field_write_role(row, user) {
|
|
1731
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1611
1732
|
for (const field of this.fields) {
|
|
1612
1733
|
if (typeof row[field.name] !== "undefined" &&
|
|
1613
1734
|
field.attributes?.min_role_write &&
|
|
1614
|
-
|
|
1735
|
+
use_user.role_id > +field.attributes?.min_role_write)
|
|
1615
1736
|
return "Not authorized";
|
|
1616
1737
|
}
|
|
1617
1738
|
return undefined;
|
|
@@ -1627,6 +1748,15 @@ class Table {
|
|
|
1627
1748
|
v_in[field.name] = v_in[field.name][pk];
|
|
1628
1749
|
}
|
|
1629
1750
|
}
|
|
1751
|
+
async run_trigger(trigger_name, row, user, extraArgs) {
|
|
1752
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1753
|
+
const trigger = trigger_1.default.findOne({ name: trigger_name });
|
|
1754
|
+
return await trigger.runWithoutRow({
|
|
1755
|
+
row,
|
|
1756
|
+
user: use_user,
|
|
1757
|
+
...(extraArgs || {}),
|
|
1758
|
+
});
|
|
1759
|
+
}
|
|
1630
1760
|
/**
|
|
1631
1761
|
* Insert row into the table. By passing in the user as
|
|
1632
1762
|
* the second argument, it will check write rights. If a user object is not
|
|
@@ -1651,6 +1781,9 @@ class Table {
|
|
|
1651
1781
|
* @returns
|
|
1652
1782
|
*/
|
|
1653
1783
|
async insertRow(v_in0, user, resultCollector, noTrigger, syncTimestamp) {
|
|
1784
|
+
let use_user = this.constructor.fixed_user || user;
|
|
1785
|
+
if (this.constructor.read_only)
|
|
1786
|
+
throw new Error("Read-only access");
|
|
1654
1787
|
const v_in = { ...v_in0 };
|
|
1655
1788
|
const fields = this.fields;
|
|
1656
1789
|
const pk_name = this.pk_name;
|
|
@@ -1667,13 +1800,13 @@ class Table {
|
|
|
1667
1800
|
}
|
|
1668
1801
|
: {};
|
|
1669
1802
|
this.normalise_fkey_values(v_in);
|
|
1670
|
-
if (
|
|
1803
|
+
if (use_user && use_user.role_id > this.min_role_write) {
|
|
1671
1804
|
if (this.ownership_field_id) {
|
|
1672
1805
|
const owner_field = fields.find((f) => f.id === this.ownership_field_id);
|
|
1673
1806
|
if (!owner_field)
|
|
1674
1807
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
1675
|
-
if (v_in[owner_field.name] !=
|
|
1676
|
-
state.log(4, `Not authorized to insertRow in table ${this.name}. ${
|
|
1808
|
+
if (v_in[owner_field.name] != use_user.id) {
|
|
1809
|
+
state.log(4, `Not authorized to insertRow in table ${this.name}. ${use_user.id} does not match owner field`);
|
|
1677
1810
|
return;
|
|
1678
1811
|
}
|
|
1679
1812
|
}
|
|
@@ -1685,27 +1818,53 @@ class Table {
|
|
|
1685
1818
|
let constraint_check = this.check_table_constraints(v_in);
|
|
1686
1819
|
if (constraint_check)
|
|
1687
1820
|
throw new Error(constraint_check);
|
|
1688
|
-
if (
|
|
1689
|
-
let field_write_check = this.check_field_write_role(v_in,
|
|
1821
|
+
if (use_user) {
|
|
1822
|
+
let field_write_check = this.check_field_write_role(v_in, use_user);
|
|
1690
1823
|
if (field_write_check)
|
|
1691
1824
|
return field_write_check;
|
|
1692
1825
|
}
|
|
1693
1826
|
//check validate here based on v_in
|
|
1694
1827
|
const valResCollector = resultCollector || {};
|
|
1695
|
-
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector,
|
|
1828
|
+
await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector, use_user);
|
|
1696
1829
|
if ("error" in valResCollector)
|
|
1697
1830
|
return valResCollector; //???
|
|
1698
1831
|
if ("set_fields" in valResCollector)
|
|
1699
1832
|
Object.assign(v_in, valResCollector.set_fields);
|
|
1833
|
+
// Apply expression defaults for fields not provided or left null by the caller
|
|
1834
|
+
for (const field of fields) {
|
|
1835
|
+
if (!field.primary_key &&
|
|
1836
|
+
field.attributes?.default_expression &&
|
|
1837
|
+
(!(field.name in v_in) || v_in[field.name] == null)) {
|
|
1838
|
+
try {
|
|
1839
|
+
const exprFn = get_async_expression_function(field.attributes.default_expression, fields, {});
|
|
1840
|
+
v_in[field.name] = await exprFn(v_in, use_user);
|
|
1841
|
+
}
|
|
1842
|
+
catch (_e) {
|
|
1843
|
+
state.log(4, `Error applying default_expression for field ${field.name}: ${_e.message}`);
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
// On mobile (SQLite), PKs with a client-side default (e.g. UUID via the
|
|
1848
|
+
// uuid-type plugin's default_js) must be generated before the insert.
|
|
1849
|
+
if (!isNode() && v_in[pk_name] == null) {
|
|
1850
|
+
const pkField = fields?.find((f) => f.primary_key && !f.is_fkey);
|
|
1851
|
+
const defaultJs = pkField?.type?.primaryKey?.default_js;
|
|
1852
|
+
if (typeof defaultJs === "function") {
|
|
1853
|
+
v_in[pk_name] = defaultJs();
|
|
1854
|
+
}
|
|
1855
|
+
}
|
|
1700
1856
|
if (Object.keys(joinFields).length > 0 ||
|
|
1701
1857
|
fields.some((f) => f.expression === "__aggregation")) {
|
|
1702
1858
|
state.log(6, `Inserting ${this.name} because join fields: ${JSON.stringify(v_in)}`);
|
|
1703
1859
|
this.prepare_row_for_writing(v_in);
|
|
1704
1860
|
id = await db_1.default.insert(this.name, v_in, { pk_name, ...sqliteJsonCols });
|
|
1861
|
+
// db.insert returns SQLite rowid, not the PK for non-integer PK types
|
|
1862
|
+
if (!isNode() && v_in[pk_name] != null)
|
|
1863
|
+
id = v_in[pk_name];
|
|
1705
1864
|
let existing = await this.getJoinedRows({
|
|
1706
1865
|
where: { [pk_name]: id },
|
|
1707
1866
|
joinFields,
|
|
1708
|
-
forUser:
|
|
1867
|
+
forUser: use_user,
|
|
1709
1868
|
});
|
|
1710
1869
|
if (!existing?.[0]) {
|
|
1711
1870
|
//failed ownership test
|
|
@@ -1714,7 +1873,7 @@ class Table {
|
|
|
1714
1873
|
state.log(4, `Not authorized to insertRow in table ${this.name}. Inserted row not retrieved.`);
|
|
1715
1874
|
return;
|
|
1716
1875
|
}
|
|
1717
|
-
let calced = await apply_calculated_fields_stored(existing[0], fields, this);
|
|
1876
|
+
let calced = await apply_calculated_fields_stored(existing[0], fields, this, use_user);
|
|
1718
1877
|
v = { ...v_in };
|
|
1719
1878
|
for (const f of fields)
|
|
1720
1879
|
if (f.calculated && f.stored)
|
|
@@ -1723,23 +1882,28 @@ class Table {
|
|
|
1723
1882
|
await db_1.default.update(this.name, v, id, { pk_name, ...sqliteJsonCols });
|
|
1724
1883
|
}
|
|
1725
1884
|
else {
|
|
1726
|
-
v = await apply_calculated_fields_stored(v_in, fields, this);
|
|
1885
|
+
v = await apply_calculated_fields_stored(v_in, fields, this, use_user);
|
|
1727
1886
|
this.prepare_row_for_writing(v);
|
|
1728
1887
|
state.log(6, `Inserting ${this.name} row: ${JSON.stringify(v)}`);
|
|
1729
1888
|
id = await db_1.default.insert(this.name, v, {
|
|
1730
1889
|
pk_name,
|
|
1731
1890
|
...sqliteJsonCols,
|
|
1732
1891
|
});
|
|
1892
|
+
// db.insert returns SQLite rowid, not the PK for non-integer PK types
|
|
1893
|
+
if (!isNode() && v[pk_name] != null)
|
|
1894
|
+
id = v[pk_name];
|
|
1733
1895
|
}
|
|
1734
|
-
if (
|
|
1896
|
+
if (use_user &&
|
|
1897
|
+
use_user.role_id > this.min_role_write &&
|
|
1898
|
+
this.ownership_formula) {
|
|
1735
1899
|
let existing = await this.getJoinedRow({
|
|
1736
1900
|
where: { [pk_name]: id },
|
|
1737
1901
|
joinFields,
|
|
1738
|
-
forUser:
|
|
1902
|
+
forUser: use_user,
|
|
1739
1903
|
});
|
|
1740
|
-
if (!existing || !this.is_owner(
|
|
1904
|
+
if (!existing || !this.is_owner(use_user, existing)) {
|
|
1741
1905
|
await this.deleteRows({ [pk_name]: id });
|
|
1742
|
-
state.log(4, `Not authorized to insertRow in table ${this.name}. User does not match formula: ${JSON.stringify(
|
|
1906
|
+
state.log(4, `Not authorized to insertRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
|
|
1743
1907
|
return;
|
|
1744
1908
|
}
|
|
1745
1909
|
}
|
|
@@ -1751,7 +1915,7 @@ class Table {
|
|
|
1751
1915
|
next_version_by_id: id,
|
|
1752
1916
|
pk_name,
|
|
1753
1917
|
},
|
|
1754
|
-
_userid:
|
|
1918
|
+
_userid: use_user?.id,
|
|
1755
1919
|
_time: new Date(),
|
|
1756
1920
|
});
|
|
1757
1921
|
if (this.has_sync_info) {
|
|
@@ -1759,15 +1923,15 @@ class Table {
|
|
|
1759
1923
|
if (isNode()) {
|
|
1760
1924
|
// sync_info for insert
|
|
1761
1925
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
1926
|
+
const tsParam = (syncTimestamp ? syncTimestamp : await db_1.default.time()).valueOf() /
|
|
1927
|
+
1000.0;
|
|
1762
1928
|
await db_1.default.query(`insert into ${schemaPrefix}"${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1763
|
-
(ref, last_modified) values(
|
|
1764
|
-
${id}, date_trunc('milliseconds', to_timestamp(${(syncTimestamp ? syncTimestamp : await db_1.default.time()).valueOf() /
|
|
1765
|
-
1000.0})))`);
|
|
1929
|
+
(ref, last_modified) values($1::text, date_trunc('milliseconds', to_timestamp($2)))`, [id, tsParam]);
|
|
1766
1930
|
}
|
|
1767
1931
|
else {
|
|
1768
1932
|
await db_1.default.query(`insert into "${db_1.default.sqlsanitize(this.name)}_sync_info"
|
|
1769
1933
|
(last_modified, ref, modified_local, deleted)
|
|
1770
|
-
values(NULL, $
|
|
1934
|
+
values(NULL, CAST($1 AS TEXT), true, false)`, [id]);
|
|
1771
1935
|
}
|
|
1772
1936
|
}, (e) => {
|
|
1773
1937
|
state.log(2, `Error inserting sync info for table ${this.name}: ${e.message}`);
|
|
@@ -1779,11 +1943,13 @@ class Table {
|
|
|
1779
1943
|
await this.auto_update_calc_aggregations(newRow);
|
|
1780
1944
|
if (!noTrigger) {
|
|
1781
1945
|
apply_calculated_fields([newRow], this.fields);
|
|
1782
|
-
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector,
|
|
1946
|
+
await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector, use_user);
|
|
1783
1947
|
}
|
|
1784
1948
|
return id;
|
|
1785
1949
|
}
|
|
1786
1950
|
async auto_update_calc_aggregations(v0, refetch, iterations = 1, changedFields, keyChanged = false) {
|
|
1951
|
+
if (this.constructor.read_only)
|
|
1952
|
+
throw new Error("Read-only access");
|
|
1787
1953
|
const state = require("../db/state").getState();
|
|
1788
1954
|
const pk_name = this.pk_name;
|
|
1789
1955
|
state.log(6, `auto_update_calc_aggregations table=${this.name} id=${v0[pk_name]} iters=${iterations}${changedFields ? ` changedFields=${[...(changedFields || [])]}` : ""}`);
|
|
@@ -1915,8 +2081,11 @@ class Table {
|
|
|
1915
2081
|
* @returns {Promise<{error}|{success: *}>}
|
|
1916
2082
|
*/
|
|
1917
2083
|
async tryInsertRow(v, user, resultCollector) {
|
|
2084
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2085
|
+
if (this.constructor.read_only)
|
|
2086
|
+
throw new Error("Read-only access");
|
|
1918
2087
|
try {
|
|
1919
|
-
const id = await this.insertRow(v,
|
|
2088
|
+
const id = await this.insertRow(v, use_user, resultCollector);
|
|
1920
2089
|
if (!id)
|
|
1921
2090
|
return { error: "Not authorized" };
|
|
1922
2091
|
if (id?.includes?.("Not authorized"))
|
|
@@ -2064,6 +2233,8 @@ class Table {
|
|
|
2064
2233
|
);`);
|
|
2065
2234
|
}
|
|
2066
2235
|
async create_sync_info_table() {
|
|
2236
|
+
if (this.constructor.read_only)
|
|
2237
|
+
throw new Error("Read-only access");
|
|
2067
2238
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2068
2239
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2069
2240
|
const fields = this.fields;
|
|
@@ -2072,10 +2243,12 @@ class Table {
|
|
|
2072
2243
|
throw new Error("Unable to find a field with a primary key.");
|
|
2073
2244
|
}
|
|
2074
2245
|
await db_1.default.query(`create table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info" (
|
|
2075
|
-
ref
|
|
2246
|
+
ref text not null,
|
|
2076
2247
|
last_modified timestamp,
|
|
2077
2248
|
deleted boolean default false,
|
|
2078
|
-
updated_fields jsonb
|
|
2249
|
+
updated_fields jsonb,
|
|
2250
|
+
owner_id integer,
|
|
2251
|
+
owner_fields jsonb)`);
|
|
2079
2252
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_ref_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(ref)`);
|
|
2080
2253
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_lm_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(last_modified)`);
|
|
2081
2254
|
await db_1.default.query(`create index "${(0, internal_1.sqlsanitize)(this.name)}_sync_info_deleted_index" on ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}_sync_info"(deleted)`);
|
|
@@ -2086,6 +2259,8 @@ class Table {
|
|
|
2086
2259
|
});
|
|
2087
2260
|
}
|
|
2088
2261
|
async drop_sync_table() {
|
|
2262
|
+
if (this.constructor.read_only)
|
|
2263
|
+
throw new Error("Read-only access");
|
|
2089
2264
|
await db_1.default.tryCatchInTransaction(async () => {
|
|
2090
2265
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2091
2266
|
await db_1.default.query(`
|
|
@@ -2103,6 +2278,9 @@ class Table {
|
|
|
2103
2278
|
* @param user
|
|
2104
2279
|
*/
|
|
2105
2280
|
async restore_row_version(id, version, user) {
|
|
2281
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2282
|
+
if (this.constructor.read_only)
|
|
2283
|
+
throw new Error("Read-only access");
|
|
2106
2284
|
const row = await db_1.default.selectOne(`${db_1.default.sqlsanitize(this.name)}__history`, {
|
|
2107
2285
|
[this.pk_name]: id,
|
|
2108
2286
|
_version: version,
|
|
@@ -2113,7 +2291,7 @@ class Table {
|
|
|
2113
2291
|
r[f.name] = row[f.name];
|
|
2114
2292
|
});
|
|
2115
2293
|
//console.log("restore_row_version", r);
|
|
2116
|
-
await this.updateRow(r, id,
|
|
2294
|
+
await this.updateRow(r, id, use_user, false, undefined, version);
|
|
2117
2295
|
}
|
|
2118
2296
|
/**
|
|
2119
2297
|
* Undo row chnages
|
|
@@ -2121,6 +2299,9 @@ class Table {
|
|
|
2121
2299
|
* @param user
|
|
2122
2300
|
*/
|
|
2123
2301
|
async undo_row_changes(id, user) {
|
|
2302
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2303
|
+
if (this.constructor.read_only)
|
|
2304
|
+
throw new Error("Read-only access");
|
|
2124
2305
|
const current_version_row = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, { [this.pk_name]: id }, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2125
2306
|
//get max that is not a restore
|
|
2126
2307
|
const last_non_restore = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2132,7 +2313,7 @@ class Table {
|
|
|
2132
2313
|
},
|
|
2133
2314
|
}, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2134
2315
|
if (last_non_restore) {
|
|
2135
|
-
await this.restore_row_version(id, last_non_restore._version,
|
|
2316
|
+
await this.restore_row_version(id, last_non_restore._version, use_user);
|
|
2136
2317
|
}
|
|
2137
2318
|
}
|
|
2138
2319
|
/**
|
|
@@ -2141,6 +2322,9 @@ class Table {
|
|
|
2141
2322
|
* @param user
|
|
2142
2323
|
*/
|
|
2143
2324
|
async redo_row_changes(id, user) {
|
|
2325
|
+
let use_user = this.constructor.fixed_user || user;
|
|
2326
|
+
if (this.constructor.read_only)
|
|
2327
|
+
throw new Error("Read-only access");
|
|
2144
2328
|
const current_version_row = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, { [this.pk_name]: id }, { orderBy: "_version", orderDesc: true, limit: 1 });
|
|
2145
2329
|
if (current_version_row._restore_of_version) {
|
|
2146
2330
|
const next_version = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
|
|
@@ -2150,7 +2334,7 @@ class Table {
|
|
|
2150
2334
|
},
|
|
2151
2335
|
}, { orderBy: "_version", limit: 1 });
|
|
2152
2336
|
if (next_version) {
|
|
2153
|
-
await this.restore_row_version(id, next_version._version,
|
|
2337
|
+
await this.restore_row_version(id, next_version._version, use_user);
|
|
2154
2338
|
}
|
|
2155
2339
|
}
|
|
2156
2340
|
}
|
|
@@ -2159,6 +2343,8 @@ class Table {
|
|
|
2159
2343
|
* with options object, or just minimal interval for legacy code
|
|
2160
2344
|
*/
|
|
2161
2345
|
async compress_history(options) {
|
|
2346
|
+
if (this.constructor.read_only)
|
|
2347
|
+
throw new Error("Read-only access");
|
|
2162
2348
|
const interval_secs = typeof options === "number" ? options : options?.interval_secs;
|
|
2163
2349
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2164
2350
|
const pk = this.pk_name;
|
|
@@ -2205,6 +2391,8 @@ class Table {
|
|
|
2205
2391
|
*/
|
|
2206
2392
|
async drop_history_table() {
|
|
2207
2393
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
2394
|
+
if (this.constructor.read_only)
|
|
2395
|
+
throw new Error("Read-only access");
|
|
2208
2396
|
await db_1.default.query(`
|
|
2209
2397
|
drop table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history";`);
|
|
2210
2398
|
}
|
|
@@ -2214,6 +2402,8 @@ class Table {
|
|
|
2214
2402
|
* @returns {Promise<void>}
|
|
2215
2403
|
*/
|
|
2216
2404
|
async rename(new_name) {
|
|
2405
|
+
if (this.constructor.read_only)
|
|
2406
|
+
throw new Error("Read-only access");
|
|
2217
2407
|
//in transaction
|
|
2218
2408
|
if (db_1.default.isSQLite)
|
|
2219
2409
|
throw new InvalidAdminAction("Cannot rename table on SQLite");
|
|
@@ -2238,6 +2428,8 @@ class Table {
|
|
|
2238
2428
|
* @returns {Promise<void>}
|
|
2239
2429
|
*/
|
|
2240
2430
|
async update(new_table_rec) {
|
|
2431
|
+
if (this.constructor.read_only)
|
|
2432
|
+
throw new Error("Read-only access");
|
|
2241
2433
|
if (new_table_rec.ownership_field_id === "")
|
|
2242
2434
|
delete new_table_rec.ownership_field_id;
|
|
2243
2435
|
const existing = Table.findOne({ id: this.id });
|
|
@@ -2245,6 +2437,7 @@ class Table {
|
|
|
2245
2437
|
throw new Error(`Unable to find table with id: ${this.id}`);
|
|
2246
2438
|
}
|
|
2247
2439
|
const { external, fields, constraints, ...upd_rec } = new_table_rec;
|
|
2440
|
+
upd_rec.updated_at = new Date();
|
|
2248
2441
|
await db_1.default.update("_sc_tables", upd_rec, this.id);
|
|
2249
2442
|
//limited refresh if we do not have a client
|
|
2250
2443
|
if (!db_1.default.getRequestContext()?.client)
|
|
@@ -2290,6 +2483,8 @@ class Table {
|
|
|
2290
2483
|
* @returns {Promise<void>}
|
|
2291
2484
|
*/
|
|
2292
2485
|
async enable_fkey_constraints() {
|
|
2486
|
+
if (this.constructor.read_only)
|
|
2487
|
+
throw new Error("Read-only access");
|
|
2293
2488
|
const fields = this.fields;
|
|
2294
2489
|
for (const f of fields)
|
|
2295
2490
|
await f.enable_fkey_constraint(this);
|
|
@@ -2301,6 +2496,8 @@ class Table {
|
|
|
2301
2496
|
* @returns {Promise<{error: string}|{error: string}|{error: string}|{error: string}|{error: string}|{success: string}|{error: (string|string|*)}>}
|
|
2302
2497
|
*/
|
|
2303
2498
|
static async create_from_csv(name, filePath) {
|
|
2499
|
+
if (this.constructor.read_only)
|
|
2500
|
+
throw new Error("Read-only access");
|
|
2304
2501
|
let rows;
|
|
2305
2502
|
const state = await require("../db/state").getState();
|
|
2306
2503
|
try {
|
|
@@ -2472,6 +2669,9 @@ class Table {
|
|
|
2472
2669
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2473
2670
|
*/
|
|
2474
2671
|
async import_csv_file(filePath, options) {
|
|
2672
|
+
//todo user check
|
|
2673
|
+
if (this.constructor.read_only)
|
|
2674
|
+
throw new Error("Read-only access");
|
|
2475
2675
|
if (typeof options === "boolean") {
|
|
2476
2676
|
options = { recalc_stored: options };
|
|
2477
2677
|
}
|
|
@@ -2556,6 +2756,10 @@ class Table {
|
|
|
2556
2756
|
// start sql transaction
|
|
2557
2757
|
if (!options?.no_transaction)
|
|
2558
2758
|
await client.query("BEGIN");
|
|
2759
|
+
if (db_1.default.isSQLite)
|
|
2760
|
+
await client.query("PRAGMA defer_foreign_keys = ON");
|
|
2761
|
+
else
|
|
2762
|
+
await client.query("SET CONSTRAINTS ALL DEFERRED");
|
|
2559
2763
|
const readStream = (0, fs_1.createReadStream)(filePath);
|
|
2560
2764
|
const returnedRows = [];
|
|
2561
2765
|
try {
|
|
@@ -2801,6 +3005,8 @@ ${rejectDetails}`,
|
|
|
2801
3005
|
return v1;
|
|
2802
3006
|
}
|
|
2803
3007
|
async import_json_history_file(filePath) {
|
|
3008
|
+
if (this.constructor.read_only)
|
|
3009
|
+
throw new Error("Read-only access");
|
|
2804
3010
|
return await (0, async_json_stream_1.default)(filePath, async (row) => {
|
|
2805
3011
|
await this.insert_history_row(row);
|
|
2806
3012
|
});
|
|
@@ -2812,6 +3018,8 @@ ${rejectDetails}`,
|
|
|
2812
3018
|
* @returns {Promise<{error: string}|{success: string}>}
|
|
2813
3019
|
*/
|
|
2814
3020
|
async import_json_file(filePath, skip_first_data_row) {
|
|
3021
|
+
if (this.constructor.read_only)
|
|
3022
|
+
throw new Error("Read-only access");
|
|
2815
3023
|
const fields = this.fields;
|
|
2816
3024
|
const pk_name = this.pk_name;
|
|
2817
3025
|
const { readState } = require("../plugin-helper");
|
|
@@ -3087,10 +3295,10 @@ ${rejectDetails}`,
|
|
|
3087
3295
|
async aggregationQuery(aggregations, options) {
|
|
3088
3296
|
const { forUser, forPublic } = options || {};
|
|
3089
3297
|
const role = forUser ? forUser.role_id : forPublic ? 100 : null;
|
|
3298
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3090
3299
|
const where = { ...(options?.where || {}) };
|
|
3091
3300
|
if (role &&
|
|
3092
|
-
this.updateWhereWithOwnership(where,
|
|
3093
|
-
?.notAuthorized) {
|
|
3301
|
+
this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
|
|
3094
3302
|
const emptyRet = {};
|
|
3095
3303
|
Object.entries(aggregations).forEach(([nm, aggObj]) => {
|
|
3096
3304
|
const agg = aggObj?.aggregate?.toLowerCase?.() || "count";
|
|
@@ -3114,9 +3322,10 @@ ${rejectDetails}`,
|
|
|
3114
3322
|
return res.rows[0];
|
|
3115
3323
|
}
|
|
3116
3324
|
ownership_formula_where(user) {
|
|
3325
|
+
let use_user = this.constructor.fixed_user || user;
|
|
3117
3326
|
if (!this.ownership_formula)
|
|
3118
3327
|
return {};
|
|
3119
|
-
const wh = jsexprToWhere(this.ownership_formula, { user }, this.fields);
|
|
3328
|
+
const wh = jsexprToWhere(this.ownership_formula, { user: use_user }, this.fields);
|
|
3120
3329
|
if (wh.eq && Array.isArray(wh.eq)) {
|
|
3121
3330
|
let arr = wh.eq;
|
|
3122
3331
|
for (let index = 0; index < arr.length; index++) {
|
|
@@ -3152,7 +3361,8 @@ ${rejectDetails}`,
|
|
|
3152
3361
|
? `"${opts.schema}".`
|
|
3153
3362
|
: db_1.default.getTenantSchemaPrefix();
|
|
3154
3363
|
const { forUser, forPublic } = opts;
|
|
3155
|
-
const
|
|
3364
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3365
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3156
3366
|
if (role && role > this.min_role_read && this.ownership_formula) {
|
|
3157
3367
|
const freeVars = freeVariables(this.ownership_formula);
|
|
3158
3368
|
add_free_variables_to_joinfields(freeVars, joinFields, fields);
|
|
@@ -3166,17 +3376,17 @@ ${rejectDetails}`,
|
|
|
3166
3376
|
if (!owner_field)
|
|
3167
3377
|
throw new Error(`Owner field in table ${this.name} not found`);
|
|
3168
3378
|
mergeIntoWhere(opts.where, {
|
|
3169
|
-
[owner_field.name]:
|
|
3379
|
+
[owner_field.name]: use_forUser.id,
|
|
3170
3380
|
});
|
|
3171
3381
|
}
|
|
3172
|
-
else if (
|
|
3382
|
+
else if (use_forUser &&
|
|
3173
3383
|
role &&
|
|
3174
3384
|
role > this.min_role_read &&
|
|
3175
3385
|
this.ownership_formula) {
|
|
3176
3386
|
if (forPublic || role === 100)
|
|
3177
3387
|
return { notAuthorized: true }; //TODO may not be true
|
|
3178
3388
|
try {
|
|
3179
|
-
mergeIntoWhere(opts.where, this.ownership_formula_where(
|
|
3389
|
+
mergeIntoWhere(opts.where, this.ownership_formula_where(use_forUser));
|
|
3180
3390
|
}
|
|
3181
3391
|
catch (e) {
|
|
3182
3392
|
//ignore, ownership formula is too difficult to merge with where
|
|
@@ -3353,7 +3563,8 @@ ${rejectDetails}`,
|
|
|
3353
3563
|
async getJoinedRows(opts = {}) {
|
|
3354
3564
|
const fields = this.fields;
|
|
3355
3565
|
const { forUser, forPublic, ...selopts1 } = opts;
|
|
3356
|
-
const
|
|
3566
|
+
const use_forUser = this.constructor.fixed_user || forUser;
|
|
3567
|
+
const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
|
|
3357
3568
|
const { sql, values, notAuthorized, joinFields, aggregations } = await this.getJoinedQuery(opts);
|
|
3358
3569
|
if (notAuthorized)
|
|
3359
3570
|
return [];
|
|
@@ -3394,7 +3605,7 @@ ${rejectDetails}`,
|
|
|
3394
3605
|
//already dealt with by changing where
|
|
3395
3606
|
}
|
|
3396
3607
|
else if (this.ownership_formula || this.name === "users") {
|
|
3397
|
-
calcRow = calcRow.filter((row) => this.is_owner(
|
|
3608
|
+
calcRow = calcRow.filter((row) => this.is_owner(use_forUser, row));
|
|
3398
3609
|
}
|
|
3399
3610
|
else
|
|
3400
3611
|
return []; //no ownership
|
|
@@ -3466,6 +3677,8 @@ ${rejectDetails}`,
|
|
|
3466
3677
|
return (0, table_helper_1.get_formula_examples)(typename, this.fields.filter((f) => !f.calculated));
|
|
3467
3678
|
}
|
|
3468
3679
|
async repairCompositePrimary() {
|
|
3680
|
+
if (this.constructor.read_only)
|
|
3681
|
+
throw new Error("Read-only access");
|
|
3469
3682
|
const primaryKeys = this.fields.filter((f) => f.primary_key);
|
|
3470
3683
|
const nonSerialPKS = primaryKeys.some((f) => f.attributes?.NonSerial);
|
|
3471
3684
|
const schemaPrefix = db_1.default.getTenantSchemaPrefix();
|
|
@@ -3507,6 +3720,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3507
3720
|
await Table.state_refresh(true);
|
|
3508
3721
|
}
|
|
3509
3722
|
async move_include_fts_to_search_context() {
|
|
3723
|
+
if (this.constructor.read_only)
|
|
3724
|
+
throw new Error("Read-only access");
|
|
3510
3725
|
const include_fts_fields = this.fields.filter((f) => f.attributes?.include_fts);
|
|
3511
3726
|
if (!include_fts_fields.length)
|
|
3512
3727
|
return;
|
|
@@ -3543,6 +3758,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
|
|
|
3543
3758
|
}
|
|
3544
3759
|
}
|
|
3545
3760
|
}
|
|
3761
|
+
Table.fixed_user = undefined;
|
|
3762
|
+
Table.read_only = false;
|
|
3546
3763
|
async function dump_table_to_json_file(filePath, tableName) {
|
|
3547
3764
|
const writeStream = (0, fs_1.createWriteStream)(filePath);
|
|
3548
3765
|
const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();
|