@saltcorn/data 1.6.0-alpha.9 → 1.6.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/dist/base-plugin/actions.d.ts +3 -3
  2. package/dist/base-plugin/actions.d.ts.map +1 -1
  3. package/dist/base-plugin/actions.js +59 -17
  4. package/dist/base-plugin/actions.js.map +1 -1
  5. package/dist/base-plugin/index.d.ts +22 -8
  6. package/dist/base-plugin/index.d.ts.map +1 -1
  7. package/dist/base-plugin/types.d.ts +26 -11
  8. package/dist/base-plugin/types.d.ts.map +1 -1
  9. package/dist/base-plugin/types.js +44 -13
  10. package/dist/base-plugin/types.js.map +1 -1
  11. package/dist/base-plugin/viewtemplates/edit.d.ts.map +1 -1
  12. package/dist/base-plugin/viewtemplates/edit.js +5 -8
  13. package/dist/base-plugin/viewtemplates/edit.js.map +1 -1
  14. package/dist/base-plugin/viewtemplates/filter.d.ts.map +1 -1
  15. package/dist/base-plugin/viewtemplates/filter.js +11 -3
  16. package/dist/base-plugin/viewtemplates/filter.js.map +1 -1
  17. package/dist/base-plugin/viewtemplates/list.d.ts +2 -1
  18. package/dist/base-plugin/viewtemplates/list.d.ts.map +1 -1
  19. package/dist/base-plugin/viewtemplates/list.js +6 -1
  20. package/dist/base-plugin/viewtemplates/list.js.map +1 -1
  21. package/dist/base-plugin/viewtemplates/show.d.ts.map +1 -1
  22. package/dist/base-plugin/viewtemplates/show.js +3 -0
  23. package/dist/base-plugin/viewtemplates/show.js.map +1 -1
  24. package/dist/db/state.d.ts +4 -0
  25. package/dist/db/state.d.ts.map +1 -1
  26. package/dist/db/state.js +42 -9
  27. package/dist/db/state.js.map +1 -1
  28. package/dist/migrate.d.ts.map +1 -1
  29. package/dist/migrate.js +9 -4
  30. package/dist/migrate.js.map +1 -1
  31. package/dist/migrations/202603101553.d.ts +4 -0
  32. package/dist/migrations/202603101553.d.ts.map +1 -0
  33. package/dist/migrations/202603101553.js +22 -0
  34. package/dist/migrations/202603101553.js.map +1 -0
  35. package/dist/mobile-mocks/node/assert.d.ts +1 -0
  36. package/dist/mobile-mocks/node/assert.d.ts.map +1 -0
  37. package/dist/mobile-mocks/node/assert.js +2 -0
  38. package/dist/mobile-mocks/node/assert.js.map +1 -0
  39. package/dist/mobile-mocks/node/fs/promises.d.ts +1 -0
  40. package/dist/mobile-mocks/node/fs/promises.d.ts.map +1 -1
  41. package/dist/mobile-mocks/node/fs/promises.js +4 -0
  42. package/dist/mobile-mocks/node/fs/promises.js.map +1 -1
  43. package/dist/mobile-mocks/node/fs.d.ts +2 -0
  44. package/dist/mobile-mocks/node/fs.d.ts.map +1 -1
  45. package/dist/mobile-mocks/node/fs.js +36 -0
  46. package/dist/mobile-mocks/node/fs.js.map +1 -1
  47. package/dist/models/expression.d.ts +1 -1
  48. package/dist/models/expression.d.ts.map +1 -1
  49. package/dist/models/expression.js +51 -16
  50. package/dist/models/expression.js.map +1 -1
  51. package/dist/models/field.d.ts.map +1 -1
  52. package/dist/models/field.js +67 -10
  53. package/dist/models/field.js.map +1 -1
  54. package/dist/models/index.d.ts +1 -1
  55. package/dist/models/index.d.ts.map +1 -1
  56. package/dist/models/internal/push_message_helper.d.ts.map +1 -1
  57. package/dist/models/internal/push_message_helper.js +5 -2
  58. package/dist/models/internal/push_message_helper.js.map +1 -1
  59. package/dist/models/metadata.d.ts +2 -1
  60. package/dist/models/metadata.d.ts.map +1 -1
  61. package/dist/models/metadata.js +5 -0
  62. package/dist/models/metadata.js.map +1 -1
  63. package/dist/models/page.d.ts.map +1 -1
  64. package/dist/models/page.js +3 -1
  65. package/dist/models/page.js.map +1 -1
  66. package/dist/models/plugin.d.ts +2 -2
  67. package/dist/models/plugin.d.ts.map +1 -1
  68. package/dist/models/plugin.js +2 -2
  69. package/dist/models/plugin.js.map +1 -1
  70. package/dist/models/scheduler.d.ts.map +1 -1
  71. package/dist/models/scheduler.js +15 -5
  72. package/dist/models/scheduler.js.map +1 -1
  73. package/dist/models/table.d.ts +8 -0
  74. package/dist/models/table.d.ts.map +1 -1
  75. package/dist/models/table.js +197 -84
  76. package/dist/models/table.js.map +1 -1
  77. package/dist/models/trigger.d.ts.map +1 -1
  78. package/dist/models/trigger.js +20 -9
  79. package/dist/models/trigger.js.map +1 -1
  80. package/dist/models/user.d.ts.map +1 -1
  81. package/dist/models/user.js +4 -0
  82. package/dist/models/user.js.map +1 -1
  83. package/dist/models/workflow.d.ts.map +1 -1
  84. package/dist/models/workflow.js +6 -0
  85. package/dist/models/workflow.js.map +1 -1
  86. package/dist/plugin-helper.d.ts.map +1 -1
  87. package/dist/plugin-helper.js +4 -0
  88. package/dist/plugin-helper.js.map +1 -1
  89. package/dist/standard-menu.d.ts.map +1 -1
  90. package/dist/standard-menu.js +19 -0
  91. package/dist/standard-menu.js.map +1 -1
  92. package/dist/viewable_fields.d.ts +2 -2
  93. package/dist/viewable_fields.d.ts.map +1 -1
  94. package/dist/viewable_fields.js +13 -8
  95. package/dist/viewable_fields.js.map +1 -1
  96. package/package.json +8 -8
  97. package/webpack.config.js +1 -0
@@ -202,6 +202,14 @@ class Table {
202
202
  this.provider_name = o.provider_name;
203
203
  this.fields = o.fields.map((f) => new field_1.default(f));
204
204
  }
205
+ static subClass({ user, read_only, } = {}) {
206
+ var _a;
207
+ return _a = class extends this {
208
+ },
209
+ _a.fixed_user = user || undefined,
210
+ _a.read_only = !!read_only,
211
+ _a;
212
+ }
205
213
  get to_json() {
206
214
  return {
207
215
  name: this.name,
@@ -263,9 +271,9 @@ class Table {
263
271
  return where;
264
272
  // todo add string & number as possible types for where
265
273
  if (typeof where === "string")
266
- return Table.findOne({ name: where });
274
+ return this.findOne({ name: where });
267
275
  if (typeof where === "number")
268
- return Table.findOne({ id: where });
276
+ return this.findOne({ id: where });
269
277
  if (typeof where === "undefined")
270
278
  return null;
271
279
  if (where === null)
@@ -283,10 +291,10 @@ class Table {
283
291
  ? (v) => v.name === where.name
284
292
  : satisfies(where));
285
293
  if (tbl?.provider_name) {
286
- return new Table(structuredClone(tbl)).to_provided_table();
294
+ return new this(structuredClone(tbl)).to_provided_table();
287
295
  }
288
296
  else
289
- return tbl ? new Table(structuredClone(tbl)) : null;
297
+ return tbl ? new this(structuredClone(tbl)) : null;
290
298
  }
291
299
  /**
292
300
  * Find Tables
@@ -298,7 +306,7 @@ class Table {
298
306
  if (selectopts.cached) {
299
307
  const { getState } = require("../db/state");
300
308
  return getState()
301
- .tables.map((t) => new Table(structuredClone(t)))
309
+ .tables.map((t) => new this(structuredClone(t)))
302
310
  .filter(satisfies(where || {}));
303
311
  }
304
312
  if (where?.name) {
@@ -327,7 +335,7 @@ class Table {
327
335
  t.constraints = constraints
328
336
  .filter((f) => f.table_id === t.id)
329
337
  .map((f) => new _TableConstraint(f));
330
- const tbl = new Table(t);
338
+ const tbl = new this(t);
331
339
  return tbl.to_provided_table();
332
340
  });
333
341
  }
@@ -355,7 +363,7 @@ class Table {
355
363
  t.fields = flds
356
364
  .filter((f) => f.table_id === t.id)
357
365
  .map((f) => new field_1.default(f));
358
- return new Table(t);
366
+ return new this(t);
359
367
  });
360
368
  }
361
369
  return [...dbs, ...externals];
@@ -401,18 +409,19 @@ class Table {
401
409
  * @returns {boolean}
402
410
  */
403
411
  is_owner(user, row) {
404
- if (!user)
412
+ let use_user = this.constructor.fixed_user || user;
413
+ if (!use_user)
405
414
  return false;
406
415
  if (this.ownership_formula && this.fields) {
407
416
  const f = get_expression_function(this.ownership_formula, this.fields);
408
- return !!f(row, user);
417
+ return !!f(row, use_user);
409
418
  }
410
419
  const field_name = this.owner_fieldname();
411
420
  // users are owners of their own row in users table
412
421
  if (this.name === "users" && !field_name)
413
- return !!user.id && `${row?.id}` === `${user.id}`;
422
+ return !!use_user.id && `${row?.id}` === `${use_user.id}`;
414
423
  return (typeof field_name === "string" &&
415
- (row[field_name] === user.id || row[field_name]?.id === user.id));
424
+ (row[field_name] === use_user.id || row[field_name]?.id === use_user.id));
416
425
  }
417
426
  /**
418
427
  * get Ownership options
@@ -553,6 +562,8 @@ class Table {
553
562
  if (pk_type !== "Integer") {
554
563
  const { getState } = require("../db/state");
555
564
  const type = getState().types[pk_type];
565
+ if (!type)
566
+ throw new Error(`Cannot find primary key type ${pk_type} in fields ${JSON.stringify(fields)}`);
556
567
  pk_sql_type = type.sql_name;
557
568
  if (type.primaryKey?.default_sql)
558
569
  pk_sql_type = `${type.sql_name} default ${type.primaryKey?.default_sql}`;
@@ -568,7 +579,11 @@ class Table {
568
579
  */
569
580
  static async create(name, options = {}, //TODO not selectoptions
570
581
  id) {
571
- const { pk_type, pk_sql_type } = Table.pkSqlType(options.fields);
582
+ if (this.constructor.read_only)
583
+ throw new Error("Read-only access");
584
+ const { pk_type, pk_sql_type } = options.provider_name
585
+ ? {}
586
+ : Table.pkSqlType(options.fields);
572
587
  const schema = db_1.default.getTenantSchemaPrefix();
573
588
  // create table in database
574
589
  if (!options.provider_name)
@@ -645,6 +660,8 @@ class Table {
645
660
  * @param table
646
661
  */
647
662
  static async createInDb(table) {
663
+ if (this.constructor.read_only)
664
+ throw new Error("Read-only access");
648
665
  const is_sqlite = db_1.default.isSQLite;
649
666
  const schema = db_1.default.getTenantSchemaPrefix();
650
667
  const { pk_sql_type } = Table.pkSqlType(table.fields);
@@ -676,6 +693,8 @@ class Table {
676
693
  */
677
694
  // tbd check all other tables related to table description
678
695
  async delete(only_forget = false) {
696
+ if (this.constructor.read_only)
697
+ throw new Error("Read-only access");
679
698
  const schema = db_1.default.getTenantSchemaPrefix();
680
699
  const is_sqlite = db_1.default.isSQLite;
681
700
  await this.update({ ownership_field_id: null });
@@ -708,6 +727,8 @@ class Table {
708
727
  * Reset Sequence
709
728
  */
710
729
  async resetSequence() {
730
+ if (this.constructor.read_only)
731
+ throw new Error("Read-only access");
711
732
  const fields = this.fields;
712
733
  const pk = fields.find((f) => f.primary_key);
713
734
  if (!pk) {
@@ -726,13 +747,14 @@ class Table {
726
747
  * @param forRead
727
748
  */
728
749
  updateWhereWithOwnership(where, user, forRead) {
729
- const role = user?.role_id;
750
+ let use_user = this.constructor.fixed_user || user;
751
+ const role = use_user?.role_id;
730
752
  const min_role = forRead ? this.min_role_read : this.min_role_write;
731
753
  if (role &&
732
754
  role > min_role &&
733
755
  ((!this.ownership_field_id && !this.ownership_formula) || role === 100))
734
756
  return { notAuthorized: true };
735
- if (user &&
757
+ if (use_user &&
736
758
  role &&
737
759
  role < 100 &&
738
760
  role > min_role &&
@@ -741,16 +763,16 @@ class Table {
741
763
  if (!owner_field)
742
764
  throw new Error(`Owner field in table ${this.name} not found`);
743
765
  mergeIntoWhere(where, {
744
- [owner_field.name]: user.id,
766
+ [owner_field.name]: use_user.id,
745
767
  });
746
768
  }
747
- else if (user &&
769
+ else if (use_user &&
748
770
  role &&
749
771
  role < 100 &&
750
772
  role > min_role &&
751
773
  this.ownership_formula) {
752
774
  try {
753
- mergeIntoWhere(where, this.ownership_formula_where(user));
775
+ mergeIntoWhere(where, this.ownership_formula_where(use_user));
754
776
  }
755
777
  catch (e) {
756
778
  //ignore, ownership formula is too difficult to merge with where
@@ -759,6 +781,8 @@ class Table {
759
781
  }
760
782
  }
761
783
  async addDeleteSyncInfo(ids, timestamp) {
784
+ if (this.constructor.read_only)
785
+ throw new Error("Read-only access");
762
786
  await db_1.default.tryCatchInTransaction(async () => {
763
787
  if (ids.length > 0) {
764
788
  const schema = db_1.default.getTenantSchemaPrefix();
@@ -797,9 +821,12 @@ class Table {
797
821
  * @returns
798
822
  */
799
823
  async deleteRows(where, user, noTrigger, resultCollector) {
824
+ let use_user = this.constructor.fixed_user || user;
825
+ if (this.constructor.read_only)
826
+ throw new Error("Read-only access");
800
827
  //Fast truncate if user is admin and where is blank
801
828
  const cfields = await field_1.default.find({ reftable_name: this.name }, { cached: true });
802
- if ((!user || user?.role_id === 1) &&
829
+ if ((!use_user || use_user?.role_id === 1) &&
803
830
  Object.keys(where).length == 0 &&
804
831
  db_1.default.truncate &&
805
832
  noTrigger &&
@@ -815,7 +842,7 @@ class Table {
815
842
  // get triggers on delete
816
843
  const triggers = await trigger_1.default.getTableTriggers("Delete", this);
817
844
  const fields = this.fields;
818
- if (this.updateWhereWithOwnership(where, user)?.notAuthorized) {
845
+ if (this.updateWhereWithOwnership(where, use_user)?.notAuthorized) {
819
846
  const state = require("../db/state").getState();
820
847
  state.log(4, `Not authorized to deleteRows in table ${this.name}.`);
821
848
  return;
@@ -829,11 +856,13 @@ class Table {
829
856
  }, { cached: true });
830
857
  let rows;
831
858
  if (calc_agg_fields.length ||
832
- (user && user.role_id > this.min_role_write && this.ownership_formula)) {
859
+ (use_user &&
860
+ use_user.role_id > this.min_role_write &&
861
+ this.ownership_formula)) {
833
862
  rows = await this.getJoinedRows({
834
863
  where,
835
- forUser: user,
836
- forPublic: user?.role_id === 100,
864
+ forUser: use_user,
865
+ forPublic: use_user?.role_id === 100,
837
866
  });
838
867
  }
839
868
  const deleteFileFields = fields.filter((f) => f.type === "File" && f.attributes?.also_delete_file);
@@ -843,15 +872,15 @@ class Table {
843
872
  if (!rows)
844
873
  rows = await this.getJoinedRows({
845
874
  where,
846
- forUser: user,
847
- forPublic: user?.role_id === 100,
875
+ forUser: use_user,
876
+ forPublic: use_user?.role_id === 100,
848
877
  });
849
878
  for (const trigger of triggers) {
850
879
  for (const row of rows) {
851
880
  // run triggers on delete
852
- if (trigger.haltOnOnlyIf?.(row, user))
881
+ if (trigger.haltOnOnlyIf?.(row, use_user))
853
882
  continue;
854
- const runres = await trigger.run(row, { user });
883
+ const runres = await trigger.run(row, { user: use_user });
855
884
  if (runres && resultCollector)
856
885
  mergeActionResults(resultCollector, runres);
857
886
  }
@@ -932,7 +961,7 @@ class Table {
932
961
  if (this.fields) {
933
962
  for (const f of this.fields) {
934
963
  if (f.type && (0, common_types_1.instanceOfType)(f.type) && f.type.readFromDB)
935
- row[f.name] = f.type.readFromDB(row[f.name]);
964
+ row[f.name] = f.type.readFromDB(row[f.name], f);
936
965
  }
937
966
  }
938
967
  return row;
@@ -972,7 +1001,8 @@ class Table {
972
1001
  async getRow(where = {}, selopts = {}) {
973
1002
  const fields = this.fields;
974
1003
  const { forUser, forPublic, ...selopts1 } = selopts;
975
- const role = forUser ? forUser.role_id : forPublic ? 100 : null;
1004
+ const use_forUser = this.constructor.fixed_user || forUser;
1005
+ const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
976
1006
  this.normalise_fkey_values(where);
977
1007
  const row = await db_1.default.selectMaybeOne(this.name, where, this.processSelectOptions(selopts1));
978
1008
  if (!row || !this.fields)
@@ -985,11 +1015,11 @@ class Table {
985
1015
  const owner_field = fields.find((f) => f.id === this.ownership_field_id);
986
1016
  if (!owner_field)
987
1017
  throw new Error(`Owner field in table ${this.name} not found`);
988
- if (row[owner_field.name] !== forUser.id)
1018
+ if (row[owner_field.name] !== use_forUser.id)
989
1019
  return null;
990
1020
  }
991
1021
  else if (this.ownership_formula || this.name === "users") {
992
- if (!this.is_owner(forUser, row))
1022
+ if (!this.is_owner(use_forUser, row))
993
1023
  return null;
994
1024
  }
995
1025
  else
@@ -1027,10 +1057,10 @@ class Table {
1027
1057
  if (!this.fields)
1028
1058
  return [];
1029
1059
  const { forUser, forPublic, ...selopts1 } = selopts;
1030
- const role = forUser ? forUser.role_id : forPublic ? 100 : null;
1060
+ const use_forUser = this.constructor.fixed_user || forUser;
1061
+ const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
1031
1062
  if (role &&
1032
- this.updateWhereWithOwnership(where, forUser || { role_id: 100 }, true)
1033
- ?.notAuthorized) {
1063
+ this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
1034
1064
  return [];
1035
1065
  }
1036
1066
  this.normalise_fkey_values(where);
@@ -1043,7 +1073,8 @@ class Table {
1043
1073
  //already dealt with by changing where
1044
1074
  }
1045
1075
  else if (this.ownership_formula || this.name === "users") {
1046
- rows = rows.filter((row) => this.is_owner(forUser, row));
1076
+ if (!selopts?.disable_ownership_postqfilter)
1077
+ rows = rows.filter((row) => this.is_owner(use_forUser, row));
1047
1078
  }
1048
1079
  else
1049
1080
  return []; //no ownership
@@ -1155,6 +1186,9 @@ class Table {
1155
1186
  * @returns
1156
1187
  */
1157
1188
  async updateRow(v_in, id_in, user, noTrigger, resultCollector, restore_of_version, syncTimestamp, additionalTriggerValues, autoRecalcIterations, extraArgs) {
1189
+ let use_user = this.constructor.fixed_user || user;
1190
+ if (this.constructor.read_only)
1191
+ throw new Error("Read-only access");
1158
1192
  // migrating to options arg
1159
1193
  if (typeof noTrigger === "object") {
1160
1194
  const extraOptions = noTrigger;
@@ -1188,7 +1222,7 @@ class Table {
1188
1222
  ]);
1189
1223
  const fields = this.fields;
1190
1224
  const pk_name = this.pk_name;
1191
- const role = user?.role_id;
1225
+ const role = use_user?.role_id;
1192
1226
  const state = require("../db/state").getState();
1193
1227
  let stringified = false;
1194
1228
  const sqliteJsonCols = !isNode()
@@ -1206,7 +1240,7 @@ class Table {
1206
1240
  }
1207
1241
  if (this.ownership_formula)
1208
1242
  add_free_variables_to_joinfields(freeVariables(this.ownership_formula), joinFields, fields);
1209
- if (user &&
1243
+ if (use_user &&
1210
1244
  role &&
1211
1245
  (role > this.min_role_write || role > this.min_role_read)) {
1212
1246
  if (role === 100)
@@ -1215,19 +1249,19 @@ class Table {
1215
1249
  const owner_field = fields.find((f) => f.id === this.ownership_field_id);
1216
1250
  if (!owner_field)
1217
1251
  throw new Error(`Owner field in table ${this.name} not found`);
1218
- if (v[owner_field.name] && v[owner_field.name] != user.id) {
1219
- state.log(4, `Not authorized to updateRow in table ${this.name}. ${user.id} does not match owner field in updates`);
1252
+ if (v[owner_field.name] && v[owner_field.name] != use_user.id) {
1253
+ state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in updates`);
1220
1254
  return "Not authorized";
1221
1255
  }
1222
1256
  //need to check existing
1223
1257
  if (!existing)
1224
1258
  existing = await this.getJoinedRow({
1225
1259
  where: { [pk_name]: id },
1226
- forUser: user,
1260
+ forUser: use_user,
1227
1261
  joinFields,
1228
1262
  });
1229
- if (!existing || existing?.[owner_field.name] !== user.id) {
1230
- state.log(4, `Not authorized to updateRow in table ${this.name}. ${user.id} does not match owner field in exisiting`);
1263
+ if (!existing || existing?.[owner_field.name] !== use_user.id) {
1264
+ state.log(4, `Not authorized to updateRow in table ${this.name}. ${use_user.id} does not match owner field in exisiting`);
1231
1265
  return "Not authorized";
1232
1266
  }
1233
1267
  }
@@ -1235,11 +1269,11 @@ class Table {
1235
1269
  if (!existing)
1236
1270
  existing = await this.getJoinedRow({
1237
1271
  where: { [pk_name]: id },
1238
- forUser: user,
1272
+ forUser: use_user,
1239
1273
  joinFields,
1240
1274
  });
1241
- if (!existing || !this.is_owner(user, existing)) {
1242
- state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(user)}`);
1275
+ if (!existing || !this.is_owner(use_user, existing)) {
1276
+ state.log(4, `Not authorized to updateRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
1243
1277
  return "Not authorized";
1244
1278
  }
1245
1279
  }
@@ -1252,7 +1286,7 @@ class Table {
1252
1286
  if (!existing)
1253
1287
  existing = await this.getJoinedRow({
1254
1288
  where: { [pk_name]: id },
1255
- forUser: user,
1289
+ forUser: use_user,
1256
1290
  joinFields,
1257
1291
  });
1258
1292
  const newRow = { ...existing, ...v };
@@ -1260,8 +1294,8 @@ class Table {
1260
1294
  if (constraint_check)
1261
1295
  return constraint_check;
1262
1296
  }
1263
- if (user) {
1264
- let field_write_check = this.check_field_write_role(v, user);
1297
+ if (use_user) {
1298
+ let field_write_check = this.check_field_write_role(v, use_user);
1265
1299
  if (field_write_check)
1266
1300
  return field_write_check;
1267
1301
  }
@@ -1270,11 +1304,11 @@ class Table {
1270
1304
  if (!existing)
1271
1305
  existing = await this.getJoinedRow({
1272
1306
  where: { [pk_name]: id },
1273
- forUser: user,
1307
+ forUser: use_user,
1274
1308
  joinFields,
1275
1309
  });
1276
1310
  const valResCollector = resultCollector || {};
1277
- await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector, user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
1311
+ await trigger_1.default.runTableTriggers("Validate", this, { ...(additionalTriggerValues || {}), ...existing, ...v }, valResCollector, use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
1278
1312
  if ("error" in valResCollector)
1279
1313
  return valResCollector.error;
1280
1314
  if ("set_fields" in valResCollector)
@@ -1286,7 +1320,7 @@ class Table {
1286
1320
  let need_to_update = Object.keys(v_in).some((k) => freeVarFKFields.has(k));
1287
1321
  existing = await this.getJoinedRow({
1288
1322
  where: { [pk_name]: id },
1289
- forUser: user,
1323
+ forUser: use_user,
1290
1324
  joinFields,
1291
1325
  });
1292
1326
  let updated;
@@ -1300,7 +1334,7 @@ class Table {
1300
1334
  });
1301
1335
  updated = await this.getJoinedRow({
1302
1336
  where: { [pk_name]: id },
1303
- forUser: user,
1337
+ forUser: use_user,
1304
1338
  joinFields,
1305
1339
  });
1306
1340
  }
@@ -1334,7 +1368,7 @@ class Table {
1334
1368
  pk_name,
1335
1369
  },
1336
1370
  _time: new Date(),
1337
- _userid: user?.id,
1371
+ _userid: use_user?.id,
1338
1372
  _restore_of_version: restore_of_version || null,
1339
1373
  });
1340
1374
  }
@@ -1343,7 +1377,7 @@ class Table {
1343
1377
  if (triggers.length > 0)
1344
1378
  existing = await this.getJoinedRow({
1345
1379
  where: { [pk_name]: id },
1346
- forUser: user,
1380
+ forUser: use_user,
1347
1381
  joinFields,
1348
1382
  });
1349
1383
  }
@@ -1364,7 +1398,7 @@ class Table {
1364
1398
  if (!existing && really_changed_field_names.size && keyChanged)
1365
1399
  existing = await this.getJoinedRow({
1366
1400
  where: { [pk_name]: id },
1367
- forUser: user,
1401
+ forUser: use_user,
1368
1402
  joinFields,
1369
1403
  });
1370
1404
  await db_1.default.update(this.name, v, id, {
@@ -1387,10 +1421,12 @@ class Table {
1387
1421
  await this.auto_update_calc_aggregations(existing, !existing, (autoRecalcIterations || 0) + 1, really_changed_field_names, keyChanged);
1388
1422
  }
1389
1423
  if (!noTrigger) {
1390
- await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined : user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
1424
+ await trigger_1.default.runTableTriggers("Update", this, { ...(additionalTriggerValues || {}), ...newRow }, resultCollector, role === 100 ? undefined : use_user, { old_row: existing, updated_fields: v_in, ...(extraArgs || {}) });
1391
1425
  }
1392
1426
  }
1393
1427
  static async analyze_all_indexed_tables() {
1428
+ if (this.constructor.read_only)
1429
+ throw new Error("Read-only access");
1394
1430
  const tables = await Table.find({}, { cached: true });
1395
1431
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
1396
1432
  for (const table of tables)
@@ -1398,6 +1434,8 @@ class Table {
1398
1434
  await db_1.default.query(`analyze ${schemaPrefix}"${(0, internal_1.sqlsanitize)(table.name)}";`);
1399
1435
  }
1400
1436
  async insert_history_row(v0, retry = 0) {
1437
+ if (this.constructor.read_only)
1438
+ throw new Error("Read-only access");
1401
1439
  // sometimes there is a race condition in history inserts
1402
1440
  // https://dba.stackexchange.com/questions/212580/concurrent-transactions-result-in-race-condition-with-unique-constraint-on-inser
1403
1441
  // solution: retry 3 times, if fails run with on conflict do nothing
@@ -1452,6 +1490,8 @@ class Table {
1452
1490
  });
1453
1491
  }
1454
1492
  async insertSyncInfo(id, updates, syncTimestamp) {
1493
+ if (this.constructor.read_only)
1494
+ throw new Error("Read-only access");
1455
1495
  await db_1.default.tryCatchInTransaction(async () => {
1456
1496
  const schema = db_1.default.getTenantSchemaPrefix();
1457
1497
  if (isNode()) {
@@ -1479,6 +1519,8 @@ class Table {
1479
1519
  });
1480
1520
  }
1481
1521
  async updateSyncInfo(id, v, oldLastModified, syncTimestamp) {
1522
+ if (this.constructor.read_only)
1523
+ throw new Error("Read-only access");
1482
1524
  await db_1.default.tryCatchInTransaction(async () => {
1483
1525
  const schema = db_1.default.getTenantSchemaPrefix();
1484
1526
  if (!db_1.default.isSQLite) {
@@ -1521,8 +1563,11 @@ class Table {
1521
1563
  * @returns {Promise<{error}|{success: boolean}>}
1522
1564
  */
1523
1565
  async tryUpdateRow(v, id, user, resultCollector, extraArgs) {
1566
+ if (this.constructor.read_only)
1567
+ throw new Error("Read-only access");
1568
+ let use_user = this.constructor.fixed_user || user;
1524
1569
  try {
1525
- const maybe_err = await this.updateRow(v, id, user, {
1570
+ const maybe_err = await this.updateRow(v, id, use_user, {
1526
1571
  noTrigger: false,
1527
1572
  resultCollector,
1528
1573
  extraArgs,
@@ -1543,9 +1588,12 @@ class Table {
1543
1588
  * @returns {Promise<void>}
1544
1589
  */
1545
1590
  async toggleBool(id, field_name, user) {
1591
+ let use_user = this.constructor.fixed_user || user;
1592
+ if (this.constructor.read_only)
1593
+ throw new Error("Read-only access");
1546
1594
  const row = await this.getRow({ [this.pk_name]: id });
1547
1595
  if (row)
1548
- await this.updateRow({ [field_name]: !row[field_name] }, id, user);
1596
+ await this.updateRow({ [field_name]: !row[field_name] }, id, use_user);
1549
1597
  }
1550
1598
  delete_url(row, moreQuery) {
1551
1599
  const comppk = this.composite_pk_names;
@@ -1608,10 +1656,11 @@ class Table {
1608
1656
  * @param user
1609
1657
  */
1610
1658
  check_field_write_role(row, user) {
1659
+ let use_user = this.constructor.fixed_user || user;
1611
1660
  for (const field of this.fields) {
1612
1661
  if (typeof row[field.name] !== "undefined" &&
1613
1662
  field.attributes?.min_role_write &&
1614
- user.role_id > +field.attributes?.min_role_write)
1663
+ use_user.role_id > +field.attributes?.min_role_write)
1615
1664
  return "Not authorized";
1616
1665
  }
1617
1666
  return undefined;
@@ -1627,6 +1676,15 @@ class Table {
1627
1676
  v_in[field.name] = v_in[field.name][pk];
1628
1677
  }
1629
1678
  }
1679
+ async run_trigger(trigger_name, row, user, extraArgs) {
1680
+ let use_user = this.constructor.fixed_user || user;
1681
+ const trigger = trigger_1.default.findOne({ name: trigger_name });
1682
+ return await trigger.runWithoutRow({
1683
+ row,
1684
+ user: use_user,
1685
+ ...(extraArgs || {}),
1686
+ });
1687
+ }
1630
1688
  /**
1631
1689
  * Insert row into the table. By passing in the user as
1632
1690
  * the second argument, it will check write rights. If a user object is not
@@ -1651,6 +1709,9 @@ class Table {
1651
1709
  * @returns
1652
1710
  */
1653
1711
  async insertRow(v_in0, user, resultCollector, noTrigger, syncTimestamp) {
1712
+ let use_user = this.constructor.fixed_user || user;
1713
+ if (this.constructor.read_only)
1714
+ throw new Error("Read-only access");
1654
1715
  const v_in = { ...v_in0 };
1655
1716
  const fields = this.fields;
1656
1717
  const pk_name = this.pk_name;
@@ -1667,13 +1728,13 @@ class Table {
1667
1728
  }
1668
1729
  : {};
1669
1730
  this.normalise_fkey_values(v_in);
1670
- if (user && user.role_id > this.min_role_write) {
1731
+ if (use_user && use_user.role_id > this.min_role_write) {
1671
1732
  if (this.ownership_field_id) {
1672
1733
  const owner_field = fields.find((f) => f.id === this.ownership_field_id);
1673
1734
  if (!owner_field)
1674
1735
  throw new Error(`Owner field in table ${this.name} not found`);
1675
- if (v_in[owner_field.name] != user.id) {
1676
- state.log(4, `Not authorized to insertRow in table ${this.name}. ${user.id} does not match owner field`);
1736
+ if (v_in[owner_field.name] != use_user.id) {
1737
+ state.log(4, `Not authorized to insertRow in table ${this.name}. ${use_user.id} does not match owner field`);
1677
1738
  return;
1678
1739
  }
1679
1740
  }
@@ -1685,14 +1746,14 @@ class Table {
1685
1746
  let constraint_check = this.check_table_constraints(v_in);
1686
1747
  if (constraint_check)
1687
1748
  throw new Error(constraint_check);
1688
- if (user) {
1689
- let field_write_check = this.check_field_write_role(v_in, user);
1749
+ if (use_user) {
1750
+ let field_write_check = this.check_field_write_role(v_in, use_user);
1690
1751
  if (field_write_check)
1691
1752
  return field_write_check;
1692
1753
  }
1693
1754
  //check validate here based on v_in
1694
1755
  const valResCollector = resultCollector || {};
1695
- await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector, user);
1756
+ await trigger_1.default.runTableTriggers("Validate", this, { ...v_in }, valResCollector, use_user);
1696
1757
  if ("error" in valResCollector)
1697
1758
  return valResCollector; //???
1698
1759
  if ("set_fields" in valResCollector)
@@ -1705,7 +1766,7 @@ class Table {
1705
1766
  let existing = await this.getJoinedRows({
1706
1767
  where: { [pk_name]: id },
1707
1768
  joinFields,
1708
- forUser: user,
1769
+ forUser: use_user,
1709
1770
  });
1710
1771
  if (!existing?.[0]) {
1711
1772
  //failed ownership test
@@ -1731,15 +1792,17 @@ class Table {
1731
1792
  ...sqliteJsonCols,
1732
1793
  });
1733
1794
  }
1734
- if (user && user.role_id > this.min_role_write && this.ownership_formula) {
1795
+ if (use_user &&
1796
+ use_user.role_id > this.min_role_write &&
1797
+ this.ownership_formula) {
1735
1798
  let existing = await this.getJoinedRow({
1736
1799
  where: { [pk_name]: id },
1737
1800
  joinFields,
1738
- forUser: user,
1801
+ forUser: use_user,
1739
1802
  });
1740
- if (!existing || !this.is_owner(user, existing)) {
1803
+ if (!existing || !this.is_owner(use_user, existing)) {
1741
1804
  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(user)}`);
1805
+ state.log(4, `Not authorized to insertRow in table ${this.name}. User does not match formula: ${JSON.stringify(use_user)}`);
1743
1806
  return;
1744
1807
  }
1745
1808
  }
@@ -1751,7 +1814,7 @@ class Table {
1751
1814
  next_version_by_id: id,
1752
1815
  pk_name,
1753
1816
  },
1754
- _userid: user?.id,
1817
+ _userid: use_user?.id,
1755
1818
  _time: new Date(),
1756
1819
  });
1757
1820
  if (this.has_sync_info) {
@@ -1779,11 +1842,13 @@ class Table {
1779
1842
  await this.auto_update_calc_aggregations(newRow);
1780
1843
  if (!noTrigger) {
1781
1844
  apply_calculated_fields([newRow], this.fields);
1782
- await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector, user);
1845
+ await trigger_1.default.runTableTriggers("Insert", this, newRow, resultCollector, use_user);
1783
1846
  }
1784
1847
  return id;
1785
1848
  }
1786
1849
  async auto_update_calc_aggregations(v0, refetch, iterations = 1, changedFields, keyChanged = false) {
1850
+ if (this.constructor.read_only)
1851
+ throw new Error("Read-only access");
1787
1852
  const state = require("../db/state").getState();
1788
1853
  const pk_name = this.pk_name;
1789
1854
  state.log(6, `auto_update_calc_aggregations table=${this.name} id=${v0[pk_name]} iters=${iterations}${changedFields ? ` changedFields=${[...(changedFields || [])]}` : ""}`);
@@ -1915,8 +1980,11 @@ class Table {
1915
1980
  * @returns {Promise<{error}|{success: *}>}
1916
1981
  */
1917
1982
  async tryInsertRow(v, user, resultCollector) {
1983
+ let use_user = this.constructor.fixed_user || user;
1984
+ if (this.constructor.read_only)
1985
+ throw new Error("Read-only access");
1918
1986
  try {
1919
- const id = await this.insertRow(v, user, resultCollector);
1987
+ const id = await this.insertRow(v, use_user, resultCollector);
1920
1988
  if (!id)
1921
1989
  return { error: "Not authorized" };
1922
1990
  if (id?.includes?.("Not authorized"))
@@ -2064,6 +2132,8 @@ class Table {
2064
2132
  );`);
2065
2133
  }
2066
2134
  async create_sync_info_table() {
2135
+ if (this.constructor.read_only)
2136
+ throw new Error("Read-only access");
2067
2137
  await db_1.default.tryCatchInTransaction(async () => {
2068
2138
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
2069
2139
  const fields = this.fields;
@@ -2086,6 +2156,8 @@ class Table {
2086
2156
  });
2087
2157
  }
2088
2158
  async drop_sync_table() {
2159
+ if (this.constructor.read_only)
2160
+ throw new Error("Read-only access");
2089
2161
  await db_1.default.tryCatchInTransaction(async () => {
2090
2162
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
2091
2163
  await db_1.default.query(`
@@ -2103,6 +2175,9 @@ class Table {
2103
2175
  * @param user
2104
2176
  */
2105
2177
  async restore_row_version(id, version, user) {
2178
+ let use_user = this.constructor.fixed_user || user;
2179
+ if (this.constructor.read_only)
2180
+ throw new Error("Read-only access");
2106
2181
  const row = await db_1.default.selectOne(`${db_1.default.sqlsanitize(this.name)}__history`, {
2107
2182
  [this.pk_name]: id,
2108
2183
  _version: version,
@@ -2113,7 +2188,7 @@ class Table {
2113
2188
  r[f.name] = row[f.name];
2114
2189
  });
2115
2190
  //console.log("restore_row_version", r);
2116
- await this.updateRow(r, id, user, false, undefined, version);
2191
+ await this.updateRow(r, id, use_user, false, undefined, version);
2117
2192
  }
2118
2193
  /**
2119
2194
  * Undo row chnages
@@ -2121,6 +2196,9 @@ class Table {
2121
2196
  * @param user
2122
2197
  */
2123
2198
  async undo_row_changes(id, user) {
2199
+ let use_user = this.constructor.fixed_user || user;
2200
+ if (this.constructor.read_only)
2201
+ throw new Error("Read-only access");
2124
2202
  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
2203
  //get max that is not a restore
2126
2204
  const last_non_restore = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
@@ -2132,7 +2210,7 @@ class Table {
2132
2210
  },
2133
2211
  }, { orderBy: "_version", orderDesc: true, limit: 1 });
2134
2212
  if (last_non_restore) {
2135
- await this.restore_row_version(id, last_non_restore._version, user);
2213
+ await this.restore_row_version(id, last_non_restore._version, use_user);
2136
2214
  }
2137
2215
  }
2138
2216
  /**
@@ -2141,6 +2219,9 @@ class Table {
2141
2219
  * @param user
2142
2220
  */
2143
2221
  async redo_row_changes(id, user) {
2222
+ let use_user = this.constructor.fixed_user || user;
2223
+ if (this.constructor.read_only)
2224
+ throw new Error("Read-only access");
2144
2225
  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
2226
  if (current_version_row._restore_of_version) {
2146
2227
  const next_version = await db_1.default.selectMaybeOne(`${(0, internal_1.sqlsanitize)(this.name)}__history`, {
@@ -2150,7 +2231,7 @@ class Table {
2150
2231
  },
2151
2232
  }, { orderBy: "_version", limit: 1 });
2152
2233
  if (next_version) {
2153
- await this.restore_row_version(id, next_version._version, user);
2234
+ await this.restore_row_version(id, next_version._version, use_user);
2154
2235
  }
2155
2236
  }
2156
2237
  }
@@ -2159,6 +2240,8 @@ class Table {
2159
2240
  * with options object, or just minimal interval for legacy code
2160
2241
  */
2161
2242
  async compress_history(options) {
2243
+ if (this.constructor.read_only)
2244
+ throw new Error("Read-only access");
2162
2245
  const interval_secs = typeof options === "number" ? options : options?.interval_secs;
2163
2246
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
2164
2247
  const pk = this.pk_name;
@@ -2205,6 +2288,8 @@ class Table {
2205
2288
  */
2206
2289
  async drop_history_table() {
2207
2290
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
2291
+ if (this.constructor.read_only)
2292
+ throw new Error("Read-only access");
2208
2293
  await db_1.default.query(`
2209
2294
  drop table ${schemaPrefix}"${(0, internal_1.sqlsanitize)(this.name)}__history";`);
2210
2295
  }
@@ -2214,6 +2299,8 @@ class Table {
2214
2299
  * @returns {Promise<void>}
2215
2300
  */
2216
2301
  async rename(new_name) {
2302
+ if (this.constructor.read_only)
2303
+ throw new Error("Read-only access");
2217
2304
  //in transaction
2218
2305
  if (db_1.default.isSQLite)
2219
2306
  throw new InvalidAdminAction("Cannot rename table on SQLite");
@@ -2238,6 +2325,8 @@ class Table {
2238
2325
  * @returns {Promise<void>}
2239
2326
  */
2240
2327
  async update(new_table_rec) {
2328
+ if (this.constructor.read_only)
2329
+ throw new Error("Read-only access");
2241
2330
  if (new_table_rec.ownership_field_id === "")
2242
2331
  delete new_table_rec.ownership_field_id;
2243
2332
  const existing = Table.findOne({ id: this.id });
@@ -2290,6 +2379,8 @@ class Table {
2290
2379
  * @returns {Promise<void>}
2291
2380
  */
2292
2381
  async enable_fkey_constraints() {
2382
+ if (this.constructor.read_only)
2383
+ throw new Error("Read-only access");
2293
2384
  const fields = this.fields;
2294
2385
  for (const f of fields)
2295
2386
  await f.enable_fkey_constraint(this);
@@ -2301,6 +2392,8 @@ class Table {
2301
2392
  * @returns {Promise<{error: string}|{error: string}|{error: string}|{error: string}|{error: string}|{success: string}|{error: (string|string|*)}>}
2302
2393
  */
2303
2394
  static async create_from_csv(name, filePath) {
2395
+ if (this.constructor.read_only)
2396
+ throw new Error("Read-only access");
2304
2397
  let rows;
2305
2398
  const state = await require("../db/state").getState();
2306
2399
  try {
@@ -2472,6 +2565,9 @@ class Table {
2472
2565
  * @returns {Promise<{error: string}|{success: string}>}
2473
2566
  */
2474
2567
  async import_csv_file(filePath, options) {
2568
+ //todo user check
2569
+ if (this.constructor.read_only)
2570
+ throw new Error("Read-only access");
2475
2571
  if (typeof options === "boolean") {
2476
2572
  options = { recalc_stored: options };
2477
2573
  }
@@ -2556,6 +2652,10 @@ class Table {
2556
2652
  // start sql transaction
2557
2653
  if (!options?.no_transaction)
2558
2654
  await client.query("BEGIN");
2655
+ if (db_1.default.isSQLite)
2656
+ await client.query("PRAGMA defer_foreign_keys = ON");
2657
+ else
2658
+ await client.query("SET CONSTRAINTS ALL DEFERRED");
2559
2659
  const readStream = (0, fs_1.createReadStream)(filePath);
2560
2660
  const returnedRows = [];
2561
2661
  try {
@@ -2801,6 +2901,8 @@ ${rejectDetails}`,
2801
2901
  return v1;
2802
2902
  }
2803
2903
  async import_json_history_file(filePath) {
2904
+ if (this.constructor.read_only)
2905
+ throw new Error("Read-only access");
2804
2906
  return await (0, async_json_stream_1.default)(filePath, async (row) => {
2805
2907
  await this.insert_history_row(row);
2806
2908
  });
@@ -2812,6 +2914,8 @@ ${rejectDetails}`,
2812
2914
  * @returns {Promise<{error: string}|{success: string}>}
2813
2915
  */
2814
2916
  async import_json_file(filePath, skip_first_data_row) {
2917
+ if (this.constructor.read_only)
2918
+ throw new Error("Read-only access");
2815
2919
  const fields = this.fields;
2816
2920
  const pk_name = this.pk_name;
2817
2921
  const { readState } = require("../plugin-helper");
@@ -3087,10 +3191,10 @@ ${rejectDetails}`,
3087
3191
  async aggregationQuery(aggregations, options) {
3088
3192
  const { forUser, forPublic } = options || {};
3089
3193
  const role = forUser ? forUser.role_id : forPublic ? 100 : null;
3194
+ const use_forUser = this.constructor.fixed_user || forUser;
3090
3195
  const where = { ...(options?.where || {}) };
3091
3196
  if (role &&
3092
- this.updateWhereWithOwnership(where, forUser || { role_id: 100 }, true)
3093
- ?.notAuthorized) {
3197
+ this.updateWhereWithOwnership(where, use_forUser || { role_id: 100 }, true)?.notAuthorized) {
3094
3198
  const emptyRet = {};
3095
3199
  Object.entries(aggregations).forEach(([nm, aggObj]) => {
3096
3200
  const agg = aggObj?.aggregate?.toLowerCase?.() || "count";
@@ -3114,9 +3218,10 @@ ${rejectDetails}`,
3114
3218
  return res.rows[0];
3115
3219
  }
3116
3220
  ownership_formula_where(user) {
3221
+ let use_user = this.constructor.fixed_user || user;
3117
3222
  if (!this.ownership_formula)
3118
3223
  return {};
3119
- const wh = jsexprToWhere(this.ownership_formula, { user }, this.fields);
3224
+ const wh = jsexprToWhere(this.ownership_formula, { user: use_user }, this.fields);
3120
3225
  if (wh.eq && Array.isArray(wh.eq)) {
3121
3226
  let arr = wh.eq;
3122
3227
  for (let index = 0; index < arr.length; index++) {
@@ -3152,7 +3257,8 @@ ${rejectDetails}`,
3152
3257
  ? `"${opts.schema}".`
3153
3258
  : db_1.default.getTenantSchemaPrefix();
3154
3259
  const { forUser, forPublic } = opts;
3155
- const role = forUser ? forUser.role_id : forPublic ? 100 : null;
3260
+ const use_forUser = this.constructor.fixed_user || forUser;
3261
+ const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
3156
3262
  if (role && role > this.min_role_read && this.ownership_formula) {
3157
3263
  const freeVars = freeVariables(this.ownership_formula);
3158
3264
  add_free_variables_to_joinfields(freeVars, joinFields, fields);
@@ -3166,17 +3272,17 @@ ${rejectDetails}`,
3166
3272
  if (!owner_field)
3167
3273
  throw new Error(`Owner field in table ${this.name} not found`);
3168
3274
  mergeIntoWhere(opts.where, {
3169
- [owner_field.name]: forUser.id,
3275
+ [owner_field.name]: use_forUser.id,
3170
3276
  });
3171
3277
  }
3172
- else if (forUser &&
3278
+ else if (use_forUser &&
3173
3279
  role &&
3174
3280
  role > this.min_role_read &&
3175
3281
  this.ownership_formula) {
3176
3282
  if (forPublic || role === 100)
3177
3283
  return { notAuthorized: true }; //TODO may not be true
3178
3284
  try {
3179
- mergeIntoWhere(opts.where, this.ownership_formula_where(forUser));
3285
+ mergeIntoWhere(opts.where, this.ownership_formula_where(use_forUser));
3180
3286
  }
3181
3287
  catch (e) {
3182
3288
  //ignore, ownership formula is too difficult to merge with where
@@ -3353,7 +3459,8 @@ ${rejectDetails}`,
3353
3459
  async getJoinedRows(opts = {}) {
3354
3460
  const fields = this.fields;
3355
3461
  const { forUser, forPublic, ...selopts1 } = opts;
3356
- const role = forUser ? forUser.role_id : forPublic ? 100 : null;
3462
+ const use_forUser = this.constructor.fixed_user || forUser;
3463
+ const role = use_forUser ? use_forUser.role_id : forPublic ? 100 : null;
3357
3464
  const { sql, values, notAuthorized, joinFields, aggregations } = await this.getJoinedQuery(opts);
3358
3465
  if (notAuthorized)
3359
3466
  return [];
@@ -3394,7 +3501,7 @@ ${rejectDetails}`,
3394
3501
  //already dealt with by changing where
3395
3502
  }
3396
3503
  else if (this.ownership_formula || this.name === "users") {
3397
- calcRow = calcRow.filter((row) => this.is_owner(forUser, row));
3504
+ calcRow = calcRow.filter((row) => this.is_owner(use_forUser, row));
3398
3505
  }
3399
3506
  else
3400
3507
  return []; //no ownership
@@ -3466,6 +3573,8 @@ ${rejectDetails}`,
3466
3573
  return (0, table_helper_1.get_formula_examples)(typename, this.fields.filter((f) => !f.calculated));
3467
3574
  }
3468
3575
  async repairCompositePrimary() {
3576
+ if (this.constructor.read_only)
3577
+ throw new Error("Read-only access");
3469
3578
  const primaryKeys = this.fields.filter((f) => f.primary_key);
3470
3579
  const nonSerialPKS = primaryKeys.some((f) => f.attributes?.NonSerial);
3471
3580
  const schemaPrefix = db_1.default.getTenantSchemaPrefix();
@@ -3507,6 +3616,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
3507
3616
  await Table.state_refresh(true);
3508
3617
  }
3509
3618
  async move_include_fts_to_search_context() {
3619
+ if (this.constructor.read_only)
3620
+ throw new Error("Read-only access");
3510
3621
  const include_fts_fields = this.fields.filter((f) => f.attributes?.include_fts);
3511
3622
  if (!include_fts_fields.length)
3512
3623
  return;
@@ -3543,6 +3654,8 @@ where table_schema = '${db_1.default.getTenantSchema() || "public"}'
3543
3654
  }
3544
3655
  }
3545
3656
  }
3657
+ Table.fixed_user = undefined;
3658
+ Table.read_only = false;
3546
3659
  async function dump_table_to_json_file(filePath, tableName) {
3547
3660
  const writeStream = (0, fs_1.createWriteStream)(filePath);
3548
3661
  const client = db_1.default.isSQLite ? db_1.default : await db_1.default.getClient();