adminforth 1.2.32 → 1.2.33

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.
@@ -1,10 +1,7 @@
1
- import betterSqlite3 from 'better-sqlite3';
2
1
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, IAdminForthDataSourceConnector, AdminForthResource, AdminForthResourceColumn } from '../types/AdminForthConfig.js';
3
2
  import AdminForthBaseConnector from './baseConnector.js';
4
3
  import dayjs from 'dayjs';
5
- import { createClient, ClickHouseLogLevel } from '@clickhouse/client' // or '@clickhouse/client-web'
6
- import { isObject } from 'util';
7
- import { date } from 'zod';
4
+ import { createClient } from '@clickhouse/client'
8
5
 
9
6
 
10
7
 
@@ -12,6 +9,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
12
9
 
13
10
  client: any;
14
11
  dbName: string;
12
+ url: string;
15
13
 
16
14
  /**
17
15
  * url: http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]
@@ -20,6 +18,7 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
20
18
  constructor({ url }: { url: string }) {
21
19
  super();
22
20
  this.dbName = new URL(url).pathname.replace('/', '');
21
+ this.url = url;;
23
22
  // create connection here
24
23
  this.client = createClient({
25
24
  url: url.replace('clickhouse://', 'http://'),
@@ -36,15 +35,23 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
36
35
  // level: ClickHouseLogLevel.TRACE,
37
36
  // }
38
37
  });
38
+
39
39
  }
40
40
 
41
41
  async discoverFields(resource: AdminForthResource): Promise<{[key: string]: AdminForthResourceColumn}> {
42
42
  const tableName = resource.table;
43
- const q = await this.client.query({
44
- query: `SELECT * FROM system.columns WHERE table = '${tableName}' and database = '${this.dbName}'`,
45
- format: 'JSONEachRow',
46
- });
47
- const rows = await q.json();
43
+
44
+ let rows;
45
+ try {
46
+ const q = await this.client.query({
47
+ query: `SELECT * FROM system.columns WHERE table = '${tableName}' and database = '${this.dbName}'`,
48
+ format: 'JSONEachRow',
49
+ });
50
+ rows = await q.json();
51
+ } catch (e) {
52
+ console.error(` 🛑Error connecting to datasource URL ${this.url}:`, e);
53
+ return null;
54
+ }
48
55
 
49
56
  const fieldTypes = {};
50
57
  rows.forEach((row) => {
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
11
11
  import AdminForthBaseConnector from './baseConnector.js';
12
12
  import dayjs from 'dayjs';
13
- import { createClient } from '@clickhouse/client'; // or '@clickhouse/client-web'
13
+ import { createClient } from '@clickhouse/client';
14
14
  class ClickhouseConnector extends AdminForthBaseConnector {
15
15
  /**
16
16
  * url: http[s]://[username:password@]hostname:port[/database][?param1=value1&param2=value2]
@@ -35,6 +35,8 @@ class ClickhouseConnector extends AdminForthBaseConnector {
35
35
  [AdminForthSortDirections.desc]: 'DESC',
36
36
  };
37
37
  this.dbName = new URL(url).pathname.replace('/', '');
38
+ this.url = url;
39
+ ;
38
40
  // create connection here
39
41
  this.client = createClient({
40
42
  url: url.replace('clickhouse://', 'http://'),
@@ -54,11 +56,18 @@ class ClickhouseConnector extends AdminForthBaseConnector {
54
56
  discoverFields(resource) {
55
57
  return __awaiter(this, void 0, void 0, function* () {
56
58
  const tableName = resource.table;
57
- const q = yield this.client.query({
58
- query: `SELECT * FROM system.columns WHERE table = '${tableName}' and database = '${this.dbName}'`,
59
- format: 'JSONEachRow',
60
- });
61
- const rows = yield q.json();
59
+ let rows;
60
+ try {
61
+ const q = yield this.client.query({
62
+ query: `SELECT * FROM system.columns WHERE table = '${tableName}' and database = '${this.dbName}'`,
63
+ format: 'JSONEachRow',
64
+ });
65
+ rows = yield q.json();
66
+ }
67
+ catch (e) {
68
+ console.error(` 🛑Error connecting to datasource URL ${this.url}:`, e);
69
+ return null;
70
+ }
62
71
  const fieldTypes = {};
63
72
  rows.forEach((row) => {
64
73
  const field = {};
package/dist/index.js CHANGED
@@ -83,9 +83,13 @@ class AdminForth {
83
83
  throw new Error(`Resource '${res.table}' refers to unknown dataSource '${res.dataSource}'`);
84
84
  }
85
85
  const fieldTypes = yield this.connectors[res.dataSource].discoverFields(res);
86
- if (!Object.keys(fieldTypes).length) {
86
+ if (fieldTypes !== null && !Object.keys(fieldTypes).length) {
87
87
  throw new Error(`Table '${res.table}' (In resource '${res.resourceId}') has no fields or does not exist`);
88
88
  }
89
+ if (fieldTypes === null) {
90
+ console.error(`DataSource ${res.dataSource} was not able to perform field discovery. It will not work properly`);
91
+ return;
92
+ }
89
93
  if (!res.columns) {
90
94
  res.columns = Object.keys(fieldTypes).map((name) => ({ name }));
91
95
  }
@@ -246,10 +246,15 @@ export default class ConfigValidator {
246
246
  state: 'danger',
247
247
  icon: 'flowbite:trash-bin-outline',
248
248
  allowed: (_b) => __awaiter(this, [_b], void 0, function* ({ resource, adminUser, allowedActions }) { return allowedActions.delete; }),
249
- action: (_c) => __awaiter(this, [_c], void 0, function* ({ selectedIds }) {
249
+ action: (_c) => __awaiter(this, [_c], void 0, function* ({ selectedIds, adminUser }) {
250
250
  const connector = this.adminforth.connectors[res.dataSource];
251
251
  yield Promise.all(selectedIds.map((recordId) => __awaiter(this, void 0, void 0, function* () {
252
+ const record = yield connector.getRecordByPrimaryKey(res, recordId);
252
253
  yield connector.deleteRecord({ resource: res, recordId });
254
+ // call afterDelete hook
255
+ yield Promise.all(res.hooks.delete.afterSave.map((hook) => __awaiter(this, void 0, void 0, function* () {
256
+ yield hook({ resource: res, record, adminUser });
257
+ })));
253
258
  })));
254
259
  })
255
260
  });
package/index.ts CHANGED
@@ -111,10 +111,13 @@ class AdminForth implements IAdminForth {
111
111
  throw new Error(`Resource '${res.table}' refers to unknown dataSource '${res.dataSource}'`);
112
112
  }
113
113
  const fieldTypes = await this.connectors[res.dataSource].discoverFields(res);
114
- if (!Object.keys(fieldTypes).length) {
114
+ if (fieldTypes !== null && !Object.keys(fieldTypes).length) {
115
115
  throw new Error(`Table '${res.table}' (In resource '${res.resourceId}') has no fields or does not exist`);
116
116
  }
117
-
117
+ if (fieldTypes === null) {
118
+ console.error(`DataSource ${res.dataSource} was not able to perform field discovery. It will not work properly`);
119
+ return;
120
+ }
118
121
  if (!res.columns) {
119
122
  res.columns = Object.keys(fieldTypes).map((name) => ({ name }));
120
123
  }
@@ -5,6 +5,7 @@ import {
5
5
  AdminForthComponentDeclaration ,
6
6
  AdminForthResourcePages, AllowedActionsEnum,
7
7
  type AdminForthComponentDeclarationFull,
8
+ type AfterSaveFunction,
8
9
  } from "../types/AdminForthConfig.js";
9
10
 
10
11
  import fs from 'fs';
@@ -143,7 +144,7 @@ export default class ConfigValidator implements IConfigValidator {
143
144
  }
144
145
 
145
146
  if (this.config.resources) {
146
- this.config.resources.forEach((res) => {
147
+ this.config.resources.forEach((res: AdminForthResource) => {
147
148
  if (!res.table) {
148
149
  errors.push(`Resource "${res.dataSource}" is missing table`);
149
150
  }
@@ -266,10 +267,21 @@ export default class ConfigValidator implements IConfigValidator {
266
267
  state: 'danger',
267
268
  icon: 'flowbite:trash-bin-outline',
268
269
  allowed: async ({ resource, adminUser, allowedActions }) => { return allowedActions.delete },
269
- action: async ({ selectedIds }) => {
270
+ action: async ({ selectedIds, adminUser }) => {
270
271
  const connector = this.adminforth.connectors[res.dataSource];
271
272
  await Promise.all(selectedIds.map(async (recordId) => {
273
+ const record = await connector.getRecordByPrimaryKey(res, recordId);
274
+
272
275
  await connector.deleteRecord({ resource: res, recordId });
276
+ // call afterDelete hook
277
+ await Promise.all(
278
+ (res.hooks.delete.afterSave as AfterSaveFunction[]).map(
279
+ async (hook) => {
280
+ await hook({ resource: res, record, adminUser });
281
+ }
282
+ )
283
+ )
284
+
273
285
  }));
274
286
  }
275
287
  });
@@ -251,10 +251,10 @@ export default class AdminForthRestAPI {
251
251
 
252
252
  function checkAccess(action: AllowedActionsEnum, allowedActions: AllowedActions): { allowed: boolean, error?: string } {
253
253
  const allowed = (allowedActions[action] as boolean | string | undefined);
254
- if (allowed !== true) {
255
- return { error: typeof allowed === 'string' ? allowed : 'Action is not allowed', allowed: false };
256
- }
257
- return { allowed: true };
254
+ if (allowed !== true) {
255
+ return { error: typeof allowed === 'string' ? allowed : 'Action is not allowed', allowed: false };
256
+ }
257
+ return { allowed: true };
258
258
  }
259
259
 
260
260
  server.endpoint({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.2.32",
3
+ "version": "1.2.33",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",