adminforth 1.4.3-next.2 → 1.4.3-next.21

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 (191) hide show
  1. package/commands/bundle.js +29 -0
  2. package/commands/generateModels.js +63 -0
  3. package/commands/utils.js +93 -0
  4. package/dist/adminforth/auth.d.ts +31 -0
  5. package/dist/adminforth/auth.d.ts.map +1 -0
  6. package/dist/adminforth/auth.js +119 -0
  7. package/dist/adminforth/auth.js.map +1 -0
  8. package/dist/adminforth/basePlugin.d.ts +23 -0
  9. package/dist/adminforth/basePlugin.d.ts.map +1 -0
  10. package/dist/adminforth/basePlugin.js +51 -0
  11. package/dist/adminforth/basePlugin.js.map +1 -0
  12. package/dist/adminforth/dataConnectors/baseConnector.d.ts +95 -0
  13. package/dist/adminforth/dataConnectors/baseConnector.d.ts.map +1 -0
  14. package/dist/adminforth/dataConnectors/baseConnector.js +178 -0
  15. package/dist/adminforth/dataConnectors/baseConnector.js.map +1 -0
  16. package/dist/adminforth/dataConnectors/clickhouse.d.ts +93 -0
  17. package/dist/adminforth/dataConnectors/clickhouse.d.ts.map +1 -0
  18. package/dist/adminforth/dataConnectors/clickhouse.js +297 -0
  19. package/dist/adminforth/dataConnectors/clickhouse.js.map +1 -0
  20. package/dist/adminforth/dataConnectors/mongo.d.ts +94 -0
  21. package/dist/adminforth/dataConnectors/mongo.d.ts.map +1 -0
  22. package/dist/adminforth/dataConnectors/mongo.js +168 -0
  23. package/dist/adminforth/dataConnectors/mongo.js.map +1 -0
  24. package/dist/adminforth/dataConnectors/postgres.d.ts +72 -0
  25. package/dist/adminforth/dataConnectors/postgres.d.ts.map +1 -0
  26. package/dist/adminforth/dataConnectors/postgres.js +298 -0
  27. package/dist/adminforth/dataConnectors/postgres.js.map +1 -0
  28. package/dist/adminforth/dataConnectors/sqlite.d.ts +67 -0
  29. package/dist/adminforth/dataConnectors/sqlite.d.ts.map +1 -0
  30. package/dist/adminforth/dataConnectors/sqlite.js +251 -0
  31. package/dist/adminforth/dataConnectors/sqlite.js.map +1 -0
  32. package/dist/adminforth/index.d.ts +94 -0
  33. package/dist/adminforth/index.d.ts.map +1 -0
  34. package/dist/adminforth/index.js +357 -0
  35. package/dist/adminforth/index.js.map +1 -0
  36. package/dist/adminforth/modules/codeInjector.d.ts +38 -0
  37. package/dist/adminforth/modules/codeInjector.d.ts.map +1 -0
  38. package/dist/adminforth/modules/codeInjector.js +673 -0
  39. package/dist/adminforth/modules/codeInjector.js.map +1 -0
  40. package/dist/adminforth/modules/configValidator.d.ts +13 -0
  41. package/dist/adminforth/modules/configValidator.d.ts.map +1 -0
  42. package/dist/adminforth/modules/configValidator.js +511 -0
  43. package/dist/adminforth/modules/configValidator.js.map +1 -0
  44. package/dist/adminforth/modules/operationalResource.d.ts +17 -0
  45. package/dist/adminforth/modules/operationalResource.d.ts.map +1 -0
  46. package/dist/adminforth/modules/operationalResource.js +75 -0
  47. package/dist/adminforth/modules/operationalResource.js.map +1 -0
  48. package/dist/adminforth/modules/restApi.d.ts +11 -0
  49. package/dist/adminforth/modules/restApi.d.ts.map +1 -0
  50. package/dist/adminforth/modules/restApi.js +657 -0
  51. package/dist/adminforth/modules/restApi.js.map +1 -0
  52. package/dist/adminforth/modules/styleGenerator.d.ts +9 -0
  53. package/dist/adminforth/modules/styleGenerator.d.ts.map +1 -0
  54. package/dist/adminforth/modules/styleGenerator.js +47 -0
  55. package/dist/adminforth/modules/styleGenerator.js.map +1 -0
  56. package/dist/adminforth/modules/styles.d.ts +96 -0
  57. package/dist/adminforth/modules/styles.d.ts.map +1 -0
  58. package/dist/adminforth/modules/styles.js +105 -0
  59. package/dist/adminforth/modules/styles.js.map +1 -0
  60. package/dist/adminforth/modules/utils.d.ts +39 -0
  61. package/dist/adminforth/modules/utils.d.ts.map +1 -0
  62. package/dist/adminforth/modules/utils.js +421 -0
  63. package/dist/adminforth/modules/utils.js.map +1 -0
  64. package/dist/adminforth/plugins/audit-log/index.d.ts +25 -0
  65. package/dist/adminforth/plugins/audit-log/index.d.ts.map +1 -0
  66. package/dist/adminforth/plugins/audit-log/index.js +129 -0
  67. package/dist/adminforth/plugins/audit-log/index.js.map +1 -0
  68. package/dist/adminforth/plugins/audit-log/types.d.ts +35 -0
  69. package/dist/adminforth/plugins/audit-log/types.d.ts.map +1 -0
  70. package/dist/adminforth/plugins/audit-log/types.js +2 -0
  71. package/dist/adminforth/plugins/audit-log/types.js.map +1 -0
  72. package/dist/adminforth/plugins/chat-gpt/index.d.ts +14 -0
  73. package/dist/adminforth/plugins/chat-gpt/index.d.ts.map +1 -0
  74. package/dist/adminforth/plugins/chat-gpt/index.js +147 -0
  75. package/dist/adminforth/plugins/chat-gpt/index.js.map +1 -0
  76. package/dist/adminforth/plugins/chat-gpt/types.d.ts +82 -0
  77. package/dist/adminforth/plugins/chat-gpt/types.d.ts.map +1 -0
  78. package/dist/adminforth/plugins/chat-gpt/types.js +2 -0
  79. package/dist/adminforth/plugins/chat-gpt/types.js.map +1 -0
  80. package/dist/adminforth/plugins/email-password-reset/index.d.ts +15 -0
  81. package/dist/adminforth/plugins/email-password-reset/index.d.ts.map +1 -0
  82. package/dist/adminforth/plugins/email-password-reset/index.js +176 -0
  83. package/dist/adminforth/plugins/email-password-reset/index.js.map +1 -0
  84. package/dist/adminforth/plugins/email-password-reset/types.d.ts +28 -0
  85. package/dist/adminforth/plugins/email-password-reset/types.d.ts.map +1 -0
  86. package/dist/adminforth/plugins/email-password-reset/types.js +2 -0
  87. package/dist/adminforth/plugins/email-password-reset/types.js.map +1 -0
  88. package/dist/adminforth/plugins/foreign-inline-list/index.d.ts +13 -0
  89. package/dist/adminforth/plugins/foreign-inline-list/index.d.ts.map +1 -0
  90. package/dist/adminforth/plugins/foreign-inline-list/index.js +56 -0
  91. package/dist/adminforth/plugins/foreign-inline-list/index.js.map +1 -0
  92. package/dist/adminforth/plugins/foreign-inline-list/types.d.ts +19 -0
  93. package/dist/adminforth/plugins/foreign-inline-list/types.d.ts.map +1 -0
  94. package/dist/adminforth/plugins/foreign-inline-list/types.js +2 -0
  95. package/dist/adminforth/plugins/foreign-inline-list/types.js.map +1 -0
  96. package/dist/adminforth/plugins/import-export/index.d.ts +15 -0
  97. package/dist/adminforth/plugins/import-export/index.d.ts.map +1 -0
  98. package/dist/adminforth/plugins/import-export/index.js +127 -0
  99. package/dist/adminforth/plugins/import-export/index.js.map +1 -0
  100. package/dist/adminforth/plugins/import-export/types.d.ts +3 -0
  101. package/dist/adminforth/plugins/import-export/types.d.ts.map +1 -0
  102. package/dist/adminforth/plugins/import-export/types.js +2 -0
  103. package/dist/adminforth/plugins/import-export/types.js.map +1 -0
  104. package/dist/adminforth/plugins/rich-editor/index.d.ts +17 -0
  105. package/dist/adminforth/plugins/rich-editor/index.d.ts.map +1 -0
  106. package/dist/adminforth/plugins/rich-editor/index.js +262 -0
  107. package/dist/adminforth/plugins/rich-editor/index.js.map +1 -0
  108. package/dist/adminforth/plugins/rich-editor/types.d.ts +153 -0
  109. package/dist/adminforth/plugins/rich-editor/types.d.ts.map +1 -0
  110. package/dist/adminforth/plugins/rich-editor/types.js +16 -0
  111. package/dist/adminforth/plugins/rich-editor/types.js.map +1 -0
  112. package/dist/adminforth/plugins/two-factors-auth/index.d.ts +16 -0
  113. package/dist/adminforth/plugins/two-factors-auth/index.d.ts.map +1 -0
  114. package/dist/adminforth/plugins/two-factors-auth/index.js +134 -0
  115. package/dist/adminforth/plugins/two-factors-auth/index.js.map +1 -0
  116. package/dist/adminforth/plugins/two-factors-auth/types.d.ts +18 -0
  117. package/dist/adminforth/plugins/two-factors-auth/types.d.ts.map +1 -0
  118. package/dist/adminforth/plugins/two-factors-auth/types.js +2 -0
  119. package/dist/adminforth/plugins/two-factors-auth/types.js.map +1 -0
  120. package/dist/adminforth/plugins/upload/index.d.ts +14 -0
  121. package/dist/adminforth/plugins/upload/index.d.ts.map +1 -0
  122. package/dist/adminforth/plugins/upload/index.js +450 -0
  123. package/dist/adminforth/plugins/upload/index.js.map +1 -0
  124. package/dist/adminforth/plugins/upload/types.d.ts +132 -0
  125. package/dist/adminforth/plugins/upload/types.d.ts.map +1 -0
  126. package/dist/adminforth/plugins/upload/types.js +2 -0
  127. package/dist/adminforth/plugins/upload/types.js.map +1 -0
  128. package/dist/adminforth/servers/express.d.ts +18 -0
  129. package/dist/adminforth/servers/express.d.ts.map +1 -0
  130. package/dist/adminforth/servers/express.js +238 -0
  131. package/dist/adminforth/servers/express.js.map +1 -0
  132. package/dist/adminforth/types/Back.d.ts +1103 -0
  133. package/dist/adminforth/types/Back.d.ts.map +1 -0
  134. package/dist/adminforth/types/Back.js +86 -0
  135. package/dist/adminforth/types/Back.js.map +1 -0
  136. package/dist/adminforth/types/Common.d.ts +616 -0
  137. package/dist/adminforth/types/Common.d.ts.map +1 -0
  138. package/dist/adminforth/types/Common.js +65 -0
  139. package/dist/adminforth/types/Common.js.map +1 -0
  140. package/dist/adminforth/types/FrontendAPI.d.ts +134 -0
  141. package/dist/adminforth/types/FrontendAPI.d.ts.map +1 -0
  142. package/dist/adminforth/types/FrontendAPI.js +8 -0
  143. package/dist/adminforth/types/FrontendAPI.js.map +1 -0
  144. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  145. package/dist/dataConnectors/baseConnector.js +7 -2
  146. package/dist/dataConnectors/baseConnector.js.map +1 -1
  147. package/dist/dataConnectors/postgres.js +3 -3
  148. package/dist/dataConnectors/postgres.js.map +1 -1
  149. package/dist/dev-demo/index.d.ts +3 -0
  150. package/dist/dev-demo/index.d.ts.map +1 -0
  151. package/dist/dev-demo/index.js +1052 -0
  152. package/dist/dev-demo/index.js.map +1 -0
  153. package/dist/modules/codeInjector.d.ts +4 -2
  154. package/dist/modules/codeInjector.d.ts.map +1 -1
  155. package/dist/modules/codeInjector.js +93 -69
  156. package/dist/modules/codeInjector.js.map +1 -1
  157. package/dist/modules/restApi.d.ts.map +1 -1
  158. package/dist/modules/restApi.js +33 -19
  159. package/dist/modules/restApi.js.map +1 -1
  160. package/dist/modules/utils.d.ts.map +1 -1
  161. package/dist/modules/utils.js +0 -1
  162. package/dist/modules/utils.js.map +1 -1
  163. package/dist/servers/express.d.ts.map +1 -1
  164. package/dist/servers/express.js +4 -1
  165. package/dist/servers/express.js.map +1 -1
  166. package/dist/spa/package-lock.json +64 -4
  167. package/dist/spa/package.json +1 -0
  168. package/dist/spa/src/App.vue +15 -25
  169. package/dist/spa/src/acl/Button.vue +21 -0
  170. package/dist/spa/src/acl/Link.vue +9 -0
  171. package/dist/spa/src/afcl/Button.vue +27 -0
  172. package/dist/spa/src/afcl/Input.vue +41 -0
  173. package/dist/spa/src/afcl/Link.vue +9 -0
  174. package/dist/spa/src/afcl/Select.vue +206 -0
  175. package/dist/spa/src/afcl/index.ts +6 -0
  176. package/dist/spa/src/components/Filters.vue +11 -5
  177. package/dist/spa/src/components/GroupsTable.vue +23 -23
  178. package/dist/spa/src/components/ValueRenderer.vue +3 -1
  179. package/dist/spa/src/renderers/CompactField.vue +1 -1
  180. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  181. package/dist/spa/src/renderers/CountryFlag.vue +3 -4
  182. package/dist/spa/src/renderers/HumanNumber.vue +3 -2
  183. package/dist/spa/src/renderers/RelativeTime.vue +3 -2
  184. package/dist/spa/src/router/index.ts +1 -1
  185. package/dist/spa/src/types/Back.ts +47 -13
  186. package/dist/spa/src/views/LoginView.vue +4 -15
  187. package/dist/spa/vite.config.ts +16 -34
  188. package/dist/types/Back.d.ts +21 -6
  189. package/dist/types/Back.d.ts.map +1 -1
  190. package/dist/types/Back.js.map +1 -1
  191. package/package.json +3 -2
@@ -0,0 +1,1052 @@
1
+ import betterSqlite3 from 'better-sqlite3';
2
+ import express from 'express';
3
+ import AdminForth, { AdminForthDataTypes, Filters } from '../adminforth/index.js';
4
+ import { v1 as uuid } from 'uuid';
5
+ import ForeignInlineListPlugin from '../adminforth/plugins/foreign-inline-list/index.js';
6
+ import AuditLogPlugin from '../adminforth/plugins/audit-log/index.js';
7
+ import TwoFactorsAuthPlugin from '../adminforth/plugins/two-factors-auth/index.js';
8
+ import UploadPlugin from '../adminforth/plugins/upload/index.js';
9
+ import RichEditorPlugin from '../adminforth/plugins/rich-editor/index.js';
10
+ import EmailResetPasswordPlugin from '../adminforth/plugins/email-password-reset/index.js';
11
+ import fs from 'fs';
12
+ import ImportExportPlugin from '../adminforth/plugins/import-export/index.js';
13
+ import { generate } from "random-words";
14
+ const ADMIN_BASE_URL = '';
15
+ // create test1.db
16
+ try {
17
+ fs.mkdirSync('db');
18
+ }
19
+ catch (e) { }
20
+ const dbPath = 'db/test.sqlite';
21
+ const db = betterSqlite3(dbPath);
22
+ const tableExists = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='apartments';`).get();
23
+ if (!tableExists) {
24
+ await db.prepare(`
25
+ CREATE TABLE apartments (
26
+ id VARCHAR(20) PRIMARY KEY NOT NULL,
27
+ title VARCHAR(255) NOT NULL,
28
+ square_meter REAL,
29
+ price DECIMAL(10, 2) NOT NULL,
30
+ number_of_rooms INT,
31
+ description TEXT,
32
+ property_type VARCHAR(255) DEFAULT 'apartment',
33
+ listed BOOLEAN DEFAULT FALSE,
34
+ created_at TIMESTAMP,
35
+ user_id VARCHAR(255),
36
+ country VARCHAR(5)
37
+ );`).run();
38
+ await db.prepare(`
39
+ CREATE TABLE users (
40
+ id VARCHAR(255) PRIMARY KEY NOT NULL,
41
+ email VARCHAR(255) NOT NULL,
42
+ secret2fa VARCHAR(255) ,
43
+ password_hash VARCHAR(255) NOT NULL,
44
+ created_at VARCHAR(255) NOT NULL,
45
+ role VARCHAR(255) NOT NULL
46
+ );`).run();
47
+ for (let i = 0; i < 50; i++) {
48
+ await db.prepare(`
49
+ INSERT INTO apartments (
50
+ id, title, square_meter, price, number_of_rooms, description, created_at, listed, property_type
51
+ ) VALUES ('${i}',
52
+ '${generate({ min: 2, max: 5, join: ' ' })}',
53
+ ${(Math.random() * 1000000).toFixed(1)}, ${(Math.random() * 10000).toFixed(2)}, ${Math
54
+ .floor(Math.random() * 5)},
55
+ 'Next gen apartments',
56
+ ${Date.now() / 1000 - 60 * 60 * 24 * 7 * Math.random()},
57
+ ${Math.random() > 0.5 ? 1 : 0},
58
+ ${Math.random() > 0.5 ? "'house'" : "'apartment'"});
59
+ `).run();
60
+ }
61
+ }
62
+ const tableExistsAuditLog = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='audit_log';`).get();
63
+ if (!tableExistsAuditLog) {
64
+ await db.prepare(`
65
+ CREATE TABLE audit_log (
66
+ id uuid NOT NULL, -- identifier of applied change record
67
+ created_at timestamp without time zone, -- timestamp of applied change
68
+ resource_id varchar(255), -- identifier of resource where change were applied
69
+ user_id uuid, -- identifier of user who made the changes
70
+ "action" varchar(255), -- type of change (create, edit, delete)
71
+ diff text, -- delta betwen before/after versions
72
+ record_id varchar, -- identifier of record that been changed
73
+ PRIMARY KEY(id)
74
+ );`).run();
75
+ }
76
+ const tableExistsDescriptionImage = db.prepare(`SELECT name FROM sqlite_master WHERE type='table' AND name='description_image';`).get();
77
+ if (!tableExistsDescriptionImage) {
78
+ await db.prepare(`
79
+ CREATE TABLE description_image (
80
+ id uuid NOT NULL, -- identifier of applied change record
81
+ created_at timestamp without time zone, -- timestamp of applied change
82
+ resource_id varchar(255), -- identifier of resource where change were applied
83
+ record_id varchar, -- identifier of record that been changed
84
+ image_path varchar(255), -- path to image
85
+ PRIMARY KEY(id)
86
+ );`).run();
87
+ }
88
+ // check column apartment_image in aparts table
89
+ const columns = await db.prepare('PRAGMA table_info(apartments);').all();
90
+ const columnExists = columns.some((c) => c.name === 'apartment_image');
91
+ if (!columnExists) {
92
+ await db.prepare('ALTER TABLE apartments ADD COLUMN apartment_image VARCHAR(255);').run();
93
+ }
94
+ const demoChecker = async ({ record, adminUser, resource }) => {
95
+ if (adminUser.dbUser.role !== 'superadmin') {
96
+ return { ok: false, error: "You can't do this on demo.adminforth.dev" };
97
+ }
98
+ return { ok: true };
99
+ };
100
+ export const admin = new AdminForth({
101
+ baseUrl: ADMIN_BASE_URL,
102
+ // deleteConfirmation: false,
103
+ auth: {
104
+ resourceId: 'users', // resource for getting user
105
+ usernameField: 'email',
106
+ passwordHashField: 'password_hash',
107
+ userFullNameField: 'fullName', // optional
108
+ loginBackgroundImage: 'https://images.unsplash.com/photo-1534239697798-120952b76f2b?q=80&w=3389&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D',
109
+ loginBackgroundPosition: '1/2', // over, 3/4, 2/5, 3/5 (tailwind grid)
110
+ demoCredentials: "adminforth:adminforth", // never use it for production
111
+ loginPromptHTML: "Use email <b>adminforth</b> and password <b>adminforth</b> to login",
112
+ // loginBackgroundImage: '@@/pho.jpg',
113
+ rememberMeDays: 30,
114
+ },
115
+ customization: {
116
+ customComponentsDir: './custom',
117
+ globalInjections: {
118
+ userMenu: '@@/login2.vue',
119
+ },
120
+ customPages: [{
121
+ path: '/login2',
122
+ component: {
123
+ file: '@@/login2.vue',
124
+ meta: {
125
+ customLayout: true,
126
+ }
127
+ }
128
+ }],
129
+ vueUsesFile: '@@/vueUses.ts', // @@ is alias to custom directory,
130
+ brandName: 'New Reality',
131
+ showBrandNameInSidebar: true,
132
+ // datesFormat: 'D MMM YY',
133
+ // timeFormat: 'HH:mm:ss',
134
+ datesFormat: 'DD MMM',
135
+ timeFormat: 'hh:mm a',
136
+ title: 'Devforth Admin',
137
+ // brandLogo: '@@/df.svg',
138
+ emptyFieldPlaceholder: '-',
139
+ // styles:{
140
+ // colors: {
141
+ // light: {
142
+ // primary: '#425BB8',
143
+ // sidebar: {main:'#1c2a5b', text:'white'},
144
+ // },
145
+ // }
146
+ // },
147
+ styles: {
148
+ colors: {
149
+ light: {
150
+ // color for buttons, links, etc.
151
+ primary: '#16537e',
152
+ // color for sidebar and text
153
+ sidebar: {
154
+ main: '#232323',
155
+ text: 'white'
156
+ },
157
+ },
158
+ dark: {
159
+ primary: '#8a158d',
160
+ sidebar: {
161
+ main: '#8a158d',
162
+ text: 'white'
163
+ },
164
+ }
165
+ }
166
+ },
167
+ announcementBadge: (adminUser) => {
168
+ return {
169
+ html: `
170
+ <svg xmlns="http://www.w3.org/2000/svg" style="display:inline; margin-top: -4px" width="16" height="16" viewBox="0 0 24 24"><path d="M12 .587l3.668 7.568 8.332 1.151-6.064 5.828 1.48 8.279-7.416-3.967-7.417 3.967 1.481-8.279-6.064-5.828 8.332-1.151z"/></svg>
171
+ <a href="https://github.com/devforth/adminforth" style="font-weight: bold; text-decoration: underline" target="_blank">Star us on GitHub</a> to support a project!`,
172
+ closable: true,
173
+ title: 'Support us for free',
174
+ };
175
+ }
176
+ },
177
+ dataSources: [
178
+ {
179
+ id: 'maindb',
180
+ url: `sqlite://${dbPath}`
181
+ },
182
+ {
183
+ id: 'db2',
184
+ url: 'postgres://postgres:35ozenad@test-db.c3sosskwwcnd.eu-central-1.rds.amazonaws.com:5432'
185
+ },
186
+ {
187
+ id: 'db3',
188
+ url: 'mongodb://127.0.0.1:27017/betbolt?retryWrites=true&w=majority&authSource=admin',
189
+ },
190
+ {
191
+ id: 'ch',
192
+ url: 'clickhouse://demo:demo@localhost:8124/demo',
193
+ }
194
+ ],
195
+ resources: [
196
+ {
197
+ dataSource: 'ch',
198
+ table: 'clicks',
199
+ /*
200
+ SQL to create table run SQL in http://localhost:8123/play
201
+ CREATE TABLE demo.clicks (
202
+ clickid UUID PRIMARY KEY,
203
+ element String,
204
+ clientX Int32,
205
+ created_at DateTime,
206
+ aggressiveness Float32,
207
+ click_price Decimal(10, 2)
208
+
209
+ )
210
+
211
+ */
212
+ columns: [
213
+ {
214
+ name: 'clickid', primaryKey: true, required: false, fillOnCreate: ({ initialRecord }) => uuid(),
215
+ showIn: ['list', 'filter', 'show'],
216
+ components: {
217
+ list: '@/renderers/CompactUUID.vue'
218
+ }
219
+ },
220
+ { name: 'element',
221
+ type: AdminForthDataTypes.STRING,
222
+ required: false,
223
+ enum: [
224
+ { value: 'button', label: 'Button' },
225
+ { value: 'link', label: 'Link' },
226
+ { value: 'image', label: 'Image' },
227
+ ]
228
+ },
229
+ { name: 'clientX',
230
+ type: AdminForthDataTypes.INTEGER,
231
+ allowMinMaxQuery: true,
232
+ required: false },
233
+ { name: 'created_at',
234
+ type: AdminForthDataTypes.DATETIME,
235
+ required: false },
236
+ { name: 'aggressiveness',
237
+ allowMinMaxQuery: true,
238
+ type: AdminForthDataTypes.FLOAT,
239
+ required: false,
240
+ showIn: ['filter', 'show', 'edit'],
241
+ },
242
+ {
243
+ name: 'click_price',
244
+ }
245
+ ],
246
+ },
247
+ {
248
+ dataSource: 'maindb', table: 'audit_log',
249
+ columns: [
250
+ { name: 'id', primaryKey: true, required: false, fillOnCreate: ({ initialRecord }) => uuid() },
251
+ { name: 'created_at', required: false },
252
+ { name: 'resource_id', required: false },
253
+ { name: 'user_id', required: false,
254
+ foreignResource: {
255
+ resourceId: 'users',
256
+ },
257
+ },
258
+ { name: 'action', required: false },
259
+ { name: 'diff', required: false, type: AdminForthDataTypes.JSON },
260
+ { name: 'record_id', required: false },
261
+ ],
262
+ options: {
263
+ allowedActions: {
264
+ edit: false,
265
+ delete: false,
266
+ create: false,
267
+ },
268
+ },
269
+ plugins: [
270
+ new AuditLogPlugin({
271
+ resourceColumns: {
272
+ resourceUserIdColumnName: 'user_id',
273
+ resourceRecordIdColumnName: 'record_id',
274
+ resourceActionColumnName: 'action',
275
+ resourceDataColumnName: 'diff',
276
+ resourceCreatedColumnName: 'created_at',
277
+ resourceIdColumnName: 'resource_id',
278
+ },
279
+ }),
280
+ ],
281
+ },
282
+ {
283
+ dataSource: 'maindb', table: 'apartments',
284
+ resourceId: 'aparts', // resourceId is defaulted to table name but you can change it e.g.
285
+ // in case of same table names from different data sources
286
+ label: 'Apartments', // label is defaulted to table name but you can change it
287
+ recordLabel: (r) => `🏡 ${r.title}`,
288
+ hooks: {
289
+ delete: {
290
+ beforeSave: demoChecker,
291
+ },
292
+ list: {
293
+ afterDatasourceResponse: async ({ response }) => {
294
+ response.forEach((r) => {
295
+ // random country
296
+ const countries = ['US', 'DE', 'FR', 'GB', 'NL', 'IT', 'ES', 'DK', 'PL', 'UA',
297
+ 'CA', 'AU', 'BR', 'JP', 'CN', 'IN', 'KR', 'TR', 'MX', 'ID',
298
+ 'NG', 'SA', 'EG', 'ZA', 'AR', 'SE', 'CH', 'AT', 'BE', 'FI', 'NO', 'PT', 'GR', 'HU', 'IE', 'IL', 'LU', 'SK', 'SI', 'LT', 'LV', 'EE', 'HR', 'CZ', 'BG', 'RO', 'RS', 'IS', 'MT', 'CY', 'AL', 'MK', 'ME', 'BA', 'XK', 'MD', 'LI', 'AD', 'MC', 'SM', 'VA', 'UA', 'BY'
299
+ ];
300
+ r.country = countries[Math.floor(Math.random() * countries.length)];
301
+ });
302
+ return { ok: true, error: "" };
303
+ }
304
+ }
305
+ },
306
+ columns: [
307
+ {
308
+ name: 'id',
309
+ label: 'Identifier', // if you wish you can redefine label
310
+ showIn: ['filter', 'show', 'list'], // show in filter and in show page
311
+ primaryKey: true,
312
+ // @ts-ignore
313
+ fillOnCreate: ({ initialRecord, adminUser }) => Math.random().toString(36).substring(7),
314
+ // fillOnCreate: ({initialRecord}: any) => randomUUID(),
315
+ components: {
316
+ list: '@/renderers/CompactUUID.vue'
317
+ } // initialRecord is values user entered, adminUser object of user who creates record
318
+ },
319
+ {
320
+ name: 'title',
321
+ // type: AdminForthDataTypes.JSON,
322
+ required: true,
323
+ showIn: ['list', 'create', 'edit', 'filter', 'show'], // the default is full set
324
+ maxLength: 255, // you can set max length for string fields
325
+ minLength: 3, // you can set min length for string fields
326
+ components: {
327
+ // edit: {
328
+ // file: '@@/IdShow.vue',
329
+ // meta: {
330
+ // title: 'Title',
331
+ // description: 'This is title of apartment'
332
+ // }
333
+ // },
334
+ // show: '@@/IdShow.vue',
335
+ // create: {
336
+ // file: '@@/IdShow.vue',
337
+ // meta: {
338
+ // title: 'Title',
339
+ // description: 'This is title of apartment'
340
+ // }
341
+ // }
342
+ // list: '@@/IdShow.vue',
343
+ },
344
+ },
345
+ {
346
+ name: 'country',
347
+ components: {
348
+ list: {
349
+ file: '@/renderers/CountryFlag.vue',
350
+ meta: {
351
+ showCountryName: false,
352
+ }
353
+ }
354
+ },
355
+ enum: [{
356
+ value: 'US',
357
+ label: 'United States'
358
+ }, {
359
+ value: 'DE',
360
+ label: 'Germany'
361
+ }, {
362
+ value: 'FR',
363
+ label: 'France'
364
+ }, {
365
+ value: 'GB',
366
+ label: 'United Kingdom'
367
+ }, {
368
+ value: 'NL',
369
+ label: 'Netherlands'
370
+ }, {
371
+ value: 'IT',
372
+ label: 'Italy'
373
+ }, {
374
+ value: 'ES',
375
+ label: 'Spain'
376
+ }, {
377
+ value: 'DK',
378
+ label: 'Denmark'
379
+ }, {
380
+ value: 'PL',
381
+ label: 'Poland'
382
+ }, {
383
+ value: 'UA',
384
+ label: 'Ukraine'
385
+ }, {
386
+ value: null,
387
+ label: 'Not defined'
388
+ }],
389
+ },
390
+ {
391
+ name: 'created_at',
392
+ type: AdminForthDataTypes.DATETIME,
393
+ allowMinMaxQuery: true,
394
+ showIn: ['list', 'filter', 'show', 'edit'],
395
+ components: {
396
+ list: '@/renderers/RelativeTime.vue'
397
+ },
398
+ // @ts-ignore
399
+ fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
400
+ },
401
+ {
402
+ name: 'apartment_image',
403
+ showIn: ['show',],
404
+ required: false,
405
+ editingNote: 'Upload image of apartment',
406
+ },
407
+ {
408
+ name: 'price',
409
+ showIn: ['create', 'edit', 'filter', 'show'],
410
+ allowMinMaxQuery: true, // use better experience for filtering e.g. date range, set it only if you have index on this column or if there will be low number of rows
411
+ editingNote: 'Price is in USD', // you can appear note on editing or creating page
412
+ },
413
+ {
414
+ name: 'square_meter',
415
+ label: 'Square',
416
+ // allowMinMaxQuery: true,
417
+ minValue: 1, // you can set min /max value for number fields
418
+ maxValue: 100000000,
419
+ components: {
420
+ list: {
421
+ file: '@/renderers/HumanNumber.vue',
422
+ meta: {
423
+ showCountryName: true,
424
+ }
425
+ }
426
+ }
427
+ },
428
+ {
429
+ name: 'number_of_rooms',
430
+ allowMinMaxQuery: true,
431
+ enum: [
432
+ { value: 1, label: '1 room' },
433
+ { value: 2, label: '2 rooms' },
434
+ { value: 3, label: '3 rooms' },
435
+ { value: 4, label: '4 rooms' },
436
+ { value: 5, label: '5 rooms' },
437
+ ],
438
+ showIn: ['filter', 'show', 'create', 'edit'],
439
+ },
440
+ {
441
+ name: 'description',
442
+ sortable: false,
443
+ type: AdminForthDataTypes.RICHTEXT,
444
+ showIn: ['filter', 'show', 'edit', 'create'],
445
+ },
446
+ {
447
+ name: 'property_type',
448
+ enum: [{
449
+ value: 'house',
450
+ label: 'House'
451
+ }, {
452
+ value: 'apartment',
453
+ label: 'Apartment'
454
+ }, {
455
+ value: null,
456
+ label: 'Not defined'
457
+ }],
458
+ // allowCustomValue: true,
459
+ },
460
+ {
461
+ name: 'listed',
462
+ required: true, // will be required on create/edit
463
+ },
464
+ {
465
+ name: 'user_id',
466
+ showIn: ['filter', 'show', 'edit', 'list', 'create'],
467
+ foreignResource: {
468
+ resourceId: 'users',
469
+ hooks: {
470
+ dropdownList: {
471
+ beforeDatasourceRequest: async ({ query, adminUser }) => {
472
+ return { ok: true, error: "" };
473
+ },
474
+ afterDatasourceResponse: async ({ response, adminUser }) => {
475
+ return { ok: true, error: "" };
476
+ }
477
+ },
478
+ }
479
+ }
480
+ }
481
+ ],
482
+ plugins: [
483
+ ...(process.env.AWS_ACCESS_KEY_ID ? [
484
+ new UploadPlugin({
485
+ pathColumnName: 'apartment_image',
486
+ s3Bucket: 'tmpbucket-adminforth',
487
+ s3Region: 'eu-central-1',
488
+ allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'exe', 'webp'],
489
+ maxFileSize: 1024 * 1024 * 20, // 5MB
490
+ s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,
491
+ s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
492
+ // s3ACL: 'public-read', // ACL which will be set to uploaded file
493
+ s3Path: ({ originalFilename, originalExtension, contentType }) => `aparts/${new Date().getFullYear()}/${uuid()}/${originalFilename}.${originalExtension}`,
494
+ generation: {
495
+ provider: 'openai-dall-e',
496
+ countToGenerate: 2,
497
+ openAiOptions: {
498
+ model: 'dall-e-3',
499
+ size: '1792x1024',
500
+ apiKey: process.env.OPENAI_API_KEY,
501
+ },
502
+ fieldsForContext: ['title'],
503
+ rateLimit: {
504
+ limit: '2/1m',
505
+ errorMessage: 'For demo purposes, you can generate only 2 images per minute',
506
+ }
507
+ },
508
+ preview: {
509
+ // Used to display preview (if it is image) in list and show views
510
+ // previewUrl: ({s3Path}) => `https://tmpbucket-adminforth.s3.eu-central-1.amazonaws.com/${s3Path}`,
511
+ showInList: true,
512
+ }
513
+ }),
514
+ ] : []),
515
+ new ImportExportPlugin({}),
516
+ // new ChatGptPlugin({
517
+ // openAiApiKey: process.env.OPENAI_API_KEY as string,
518
+ // model: 'gpt-4o',
519
+ // fieldName: 'title',
520
+ // expert: {
521
+ // debounceTime: 250,
522
+ // }
523
+ // }),
524
+ // new ChatGptPlugin({
525
+ // openAiApiKey: process.env.OPENAI_API_KEY as string,
526
+ // fieldName: 'description',
527
+ // model: 'gpt-4o',
528
+ // expert: {
529
+ // debounceTime: 250,
530
+ // }
531
+ // }),
532
+ new RichEditorPlugin(Object.assign({ htmlFieldName: 'description', completion: {
533
+ provider: 'openai-chat-gpt',
534
+ params: {
535
+ apiKey: process.env.OPENAI_API_KEY,
536
+ model: 'gpt-4o',
537
+ },
538
+ expert: {
539
+ debounceTime: 250,
540
+ }
541
+ } }, (process.env.AWS_ACCESS_KEY_ID ? {
542
+ attachments: {
543
+ attachmentResource: 'description_images',
544
+ attachmentFieldName: 'image_path',
545
+ attachmentRecordIdFieldName: 'record_id',
546
+ attachmentResourceIdFieldName: 'resource_id',
547
+ },
548
+ } : {}))),
549
+ ],
550
+ options: {
551
+ listRowsAutoRefreshSeconds: 100,
552
+ pageInjections: {
553
+ list: {
554
+ customActionIcons: '@@/IdShow.vue',
555
+ // bottom: {
556
+ // file: '@@/TopLine.vue',
557
+ // meta: {
558
+ // thinEnoughToShrinkTable: true,
559
+ // }
560
+ // }
561
+ },
562
+ // show: {
563
+ // beforeBreadcrumbs: '@@/TopLine.vue',
564
+ // },
565
+ },
566
+ listPageSize: 25,
567
+ // listTableClickUrl: async (record, adminUser) => null,
568
+ createEditGroups: [
569
+ {
570
+ groupName: 'Main info',
571
+ columns: ['id', 'title', 'description', 'country', 'apartment_image']
572
+ },
573
+ {
574
+ groupName: 'Characteristics',
575
+ columns: ['price', 'square_meter', 'number_of_rooms', "property_type", "listed"]
576
+ }
577
+ ],
578
+ bulkActions: [
579
+ {
580
+ label: 'Mark as listed',
581
+ // icon: 'typcn:archive',
582
+ state: 'active',
583
+ confirm: 'Are you sure you want to mark all selected apartments as listed?',
584
+ action: async function ({ selectedIds, adminUser }) {
585
+ const stmt = db.prepare(`UPDATE apartments SET listed = 1 WHERE id IN (${selectedIds.map(() => '?').join(',')})`);
586
+ await stmt.run(...selectedIds);
587
+ return { ok: true, successMessage: 'Marked as listed' };
588
+ }
589
+ }
590
+ ],
591
+ allowedActions: {
592
+ edit: true,
593
+ delete: async (p) => { return true; },
594
+ show: true,
595
+ filter: true,
596
+ create: true,
597
+ },
598
+ }
599
+ },
600
+ {
601
+ dataSource: 'maindb',
602
+ table: 'users',
603
+ resourceId: 'users',
604
+ label: 'Users',
605
+ recordLabel: (r) => `👤 ${r.email}`,
606
+ plugins: [
607
+ new ForeignInlineListPlugin({
608
+ foreignResourceId: 'aparts',
609
+ modifyTableResourceConfig: (resourceConfig) => {
610
+ // hide column 'square_meter' from both 'list' and 'filter'
611
+ resourceConfig.columns.find((c) => c.name === 'square_meter').showIn = [];
612
+ resourceConfig.options.listPageSize = 3;
613
+ },
614
+ }),
615
+ new ForeignInlineListPlugin({
616
+ foreignResourceId: 'audit_log',
617
+ }),
618
+ new TwoFactorsAuthPlugin({
619
+ twoFaSecretFieldName: 'secret2fa',
620
+ // optional callback to define which users should be enforced to use 2FA
621
+ usersFilterToApply: (adminUser) => {
622
+ if (process.env.NODE_ENV === 'development') {
623
+ return false;
624
+ }
625
+ // return true if user should be enforced to use 2FA,
626
+ // return true;
627
+ return adminUser.dbUser.email !== 'adminforth';
628
+ },
629
+ }),
630
+ new EmailResetPasswordPlugin({
631
+ emailProvider: 'AWS_SES',
632
+ emailField: 'email',
633
+ sendFrom: 'no-reply@devforth.io',
634
+ providerOptions: {
635
+ AWS_SES: {
636
+ region: 'eu-central-1',
637
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
638
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
639
+ }
640
+ },
641
+ passwordField: 'password',
642
+ }),
643
+ ],
644
+ options: {
645
+ allowedActions: {
646
+ create: async ({ adminUser, meta }) => {
647
+ // console.log('create', adminUser, meta);
648
+ return true;
649
+ },
650
+ delete: true,
651
+ },
652
+ },
653
+ columns: [
654
+ {
655
+ name: 'id',
656
+ primaryKey: true,
657
+ fillOnCreate: ({ initialRecord, adminUser }) => uuid(),
658
+ showIn: ['list', 'filter', 'show'], // the default is full set
659
+ },
660
+ {
661
+ name: 'secret2fa',
662
+ type: AdminForthDataTypes.STRING,
663
+ showIn: [],
664
+ backendOnly: true,
665
+ },
666
+ {
667
+ name: 'email',
668
+ isUnique: true,
669
+ required: true,
670
+ enforceLowerCase: true,
671
+ },
672
+ {
673
+ name: 'created_at',
674
+ type: AdminForthDataTypes.DATETIME,
675
+ showIn: ['list', 'filter', 'show'],
676
+ fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
677
+ },
678
+ {
679
+ name: 'password_hash',
680
+ showIn: [],
681
+ backendOnly: true, // will never go to frontend
682
+ },
683
+ {
684
+ name: 'role',
685
+ enum: [
686
+ // { value: 'superadmin', label: 'Super Admin' },
687
+ { value: 'user', label: 'User' },
688
+ ]
689
+ },
690
+ {
691
+ name: 'password',
692
+ virtual: true, // field will not be persisted into db
693
+ required: { create: true }, // to show only in create page
694
+ editingNote: { edit: 'Leave empty to keep password unchanged' },
695
+ minLength: 8,
696
+ validation: [
697
+ AdminForth.Utils.PASSWORD_VALIDATORS.UP_LOW_NUM,
698
+ ],
699
+ type: AdminForthDataTypes.STRING,
700
+ showIn: ['create', 'edit'], // to show in create and edit pages
701
+ masked: true, // to show stars in input field
702
+ }
703
+ ],
704
+ hooks: {
705
+ create: {
706
+ beforeSave: async ({ record, adminUser, resource }) => {
707
+ record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
708
+ return { ok: true, error: "" };
709
+ // if return 'error': , record will not be saved and error will be proxied
710
+ }
711
+ },
712
+ edit: {
713
+ beforeSave: async ({ record, adminUser, resource }) => {
714
+ if (record.password) {
715
+ record.password_hash = await AdminForth.Utils.generatePasswordHash(record.password);
716
+ }
717
+ return { ok: true, error: "" };
718
+ },
719
+ // beforeDatasourceRequest: async ({ query, adminUser, resource }) => {
720
+ // return { ok: true, error: false }
721
+ // },
722
+ // afterDatasourceResponse: async ({ response, adminUser }) => {
723
+ // return { ok: true, error: false }
724
+ // }
725
+ },
726
+ // list: {
727
+ // beforeDatasourceRequest: async ({ query, adminUser }) => {
728
+ // return { ok: true, error: false }
729
+ // },
730
+ // afterDatasourceResponse: async ({ response, adminUser }) => {
731
+ // return { ok: true, error: false }
732
+ // }
733
+ // },
734
+ // show: {
735
+ // beforeDatasourceRequest: async ({ query, adminUser, resource }) => {
736
+ // return { ok: true, error: false }
737
+ // },
738
+ // afterDatasourceResponse: async ({ response, adminUser, resource }) => {
739
+ // return { ok: true, error: false }
740
+ // }
741
+ // },
742
+ }
743
+ },
744
+ {
745
+ dataSource: 'maindb',
746
+ table: 'description_image',
747
+ resourceId: 'description_images',
748
+ label: 'Description images',
749
+ columns: [
750
+ { name: 'id', primaryKey: true, required: false, fillOnCreate: ({ initialRecord }) => uuid() },
751
+ { name: 'created_at', required: false, fillOnCreate: ({ initialRecord }) => (new Date()).toISOString() },
752
+ { name: 'resource_id', required: false },
753
+ { name: 'record_id', required: false },
754
+ { name: 'image_path', required: false },
755
+ ],
756
+ plugins: [
757
+ ...(process.env.AWS_ACCESS_KEY_ID ? [
758
+ new UploadPlugin({
759
+ pathColumnName: 'image_path',
760
+ s3Bucket: 'tmpbucket-adminforth',
761
+ s3Region: 'eu-central-1',
762
+ allowedFileExtensions: ['jpg', 'jpeg', 'png', 'gif', 'webm', 'exe', 'webp'],
763
+ maxFileSize: 1024 * 1024 * 20, // 5MB
764
+ s3AccessKeyId: process.env.AWS_ACCESS_KEY_ID,
765
+ s3SecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
766
+ // rich editor plugin supports only 'public-read' ACL images for SEO purposes (instead of presigned URLs which change every time)
767
+ s3ACL: 'public-read', // ACL which will be set to uploaded file
768
+ s3Path: ({ originalFilename, originalExtension, contentType }) => `description_images/${new Date().getFullYear()}/${uuid()}/${originalFilename}.${originalExtension}`,
769
+ preview: {
770
+ // Used to display preview (if it is image) in list and show views instead of just path
771
+ // previewUrl: ({s3Path}) => `https://tmpbucket-adminforth.s3.eu-central-1.amazonaws.com/${s3Path}`,
772
+ // show image preview instead of path in list view
773
+ // showInList: false,
774
+ }
775
+ }),
776
+ ] : []),
777
+ ],
778
+ },
779
+ {
780
+ dataSource: 'db2', table: 'games',
781
+ resourceId: 'games',
782
+ label: 'Games',
783
+ columns: [
784
+ {
785
+ name: 'id',
786
+ required: false,
787
+ label: 'Identifier', fillOnCreate: ({ initialRecord }) => uuid(),
788
+ showIn: ['list', 'filter', 'show'], // the default is full set
789
+ },
790
+ { name: 'name', required: true, isUnique: true },
791
+ { name: 'created_by', required: true,
792
+ enum: [
793
+ { value: 'CD Projekt Red', label: 'CD Projekt Red' },
794
+ { value: 'Rockstar Studios', label: 'Rockstar' },
795
+ { value: 'Bethesda Game Studios', label: 'Bethesda' },
796
+ ]
797
+ },
798
+ { name: 'release_date', fillOnCreate: ({ initialRecord }) => (new Date()).toISOString() },
799
+ { name: 'release_date2' },
800
+ { name: 'description' },
801
+ { name: 'price' },
802
+ { name: 'enabled' },
803
+ ],
804
+ options: {
805
+ listPageSize: 10,
806
+ },
807
+ },
808
+ {
809
+ dataSource: 'db2', table: 'users',
810
+ resourceId: 'games_users',
811
+ label: 'Games users',
812
+ columns: [
813
+ {
814
+ name: 'id',
815
+ primaryKey: true,
816
+ fillOnCreate: ({ initialRecord, adminUser }) => uuid(),
817
+ showIn: ['list', 'filter', 'show'], // the default is full set
818
+ },
819
+ {
820
+ name: 'email',
821
+ required: true,
822
+ isUnique: true,
823
+ sortable: false,
824
+ },
825
+ {
826
+ name: 'created_at',
827
+ type: AdminForthDataTypes.DATETIME,
828
+ showIn: ['list', 'filter', 'show'],
829
+ fillOnCreate: ({ initialRecord, adminUser }) => (new Date()).toISOString(),
830
+ },
831
+ {
832
+ name: 'role',
833
+ enum: [
834
+ { value: 'superadmin', label: 'Super Admin' },
835
+ { value: 'user', label: 'User' },
836
+ ]
837
+ },
838
+ {
839
+ name: 'password',
840
+ virtual: true, // field will not be persisted into db
841
+ required: { create: true }, // to show only in create page
842
+ editingNote: { edit: 'Leave empty to keep password unchanged' },
843
+ minLength: 8,
844
+ type: AdminForthDataTypes.STRING,
845
+ showIn: ['create', 'edit'], // to show in create and edit pages
846
+ masked: true, // to show stars in input field
847
+ }
848
+ ],
849
+ },
850
+ {
851
+ dataSource: 'db3', table: 'game',
852
+ columns: [
853
+ { name: '_id', primaryKey: true,
854
+ type: AdminForthDataTypes.STRING,
855
+ "maxLength": 255, "required": true, },
856
+ { name: 'bb_enabled',
857
+ "type": AdminForthDataTypes.BOOLEAN,
858
+ "required": false, backendOnly: true
859
+ },
860
+ { name: 'bb_rank',
861
+ "type": AdminForthDataTypes.INTEGER,
862
+ "required": false,
863
+ },
864
+ {
865
+ name: 'blocked_countries',
866
+ "type": AdminForthDataTypes.STRING,
867
+ "maxLength": 255, "required": false,
868
+ enum: [
869
+ { value: 'TR', label: 'Turkey' },
870
+ { value: 'DE', label: 'Germany' },
871
+ { value: 'RU', label: 'Russia' },
872
+ { value: 'US', label: 'United States' },
873
+ { value: 'GB', label: 'United Kingdom' },
874
+ { value: 'FR', label: 'France' },
875
+ { value: 'IT', label: 'Italy' },
876
+ { value: 'ES', label: 'Spain' },
877
+ { value: 'BR', label: 'Brazil' },
878
+ { value: 'CA', label: 'Canada' },
879
+ { value: 'AU', label: 'Australia' },
880
+ { value: 'NL', label: 'Netherlands' },
881
+ { value: 'SE', label: 'Sweden' },
882
+ { value: 'NO', label: 'Norway' },
883
+ { value: 'FI', label: 'Finland' },
884
+ { value: 'DK', label: 'Denmark' },
885
+ { value: 'PL', label: 'Poland' },
886
+ { value: 'CZ', label: 'Czechia' },
887
+ { value: 'SK', label: 'Slovakia' },
888
+ { value: 'HU', label: 'Hungary' },
889
+ { value: 'RO', label: 'Romania' },
890
+ { value: 'BG', label: 'Bulgaria' },
891
+ { value: 'GR', label: 'Greece' },
892
+ { value: 'TR', label: 'Turkey' }
893
+ ]
894
+ },
895
+ { name: 'release_date', "type": AdminForthDataTypes.DATETIME,
896
+ "required": false,
897
+ },
898
+ ]
899
+ }
900
+ ],
901
+ menu: [
902
+ {
903
+ label: 'Dashboard',
904
+ icon: 'flowbite:chart-pie-solid',
905
+ component: '@@/Dash.vue',
906
+ path: '/dashboard',
907
+ // homepage: true,
908
+ isStaticRoute: false,
909
+ meta: {
910
+ title: 'Dashboard',
911
+ }
912
+ },
913
+ {
914
+ label: 'Core',
915
+ icon: 'flowbite:brain-solid', //from here https://icon-sets.iconify.design/flowbite/
916
+ open: true,
917
+ children: [
918
+ {
919
+ label: 'Apartments',
920
+ icon: 'flowbite:home-solid',
921
+ resourceId: 'aparts',
922
+ // badge: async (adminUser) => {
923
+ // return '10'
924
+ // }
925
+ },
926
+ {
927
+ label: 'Description Images',
928
+ resourceId: 'description_images',
929
+ icon: 'flowbite:image-solid',
930
+ },
931
+ // {
932
+ // label: 'Games',
933
+ // icon: 'flowbite:caret-right-solid',
934
+ // resourceId: 'games',
935
+ // },
936
+ // {
937
+ // label: 'Games Users',
938
+ // icon: 'flowbite:user-solid',
939
+ // resourceId: 'games_users',
940
+ // visible:(user) => {
941
+ // return user.dbUser.role === 'superadmin'
942
+ // }
943
+ // },
944
+ // {
945
+ // label: 'Casino Games',
946
+ // icon: 'flowbite:caret-right-solid',
947
+ // resourceId: 'game',
948
+ // },
949
+ {
950
+ label: 'Clicks',
951
+ icon: 'flowbite:search-outline',
952
+ resourceId: 'clicks',
953
+ },
954
+ ]
955
+ },
956
+ {
957
+ type: 'gap'
958
+ },
959
+ {
960
+ type: 'divider'
961
+ },
962
+ {
963
+ type: 'heading',
964
+ label: 'SYSTEM',
965
+ },
966
+ {
967
+ label: 'Users',
968
+ icon: 'flowbite:user-solid',
969
+ resourceId: 'users',
970
+ visible: (user) => {
971
+ return user.dbUser.role === 'superadmin';
972
+ }
973
+ },
974
+ {
975
+ label: 'Logs',
976
+ icon: 'flowbite:search-outline',
977
+ resourceId: 'audit_log',
978
+ },
979
+ ],
980
+ });
981
+ const app = express();
982
+ app.use(express.json());
983
+ const port = 3000;
984
+ (async () => {
985
+ // needed to compile SPA. Call it here or from a build script e.g. in Docker build time to reduce downtime
986
+ await admin.bundleNow({ hotReload: process.env.NODE_ENV === 'development' });
987
+ console.log('Bundling AdminForth done. For faster serving consider calling bundleNow() from a build script.');
988
+ })();
989
+ // add api before .serve
990
+ app.get('/api/testtest/', admin.express.authorize(async (req, res, next) => {
991
+ res.json({ ok: true, data: [1, 2, 3], adminUser: req.adminUser });
992
+ }));
993
+ app.get(`${ADMIN_BASE_URL}/api/dashboard/`, admin.express.authorize(async (req, res) => {
994
+ admin.getPluginByClassName('AuditLogPlugin').logCustomAction('aparts', null, 'visitedDashboard', { dashboard: 'main' }, req.adminUser);
995
+ const days = req.body.days || 7;
996
+ const apartsByDays = await db.prepare(`SELECT
997
+ strftime('%Y-%m-%d', created_at, 'unixepoch') as day,
998
+ COUNT(*) as count
999
+ FROM apartments
1000
+ GROUP BY day
1001
+ ORDER BY day DESC
1002
+ LIMIT ?;
1003
+ `).all(days);
1004
+ const totalAparts = apartsByDays.reduce((acc, { count }) => acc + count, 0);
1005
+ // add listed, unlisted, listedPrice, unlistedPrice
1006
+ const listedVsUnlistedByDays = await db.prepare(`SELECT
1007
+ strftime('%Y-%m-%d', created_at, 'unixepoch') as day,
1008
+ SUM(listed) as listed,
1009
+ COUNT(*) - SUM(listed) as unlisted,
1010
+ SUM(listed * price) as listedPrice,
1011
+ SUM((1 - listed) * price) as unlistedPrice
1012
+ FROM apartments
1013
+ GROUP BY day
1014
+ ORDER BY day DESC
1015
+ LIMIT ?;
1016
+ `).all(days);
1017
+ const listedVsUnlistedPriceByDays = await db.prepare(`SELECT
1018
+ strftime('%Y-%m-%d', created_at, 'unixepoch') as day,
1019
+ SUM(listed * price) as listedPrice,
1020
+ SUM((1 - listed) * price) as unlistedPrice
1021
+ FROM apartments
1022
+ GROUP BY day
1023
+ ORDER BY day DESC
1024
+ LIMIT ?;
1025
+ `).all(days);
1026
+ const totalListedPrice = Math.round(listedVsUnlistedByDays.reduce((acc, { listedPrice }) => acc + listedPrice, 0));
1027
+ const totalUnlistedPrice = Math.round(listedVsUnlistedByDays.reduce((acc, { unlistedPrice }) => acc + unlistedPrice, 0));
1028
+ res.json({
1029
+ apartsByDays,
1030
+ totalAparts,
1031
+ listedVsUnlistedByDays,
1032
+ totalListedPrice,
1033
+ totalUnlistedPrice,
1034
+ listedVsUnlistedPriceByDays,
1035
+ });
1036
+ }));
1037
+ // serve after you added all api
1038
+ admin.express.serve(app);
1039
+ admin.discoverDatabases().then(async () => {
1040
+ if (!await admin.resource('users').get([Filters.EQ('email', 'adminforth')])) {
1041
+ await admin.resource('users').create({
1042
+ email: 'adminforth',
1043
+ password_hash: await AdminForth.Utils.generatePasswordHash('adminforth'),
1044
+ role: 'superadmin',
1045
+ });
1046
+ }
1047
+ });
1048
+ app.listen(port, () => {
1049
+ console.log(`Example app listening at http://localhost:${port}`);
1050
+ console.log(`\n⚡ AdminForth is available at http://localhost:${port}${ADMIN_BASE_URL}\n`);
1051
+ });
1052
+ //# sourceMappingURL=index.js.map