adminforth 1.1.73 → 1.1.75

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 (122) hide show
  1. package/auth.ts +14 -0
  2. package/dataConnectors/baseConnector.ts +107 -0
  3. package/dataConnectors/mongo.ts +6 -12
  4. package/dataConnectors/postgres.ts +17 -37
  5. package/dataConnectors/sqlite.ts +44 -55
  6. package/dist/auth.js +7 -0
  7. package/dist/dataConnectors/baseConnector.js +90 -0
  8. package/dist/dataConnectors/mongo.js +6 -12
  9. package/dist/dataConnectors/postgres.js +12 -33
  10. package/dist/dataConnectors/sqlite.js +67 -83
  11. package/dist/index.js +125 -66
  12. package/dist/modules/codeInjector.js +29 -15
  13. package/dist/plugins/AuditLogPlugin/index.js +2 -17
  14. package/dist/plugins/S3UploadPlugin/custom/s3uploader.vue +120 -0
  15. package/dist/plugins/S3UploadPlugin/index.js +103 -0
  16. package/dist/plugins/S3UploadPlugin/package-lock.json +431 -0
  17. package/dist/plugins/S3UploadPlugin/package.json +15 -0
  18. package/dist/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsConfirmation.vue +152 -0
  19. package/dist/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsSetup.vue +188 -0
  20. package/dist/plugins/TwoFactorsAuthPlugin/index.js +150 -0
  21. package/dist/plugins/TwoFactorsAuthPlugin/types.js +1 -0
  22. package/dist/spa/index.html +1 -1
  23. package/dist/spa/package-lock.json +111 -0
  24. package/dist/spa/package.json +1 -0
  25. package/dist/spa/src/App.vue +178 -133
  26. package/dist/spa/src/components/MenuLink.vue +3 -3
  27. package/dist/spa/src/components/ResourceForm.vue +96 -96
  28. package/dist/spa/src/components/ResourceListTable.vue +69 -84
  29. package/dist/spa/src/components/ValueRenderer.vue +2 -3
  30. package/dist/spa/src/composables/useStores.ts +24 -11
  31. package/dist/spa/src/main.ts +1 -1
  32. package/dist/spa/src/router/index.ts +0 -1
  33. package/dist/spa/src/utils.ts +1 -1
  34. package/dist/spa/src/views/CreateView.vue +1 -9
  35. package/dist/spa/src/views/EditView.vue +1 -9
  36. package/dist/spa/src/views/ListView.vue +12 -3
  37. package/dist/spa/src/views/LoginView.vue +6 -1
  38. package/dist/spa/src/views/ResourceParent.vue +1 -2
  39. package/dist/spa/src/views/ShowView.vue +6 -14
  40. package/dist/spa/tailwind.config.js +3 -1
  41. package/index.ts +103 -36
  42. package/modules/codeInjector.ts +38 -27
  43. package/package.json +2 -1
  44. package/plugins/AuditLogPlugin/index.ts +4 -20
  45. package/plugins/S3UploadPlugin/custom/s3uploader.vue +120 -0
  46. package/plugins/S3UploadPlugin/index.ts +112 -0
  47. package/plugins/S3UploadPlugin/package-lock.json +431 -0
  48. package/plugins/S3UploadPlugin/package.json +15 -0
  49. package/plugins/S3UploadPlugin/types.ts +76 -0
  50. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsConfirmation.vue +152 -0
  51. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsOtp.vue +0 -0
  52. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsSetup.vue +188 -0
  53. package/plugins/TwoFactorsAuthPlugin/index.ts +150 -0
  54. package/plugins/TwoFactorsAuthPlugin/types.ts +10 -0
  55. package/servers/express.ts +1 -0
  56. package/spa/src/App.vue +154 -121
  57. package/spa/src/components/ResourceForm.vue +91 -91
  58. package/spa/src/router/index.ts +0 -1
  59. package/spa/src/utils.ts +1 -1
  60. package/spa/src/views/CreateView.vue +0 -1
  61. package/spa/src/views/LoginView.vue +4 -1
  62. package/types/AdminForthConfig.ts +166 -5
  63. package/dist/plugins/AuditLog/index.js +0 -13
  64. package/dist/spa/spa/.eslintrc.cjs +0 -14
  65. package/dist/spa/spa/.vscode/extensions.json +0 -6
  66. package/dist/spa/spa/README.md +0 -39
  67. package/dist/spa/spa/env.d.ts +0 -1
  68. package/dist/spa/spa/index.html +0 -23
  69. package/dist/spa/spa/package-lock.json +0 -4470
  70. package/dist/spa/spa/package.json +0 -48
  71. package/dist/spa/spa/postcss.config.js +0 -6
  72. package/dist/spa/spa/public/assets/favicon.png +0 -0
  73. package/dist/spa/spa/src/App.vue +0 -283
  74. package/dist/spa/spa/src/assets/logo.svg +0 -19
  75. package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
  76. package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
  77. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  78. package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
  79. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
  80. package/dist/spa/spa/src/components/CustomRangePicker.vue +0 -148
  81. package/dist/spa/spa/src/components/Dropdown.vue +0 -168
  82. package/dist/spa/spa/src/components/Filters.vue +0 -211
  83. package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
  84. package/dist/spa/spa/src/components/MenuLink.vue +0 -24
  85. package/dist/spa/spa/src/components/ResourceForm.vue +0 -265
  86. package/dist/spa/spa/src/components/ResourceListTable.vue +0 -404
  87. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
  88. package/dist/spa/spa/src/components/Toast.vue +0 -66
  89. package/dist/spa/spa/src/components/ValueRenderer.vue +0 -59
  90. package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
  91. package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
  92. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
  93. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
  94. package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
  95. package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
  96. package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
  97. package/dist/spa/spa/src/composables/useFrontendApi.ts +0 -26
  98. package/dist/spa/spa/src/composables/useStores.ts +0 -126
  99. package/dist/spa/spa/src/index.scss +0 -26
  100. package/dist/spa/spa/src/main.ts +0 -18
  101. package/dist/spa/spa/src/router/index.ts +0 -64
  102. package/dist/spa/spa/src/spa_types/core.ts +0 -51
  103. package/dist/spa/spa/src/stores/core.ts +0 -144
  104. package/dist/spa/spa/src/stores/filters.ts +0 -22
  105. package/dist/spa/spa/src/stores/modal.ts +0 -48
  106. package/dist/spa/spa/src/stores/toast.ts +0 -15
  107. package/dist/spa/spa/src/stores/user.ts +0 -54
  108. package/dist/spa/spa/src/utils.ts +0 -101
  109. package/dist/spa/spa/src/views/CreateView.vue +0 -156
  110. package/dist/spa/spa/src/views/EditView.vue +0 -157
  111. package/dist/spa/spa/src/views/ListView.vue +0 -266
  112. package/dist/spa/spa/src/views/LoginView.vue +0 -136
  113. package/dist/spa/spa/src/views/ResourceParent.vue +0 -17
  114. package/dist/spa/spa/src/views/ShowView.vue +0 -180
  115. package/dist/spa/spa/tailwind.config.js +0 -17
  116. package/dist/spa/spa/tsconfig.app.json +0 -14
  117. package/dist/spa/spa/tsconfig.json +0 -11
  118. package/dist/spa/spa/tsconfig.node.json +0 -19
  119. package/dist/spa/spa/vite.config.ts +0 -49
  120. /package/dist/plugins/{AuditLog → S3UploadPlugin}/types.js +0 -0
  121. /package/dist/{spa/spa/src/assets/base.css → plugins/TwoFactorsAuthPlugin/custom/TwoFactorsOtp.vue} +0 -0
  122. /package/dist/spa/{spa/src → src}/components/SkeleteLoader.vue +0 -0
package/auth.ts CHANGED
@@ -37,6 +37,20 @@ class AdminForthAuth {
37
37
  response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
38
38
  }
39
39
 
40
+ removeCustomCookie({response, name}) {
41
+ response.setHeader('Set-Cookie', `adminforth_test'='2'; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
42
+ }
43
+
44
+ setCustomCookie({ response, payload }: {
45
+ response: any, payload: {name: string, value: string, expiry: number, httpOnly: boolean}
46
+ }) {
47
+ const {name,value,expiry,httpOnly} = payload
48
+ response.setHeader('Set-Cookie', `adminforth_${name}=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + expiry).toUTCString() } `);
49
+ }
50
+
51
+
52
+
53
+
40
54
  issueJWT(payload: Object, type: string) {
41
55
  // read ADMINFORH_SECRET from environment if not drop error
42
56
  const secret = process.env.ADMINFORTH_SECRET;
@@ -0,0 +1,107 @@
1
+ import { AdminForthResource, AdminForthDataSourceConnectorBase, AdminForthSortDirections, AdminForthFilterOperators, AdminForthResourceColumn } from "../types/AdminForthConfig.js";
2
+
3
+
4
+ export default class AdminForthBaseConnector implements AdminForthDataSourceConnectorBase {
5
+ getPrimaryKey(resource: AdminForthResource): string {
6
+ for (const col of resource.dataSourceColumns) {
7
+ if (col.primaryKey) {
8
+ return col.name;
9
+ }
10
+ }
11
+ }
12
+
13
+ getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, id: string): Promise<any> {
14
+ throw new Error('Method not implemented.');
15
+ }
16
+
17
+ getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
18
+ resource: AdminForthResource,
19
+ limit: number,
20
+ offset: number,
21
+ sort: { field: string, direction: AdminForthSortDirections }[],
22
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
23
+ }): Promise<{ data: any[], total: number }> {
24
+ throw new Error('Method not implemented.');
25
+ }
26
+
27
+ discoverFields(resource: AdminForthResource): Promise<{ [key: string]: AdminForthResourceColumn; }> {
28
+ throw new Error('Method not implemented.');
29
+ }
30
+
31
+ getFieldValue(field: AdminForthResourceColumn, value: any) {
32
+ throw new Error('Method not implemented.');
33
+ }
34
+
35
+ setFieldValue(field: AdminForthResourceColumn, value: any) {
36
+ throw new Error('Method not implemented.');
37
+ }
38
+
39
+ getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
40
+ throw new Error('Method not implemented.');
41
+ }
42
+
43
+ createRecord({ resource, record }: { resource: AdminForthResource; record: any; }): Promise<void> {
44
+ throw new Error('Method not implemented.');
45
+ }
46
+
47
+ updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource; recordId: string; newValues: any; }): Promise<void> {
48
+ throw new Error('Method not implemented.');
49
+ }
50
+
51
+ deleteRecord({ resource, recordId }: { resource: AdminForthResource; recordId: string; }): Promise<void> {
52
+ throw new Error('Method not implemented.');
53
+ }
54
+
55
+
56
+ async getData({ resource, limit, offset, sort, filters }: {
57
+ resource: AdminForthResource,
58
+ limit: number,
59
+ offset: number,
60
+ sort: { field: string, direction: AdminForthSortDirections }[],
61
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
62
+ }): Promise<{ data: any[], total: number }> {
63
+ if (filters) {
64
+ filters.map((f) => {
65
+ if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
66
+ f.value = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
67
+ } else {
68
+ f.value = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
69
+ }
70
+ });
71
+ }
72
+
73
+ const d = await this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters });
74
+ // call getFieldValue for each field
75
+ d.data.map((record) => {
76
+ for (const col of resource.dataSourceColumns) {
77
+ record[col.name] = this.getFieldValue(col, record[col.name]);
78
+ }
79
+ });
80
+
81
+ return d;
82
+ }
83
+
84
+ async getMinMaxForColumns({ resource, columns }: { resource: AdminForthResource; columns: AdminForthResourceColumn[]; }): Promise<{ [key: string]: { min: any; max: any; }; }> {
85
+ const mm = await this.getMinMaxForColumnsWithOriginalTypes({ resource, columns });
86
+ const result = {};
87
+ for (const col of columns) {
88
+ result[col.name] = {
89
+ min: this.getFieldValue(col, mm[col.name].min),
90
+ max: this.getFieldValue(col, mm[col.name].max),
91
+ };
92
+ }
93
+ return result;
94
+ }
95
+
96
+ getRecordByPrimaryKey(resource: AdminForthResource, recordId: string): Promise<any> {
97
+ return this.getRecordByPrimaryKeyWithOriginalTypes(resource, recordId).then((record) => {
98
+ const newRecord = {};
99
+ for (const col of resource.dataSourceColumns) {
100
+ newRecord[col.name] = this.getFieldValue(col, record[col.name]);
101
+ }
102
+ return newRecord;
103
+ });
104
+ }
105
+
106
+
107
+ }
@@ -83,7 +83,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
83
83
  return value;
84
84
  }
85
85
 
86
- async getRecordByPrimaryKey(resource, key) {
86
+ async getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
87
87
  const tableName = resource.table;
88
88
  const collection = this.db.db().collection(tableName);
89
89
  const row = await collection.findOne({ [this.getPrimaryKey(resource)]: key });
@@ -97,7 +97,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
97
97
  continue; // should I continue or throw an error?
98
98
  throw new Error(`Resource '${resource.table}' has no column '${key_1}' defined`);
99
99
  }
100
- newRow[key_1] = this.getFieldValue(dbKey, value);
100
+ newRow[key_1] = value;
101
101
  }
102
102
  console.log('newRow', newRow);
103
103
  return newRow;
@@ -121,7 +121,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
121
121
  return value;
122
122
  }
123
123
 
124
- async getData({ resource, limit, offset, sort, filters }) {
124
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
125
125
  // const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
126
126
  const tableName = resource.table;
127
127
 
@@ -140,7 +140,7 @@ class MongoConnector implements AdminForthDataSourceConnector {
140
140
  return { data: result, total }
141
141
  }
142
142
 
143
- async getMinMaxForColumns({ resource, columns }) {
143
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
144
144
  const tableName = resource.table;
145
145
  const collection = this.db.db().collection(tableName);
146
146
  const result = {};
@@ -160,18 +160,12 @@ class MongoConnector implements AdminForthDataSourceConnector {
160
160
  const columns = Object.keys(record);
161
161
  const newRecord = {};
162
162
  for (const colName of columns) {
163
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
164
- if (col) {
165
- newRecord[colName] = this.setFieldValue(col, record[colName]);
166
- } else {
167
- newRecord[colName] = record[colName];
168
- }
163
+ newRecord[colName] = record[colName];
169
164
  }
170
-
171
165
  await collection.insertOne(newRecord);
172
166
  }
173
167
 
174
- async updateRecord({ resource, recordId, record, newValues }) {
168
+ async updateRecord({ resource, recordId, newValues }) {
175
169
  const collection = this.db.db().collection(resource.table);
176
170
  await collection.updateOne({ [this.getPrimaryKey(resource)]: recordId }, { $set: newValues });
177
171
  }
@@ -1,15 +1,16 @@
1
1
  import dayjs from 'dayjs';
2
2
  import pkg from 'pg';
3
3
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
4
-
4
+ import AdminForthBaseConnector from './baseConnector.js';
5
5
  const { Client } = pkg;
6
6
 
7
7
 
8
- class PostgresConnector implements AdminForthDataSourceConnector {
8
+ class PostgresConnector extends AdminForthBaseConnector implements AdminForthDataSourceConnector {
9
9
 
10
10
  db: any;
11
11
 
12
12
  constructor({ url }) {
13
+ super();
13
14
  this.db = new Client({
14
15
  connectionString: url
15
16
  });
@@ -34,12 +35,12 @@ class PostgresConnector implements AdminForthDataSourceConnector {
34
35
  [AdminForthFilterOperators.ILIKE]: 'ILIKE',
35
36
  [AdminForthFilterOperators.IN]: 'IN',
36
37
  [AdminForthFilterOperators.NIN]: 'NOT IN',
37
- };
38
+ };
38
39
 
39
- SortDirectionsMap = {
40
+ SortDirectionsMap = {
40
41
  [AdminForthSortDirections.asc]: 'ASC',
41
42
  [AdminForthSortDirections.desc]: 'DESC',
42
- };
43
+ };
43
44
 
44
45
  async discoverFields(resource) {
45
46
  const tableName = resource.table;
@@ -125,8 +126,8 @@ class PostgresConnector implements AdminForthDataSourceConnector {
125
126
  field.default = row.dflt_value;
126
127
  field.required = row.notnull && !row.dflt_value;
127
128
  fieldTypes[row.name] = field
128
- });
129
- return fieldTypes;
129
+ });
130
+ return fieldTypes;
130
131
  }
131
132
 
132
133
  getFieldValue(field, value) {
@@ -153,15 +154,8 @@ class PostgresConnector implements AdminForthDataSourceConnector {
153
154
  return value;
154
155
  }
155
156
 
156
- getPrimaryKey(resource) {
157
- for (const col of resource.dataSourceColumns) {
158
- if (col.primaryKey) {
159
- return col.name;
160
- }
161
- }
162
- }
163
157
 
164
- async getRecordByPrimaryKey(resource, key) {
158
+ async getRecordByPrimaryKeyWithOriginalTypes(resource, key) {
165
159
  const tableName = resource.table;
166
160
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
167
161
  const stmt = await this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
@@ -171,7 +165,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
171
165
  }
172
166
  const newRow = {};
173
167
  for (const [key_1, value] of Object.entries(row)) {
174
- newRow[key_1] = this.getFieldValue(resource.dataSourceColumns.find((col_1) => col_1.name == key_1), value);
168
+ newRow[key_1] = value;
175
169
  }
176
170
  return newRow;
177
171
  }
@@ -192,7 +186,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
192
186
  return value;
193
187
  }
194
188
 
195
- async getData({ resource, limit, offset, sort, filters }) {
189
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
196
190
  const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
197
191
  const tableName = resource.table;
198
192
 
@@ -220,12 +214,7 @@ class PostgresConnector implements AdminForthDataSourceConnector {
220
214
  const filterValues = [];
221
215
  filters.length ? filters.forEach((f) => {
222
216
  // for arrays do set in map
223
- let v;
224
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
225
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
226
- } else {
227
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
228
- }
217
+ let v = f.value;
229
218
 
230
219
  if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
231
220
  filterValues.push(`%${v}%`);
@@ -247,12 +236,11 @@ class PostgresConnector implements AdminForthDataSourceConnector {
247
236
  const rows = stmt.rows;
248
237
 
249
238
  const total = (await this.db.query(`SELECT COUNT(*) FROM ${tableName} ${where}`, filterValues)).rows[0].count;
250
- // run all fields via getFieldValue
251
239
  return {
252
240
  data: rows.map((row) => {
253
241
  const newRow = {};
254
242
  for (const [key, value] of Object.entries(row)) {
255
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
243
+ newRow[key] = value;
256
244
  }
257
245
  return newRow;
258
246
  }),
@@ -260,15 +248,14 @@ class PostgresConnector implements AdminForthDataSourceConnector {
260
248
  };
261
249
  }
262
250
 
263
- async getMinMaxForColumns({ resource, columns }) {
251
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
264
252
  const tableName = resource.table;
265
253
  const result = {};
266
254
  await Promise.all(columns.map(async (col) => {
267
255
  const stmt = await this.db.query(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
268
256
  const { min, max } = stmt.rows[0];
269
257
  result[col.name] = {
270
- min: this.getFieldValue(col, min),
271
- max: this.getFieldValue(col, max),
258
+ min, max,
272
259
  };
273
260
  }))
274
261
  return result;
@@ -278,21 +265,14 @@ class PostgresConnector implements AdminForthDataSourceConnector {
278
265
  const tableName = resource.table;
279
266
  const columns = Object.keys(record);
280
267
  const placeholders = columns.map((_, i) => `$${i + 1}`).join(', ');
281
- const values = columns.map((colName) => {
282
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
283
- if (col) {
284
- return this.setFieldValue(col, record[colName])
285
- } else {
286
- return record[colName];
287
- }
288
- });
268
+ const values = columns.map((colName) => record[colName]);
289
269
  for (let i = 0; i < columns.length; i++) {
290
270
  columns[i] = `"${columns[i]}"`;
291
271
  }
292
272
  await this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
293
273
  }
294
274
 
295
- async updateRecord({ resource, recordId, record, newValues }) {
275
+ async updateRecord({ resource, recordId, newValues }) {
296
276
  const values = [...Object.values(newValues), recordId];
297
277
  const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
298
278
  await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
@@ -1,19 +1,19 @@
1
1
  import betterSqlite3 from 'better-sqlite3';
2
- import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
3
-
2
+ import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector, AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig.js';
3
+ import AdminForthBaseConnector from './baseConnector.js';
4
4
  import dayjs from 'dayjs';
5
5
 
6
- class SQLiteConnector implements AdminForthDataSourceConnector {
6
+ class SQLiteConnector extends AdminForthBaseConnector implements AdminForthDataSourceConnector {
7
7
 
8
8
  db: any;
9
9
 
10
- constructor({ url }) {
10
+ constructor({ url }: { url: string }) {
11
+ super();
11
12
  // create connection here
12
-
13
13
  this.db = betterSqlite3(url.replace('sqlite://', ''));
14
14
  }
15
15
 
16
- async discoverFields(resource) {
16
+ async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {
17
17
  const tableName = resource.table;
18
18
  const stmt = this.db.prepare(`PRAGMA table_info(${tableName})`);
19
19
  const rows = await stmt.all();
@@ -59,15 +59,7 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
59
59
  return fieldTypes;
60
60
  }
61
61
 
62
- getPrimaryKey(resource) {
63
- for (const col of resource.dataSourceColumns) {
64
- if (col.primaryKey) {
65
- return col.name;
66
- }
67
- }
68
- }
69
-
70
- getFieldValue(field, value) {
62
+ getFieldValue(field: AdminForthResourceColumn, value: any): any {
71
63
  if (field.type == AdminForthDataTypes.DATETIME) {
72
64
  if (!value) {
73
65
  return null;
@@ -93,7 +85,7 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
93
85
  return value;
94
86
  }
95
87
 
96
- async getRecordByPrimaryKey(resource, key) {
88
+ async getRecordByPrimaryKeyWithOriginalTypes(resource: AdminForthResource, key: any): Promise<any> {
97
89
  const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
98
90
  const tableName = resource.table;
99
91
  const stmt = this.db.prepare(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = ?`);
@@ -103,12 +95,12 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
103
95
  }
104
96
  const newRow = {};
105
97
  for (const [key, value] of Object.entries(row)) {
106
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
98
+ newRow[key] = value;
107
99
  }
108
100
  return newRow;
109
101
  }
110
102
 
111
- setFieldValue(field, value) {
103
+ setFieldValue(field: AdminForthResourceColumn, value: any): any {
112
104
  if (field.type == AdminForthDataTypes.DATETIME) {
113
105
  if (!value) {
114
106
  return null;
@@ -146,7 +138,13 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
146
138
  };
147
139
 
148
140
 
149
- getData({ resource, limit, offset, sort, filters }) {
141
+ async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }: {
142
+ resource: AdminForthResource,
143
+ limit: number,
144
+ offset: number,
145
+ sort: { field: string, direction: AdminForthSortDirections }[],
146
+ filters: { field: string, operator: AdminForthFilterOperators, value: any }[]
147
+ }): Promise<{ data: any[], total: number }> {
150
148
  const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
151
149
  const tableName = resource.table;
152
150
 
@@ -165,17 +163,11 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
165
163
  return `${field} ${operator} ${placeholder}`
166
164
  }).join(' AND ')}` : '';
167
165
 
168
-
169
166
  const filterValues = [];
170
167
 
171
168
  filters.length ? filters.forEach((f) => {
172
169
  // for arrays do set in map
173
- let v;
174
- if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
175
- v = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
176
- } else {
177
- v = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
178
- }
170
+ const v = f.value;
179
171
 
180
172
  if (f.operator == AdminForthFilterOperators.LIKE || f.operator == AdminForthFilterOperators.ILIKE) {
181
173
  filterValues.push(`%${v}%`);
@@ -196,15 +188,17 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
196
188
  if (process.env.HEAVY_DEBUG) {
197
189
  console.log('🪲 SQLITE Query', q, 'params:', d);
198
190
  }
199
- const rows = stmt.all(d);
191
+ const rows = await stmt.all(d);
192
+
193
+ const total = (
194
+ await this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])
195
+ )['COUNT(*)'];
200
196
 
201
- const total = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])['COUNT(*)'];
202
- // run all fields via getFieldValue
203
197
  return {
204
198
  data: rows.map((row) => {
205
199
  const newRow = {};
206
200
  for (const [key, value] of Object.entries(row)) {
207
- newRow[key] = this.getFieldValue(resource.dataSourceColumns.find((col) => col.name == key), value);
201
+ newRow[key] = value;
208
202
  }
209
203
  return newRow;
210
204
  }),
@@ -212,50 +206,45 @@ class SQLiteConnector implements AdminForthDataSourceConnector {
212
206
  };
213
207
  }
214
208
 
215
- async getMinMaxForColumns({ resource, columns }) {
209
+ async getMinMaxForColumnsWithOriginalTypes({ resource, columns }: { resource: AdminForthResource, columns: AdminForthResourceColumn[] }): Promise<{ [key: string]: { min: any, max: any } }> {
216
210
  const tableName = resource.table;
217
211
  const result = {};
218
212
  await Promise.all(columns.map(async (col) => {
219
213
  const stmt = await this.db.prepare(`SELECT MIN(${col.name}) as min, MAX(${col.name}) as max FROM ${tableName}`);
220
214
  const { min, max } = stmt.get();
221
215
  result[col.name] = {
222
- min: this.getFieldValue(col, min),
223
- max: this.getFieldValue(col, max),
216
+ min, max,
224
217
  };
225
218
  }))
226
219
  return result;
227
220
  }
228
221
 
229
- async createRecord({ resource, record }) {
230
- const tableName = resource.table;
231
- const columns = Object.keys(record);
232
- const placeholders = columns.map(() => '?').join(', ');
233
- const values = columns.map((colName) => {
234
- const col = resource.dataSourceColumns.find((col) => col.name == colName);
235
- if (col) {
236
- return this.setFieldValue(col, record[colName])
237
- } else {
238
- return record[colName];
239
- }
240
- });
241
- this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`).run(values);
222
+ async createRecord({ resource, record }: { resource: AdminForthResource, record: any }) {
223
+ const tableName = resource.table;
224
+ const columns = Object.keys(record);
225
+ const placeholders = columns.map(() => '?').join(', ');
226
+ const values = columns.map((colName) => record[colName]);
227
+ const q = this.db.prepare(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`)
228
+ await q.run(values);
242
229
  }
243
230
 
244
- async updateRecord({ resource, recordId, record, newValues }) {
245
- const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`);
246
- const values = [...Object.values(newValues), recordId];
231
+ async updateRecord({ resource, recordId, newValues }: { resource: AdminForthResource, recordId: any, newValues: any }) {
232
+ const columnsWithPlaceholders = Object.keys(newValues).map((col) => `${col} = ?`);
233
+ const values = [...Object.values(newValues), recordId];
247
234
 
248
- this.db.prepare(
249
- `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`
250
- ).run(values);
235
+ const q = this.db.prepare(
236
+ `UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = ?`
237
+ )
238
+ await q.run(values);
251
239
  }
252
240
 
253
- async deleteRecord({ resource, recordId }) {
254
- this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`).run(recordId);
241
+ async deleteRecord({ resource, recordId }: { resource: AdminForthResource, recordId: any }) {
242
+ const q = this.db.prepare(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = ?`);
243
+ await q.run(recordId);
255
244
  }
256
245
 
257
246
  close() {
258
- this.db.close();
247
+ this.db.close();
259
248
  }
260
249
  }
261
250
 
package/dist/auth.js CHANGED
@@ -34,6 +34,13 @@ class AdminForthAuth {
34
34
  const token = this.issueJWT({ username, pk, }, 'auth');
35
35
  response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
36
36
  }
37
+ removeCustomCookie({ response, name }) {
38
+ response.setHeader('Set-Cookie', `adminforth_test'='2'; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
39
+ }
40
+ setCustomCookie({ response, payload }) {
41
+ const { name, value, expiry, httpOnly } = payload;
42
+ response.setHeader('Set-Cookie', `adminforth_${name}=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + expiry).toUTCString()} `);
43
+ }
37
44
  issueJWT(payload, type) {
38
45
  // read ADMINFORH_SECRET from environment if not drop error
39
46
  const secret = process.env.ADMINFORTH_SECRET;
@@ -0,0 +1,90 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { AdminForthFilterOperators } from "../types/AdminForthConfig.js";
11
+ export default class AdminForthBaseConnector {
12
+ getPrimaryKey(resource) {
13
+ for (const col of resource.dataSourceColumns) {
14
+ if (col.primaryKey) {
15
+ return col.name;
16
+ }
17
+ }
18
+ }
19
+ getRecordByPrimaryKeyWithOriginalTypes(resource, id) {
20
+ throw new Error('Method not implemented.');
21
+ }
22
+ getDataWithOriginalTypes({ resource, limit, offset, sort, filters }) {
23
+ throw new Error('Method not implemented.');
24
+ }
25
+ discoverFields(resource) {
26
+ throw new Error('Method not implemented.');
27
+ }
28
+ getFieldValue(field, value) {
29
+ throw new Error('Method not implemented.');
30
+ }
31
+ setFieldValue(field, value) {
32
+ throw new Error('Method not implemented.');
33
+ }
34
+ getMinMaxForColumnsWithOriginalTypes({ resource, columns }) {
35
+ throw new Error('Method not implemented.');
36
+ }
37
+ createRecord({ resource, record }) {
38
+ throw new Error('Method not implemented.');
39
+ }
40
+ updateRecord({ resource, recordId, newValues }) {
41
+ throw new Error('Method not implemented.');
42
+ }
43
+ deleteRecord({ resource, recordId }) {
44
+ throw new Error('Method not implemented.');
45
+ }
46
+ getData(_a) {
47
+ return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
48
+ if (filters) {
49
+ filters.map((f) => {
50
+ if (f.operator == AdminForthFilterOperators.IN || f.operator == AdminForthFilterOperators.NIN) {
51
+ f.value = f.value.map((val) => this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), val));
52
+ }
53
+ else {
54
+ f.value = this.setFieldValue(resource.dataSourceColumns.find((col) => col.name == f.field), f.value);
55
+ }
56
+ });
57
+ }
58
+ const d = yield this.getDataWithOriginalTypes({ resource, limit, offset, sort, filters });
59
+ // call getFieldValue for each field
60
+ d.data.map((record) => {
61
+ for (const col of resource.dataSourceColumns) {
62
+ record[col.name] = this.getFieldValue(col, record[col.name]);
63
+ }
64
+ });
65
+ return d;
66
+ });
67
+ }
68
+ getMinMaxForColumns(_a) {
69
+ return __awaiter(this, arguments, void 0, function* ({ resource, columns }) {
70
+ const mm = yield this.getMinMaxForColumnsWithOriginalTypes({ resource, columns });
71
+ const result = {};
72
+ for (const col of columns) {
73
+ result[col.name] = {
74
+ min: this.getFieldValue(col, mm[col.name].min),
75
+ max: this.getFieldValue(col, mm[col.name].max),
76
+ };
77
+ }
78
+ return result;
79
+ });
80
+ }
81
+ getRecordByPrimaryKey(resource, recordId) {
82
+ return this.getRecordByPrimaryKeyWithOriginalTypes(resource, recordId).then((record) => {
83
+ const newRecord = {};
84
+ for (const col of resource.dataSourceColumns) {
85
+ newRecord[col.name] = this.getFieldValue(col, record[col.name]);
86
+ }
87
+ return newRecord;
88
+ });
89
+ }
90
+ }