@budibase/backend-core 2.9.40-alpha.6 → 2.10.1

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 (252) hide show
  1. package/dist/index.js +5 -4
  2. package/dist/index.js.map +2 -2
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +6 -6
  5. package/dist/src/cache/appMetadata.js +1 -1
  6. package/dist/src/cache/appMetadata.js.map +1 -1
  7. package/dist/src/constants/misc.d.ts +0 -2
  8. package/dist/src/constants/misc.js +0 -2
  9. package/dist/src/constants/misc.js.map +1 -1
  10. package/dist/src/environment.js +5 -4
  11. package/dist/src/environment.js.map +1 -1
  12. package/dist/src/logging/system.d.ts +1 -1
  13. package/dist/src/timers/timers.d.ts +1 -1
  14. package/package.json +6 -6
  15. package/src/accounts/accounts.ts +82 -0
  16. package/src/accounts/api.ts +59 -0
  17. package/src/accounts/index.ts +1 -0
  18. package/src/auth/auth.ts +208 -0
  19. package/src/auth/index.ts +1 -0
  20. package/src/auth/tests/auth.spec.ts +14 -0
  21. package/src/blacklist/blacklist.ts +54 -0
  22. package/src/blacklist/index.ts +1 -0
  23. package/src/blacklist/tests/blacklist.spec.ts +46 -0
  24. package/src/cache/appMetadata.ts +88 -0
  25. package/src/cache/base/index.ts +92 -0
  26. package/src/cache/generic.ts +30 -0
  27. package/src/cache/index.ts +5 -0
  28. package/src/cache/tests/writethrough.spec.ts +138 -0
  29. package/src/cache/user.ts +83 -0
  30. package/src/cache/writethrough.ts +133 -0
  31. package/src/configs/configs.ts +257 -0
  32. package/src/configs/index.ts +1 -0
  33. package/src/configs/tests/configs.spec.ts +184 -0
  34. package/src/constants/db.ts +63 -0
  35. package/src/constants/index.ts +2 -0
  36. package/src/constants/misc.ts +50 -0
  37. package/src/context/Context.ts +14 -0
  38. package/src/context/identity.ts +58 -0
  39. package/src/context/index.ts +3 -0
  40. package/src/context/mainContext.ts +310 -0
  41. package/src/context/tests/index.spec.ts +147 -0
  42. package/src/context/types.ts +11 -0
  43. package/src/db/Replication.ts +84 -0
  44. package/src/db/constants.ts +10 -0
  45. package/src/db/couch/DatabaseImpl.ts +238 -0
  46. package/src/db/couch/connections.ts +77 -0
  47. package/src/db/couch/index.ts +5 -0
  48. package/src/db/couch/pouchDB.ts +97 -0
  49. package/src/db/couch/pouchDump.ts +0 -0
  50. package/src/db/couch/utils.ts +50 -0
  51. package/src/db/db.ts +43 -0
  52. package/src/db/errors.ts +14 -0
  53. package/src/db/index.ts +12 -0
  54. package/src/db/lucene.ts +750 -0
  55. package/src/db/searchIndexes/index.ts +1 -0
  56. package/src/db/searchIndexes/searchIndexes.ts +62 -0
  57. package/src/db/tests/index.spec.js +25 -0
  58. package/src/db/tests/lucene.spec.ts +368 -0
  59. package/src/db/tests/pouch.spec.js +62 -0
  60. package/src/db/tests/utils.spec.ts +63 -0
  61. package/src/db/utils.ts +207 -0
  62. package/src/db/views.ts +241 -0
  63. package/src/docIds/conversions.ts +59 -0
  64. package/src/docIds/ids.ts +113 -0
  65. package/src/docIds/index.ts +2 -0
  66. package/src/docIds/newid.ts +5 -0
  67. package/src/docIds/params.ts +174 -0
  68. package/src/docUpdates/index.ts +29 -0
  69. package/src/environment.ts +201 -0
  70. package/src/errors/errors.ts +119 -0
  71. package/src/errors/index.ts +1 -0
  72. package/src/events/analytics.ts +6 -0
  73. package/src/events/asyncEvents/index.ts +2 -0
  74. package/src/events/asyncEvents/publisher.ts +12 -0
  75. package/src/events/asyncEvents/queue.ts +22 -0
  76. package/src/events/backfill.ts +183 -0
  77. package/src/events/documentId.ts +56 -0
  78. package/src/events/events.ts +40 -0
  79. package/src/events/identification.ts +310 -0
  80. package/src/events/index.ts +14 -0
  81. package/src/events/processors/AnalyticsProcessor.ts +64 -0
  82. package/src/events/processors/AuditLogsProcessor.ts +93 -0
  83. package/src/events/processors/LoggingProcessor.ts +37 -0
  84. package/src/events/processors/Processors.ts +52 -0
  85. package/src/events/processors/async/DocumentUpdateProcessor.ts +43 -0
  86. package/src/events/processors/index.ts +19 -0
  87. package/src/events/processors/posthog/PosthogProcessor.ts +118 -0
  88. package/src/events/processors/posthog/index.ts +2 -0
  89. package/src/events/processors/posthog/rateLimiting.ts +106 -0
  90. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +168 -0
  91. package/src/events/processors/types.ts +1 -0
  92. package/src/events/publishers/account.ts +35 -0
  93. package/src/events/publishers/app.ts +155 -0
  94. package/src/events/publishers/auditLog.ts +26 -0
  95. package/src/events/publishers/auth.ts +73 -0
  96. package/src/events/publishers/automation.ts +110 -0
  97. package/src/events/publishers/backfill.ts +74 -0
  98. package/src/events/publishers/backup.ts +42 -0
  99. package/src/events/publishers/datasource.ts +48 -0
  100. package/src/events/publishers/email.ts +17 -0
  101. package/src/events/publishers/environmentVariable.ts +38 -0
  102. package/src/events/publishers/group.ts +99 -0
  103. package/src/events/publishers/index.ts +24 -0
  104. package/src/events/publishers/installation.ts +38 -0
  105. package/src/events/publishers/layout.ts +26 -0
  106. package/src/events/publishers/license.ts +84 -0
  107. package/src/events/publishers/org.ts +37 -0
  108. package/src/events/publishers/plugin.ts +47 -0
  109. package/src/events/publishers/query.ts +88 -0
  110. package/src/events/publishers/role.ts +62 -0
  111. package/src/events/publishers/rows.ts +29 -0
  112. package/src/events/publishers/screen.ts +36 -0
  113. package/src/events/publishers/serve.ts +43 -0
  114. package/src/events/publishers/table.ts +70 -0
  115. package/src/events/publishers/user.ts +202 -0
  116. package/src/events/publishers/view.ts +107 -0
  117. package/src/features/index.ts +78 -0
  118. package/src/features/installation.ts +17 -0
  119. package/src/features/tests/featureFlags.spec.ts +85 -0
  120. package/src/helpers.ts +9 -0
  121. package/src/index.ts +54 -0
  122. package/src/installation.ts +107 -0
  123. package/src/logging/alerts.ts +26 -0
  124. package/src/logging/correlation/correlation.ts +13 -0
  125. package/src/logging/correlation/index.ts +1 -0
  126. package/src/logging/correlation/middleware.ts +17 -0
  127. package/src/logging/index.ts +4 -0
  128. package/src/logging/pino/logger.ts +232 -0
  129. package/src/logging/pino/middleware.ts +45 -0
  130. package/src/logging/system.ts +81 -0
  131. package/src/logging/tests/system.spec.ts +61 -0
  132. package/src/middleware/adminOnly.ts +9 -0
  133. package/src/middleware/auditLog.ts +6 -0
  134. package/src/middleware/authenticated.ts +193 -0
  135. package/src/middleware/builderOnly.ts +21 -0
  136. package/src/middleware/builderOrAdmin.ts +21 -0
  137. package/src/middleware/csrf.ts +81 -0
  138. package/src/middleware/errorHandling.ts +29 -0
  139. package/src/middleware/index.ts +21 -0
  140. package/src/middleware/internalApi.ts +23 -0
  141. package/src/middleware/joi-validator.ts +45 -0
  142. package/src/middleware/matchers.ts +47 -0
  143. package/src/middleware/passport/datasource/google.ts +95 -0
  144. package/src/middleware/passport/local.ts +54 -0
  145. package/src/middleware/passport/sso/google.ts +77 -0
  146. package/src/middleware/passport/sso/oidc.ts +154 -0
  147. package/src/middleware/passport/sso/sso.ts +165 -0
  148. package/src/middleware/passport/sso/tests/google.spec.ts +67 -0
  149. package/src/middleware/passport/sso/tests/oidc.spec.ts +152 -0
  150. package/src/middleware/passport/sso/tests/sso.spec.ts +197 -0
  151. package/src/middleware/passport/utils.ts +38 -0
  152. package/src/middleware/querystringToBody.ts +28 -0
  153. package/src/middleware/tenancy.ts +36 -0
  154. package/src/middleware/tests/builder.spec.ts +180 -0
  155. package/src/middleware/tests/matchers.spec.ts +134 -0
  156. package/src/migrations/definitions.ts +40 -0
  157. package/src/migrations/index.ts +2 -0
  158. package/src/migrations/migrations.ts +191 -0
  159. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +11 -0
  160. package/src/migrations/tests/migrations.spec.ts +64 -0
  161. package/src/objectStore/buckets/app.ts +40 -0
  162. package/src/objectStore/buckets/global.ts +29 -0
  163. package/src/objectStore/buckets/index.ts +3 -0
  164. package/src/objectStore/buckets/plugins.ts +71 -0
  165. package/src/objectStore/buckets/tests/app.spec.ts +171 -0
  166. package/src/objectStore/buckets/tests/global.spec.ts +74 -0
  167. package/src/objectStore/buckets/tests/plugins.spec.ts +111 -0
  168. package/src/objectStore/cloudfront.ts +41 -0
  169. package/src/objectStore/index.ts +3 -0
  170. package/src/objectStore/objectStore.ts +440 -0
  171. package/src/objectStore/utils.ts +27 -0
  172. package/src/platform/index.ts +3 -0
  173. package/src/platform/platformDb.ts +6 -0
  174. package/src/platform/tenants.ts +101 -0
  175. package/src/platform/tests/tenants.spec.ts +26 -0
  176. package/src/platform/users.ts +90 -0
  177. package/src/plugin/index.ts +1 -0
  178. package/src/plugin/tests/validation.spec.ts +83 -0
  179. package/src/plugin/utils.ts +156 -0
  180. package/src/queue/constants.ts +6 -0
  181. package/src/queue/inMemoryQueue.ts +141 -0
  182. package/src/queue/index.ts +2 -0
  183. package/src/queue/listeners.ts +195 -0
  184. package/src/queue/queue.ts +54 -0
  185. package/src/redis/index.ts +6 -0
  186. package/src/redis/init.ts +86 -0
  187. package/src/redis/redis.ts +308 -0
  188. package/src/redis/redlockImpl.ts +139 -0
  189. package/src/redis/utils.ts +117 -0
  190. package/src/security/encryption.ts +179 -0
  191. package/src/security/permissions.ts +158 -0
  192. package/src/security/roles.ts +389 -0
  193. package/src/security/sessions.ts +120 -0
  194. package/src/security/tests/encryption.spec.ts +31 -0
  195. package/src/security/tests/permissions.spec.ts +145 -0
  196. package/src/security/tests/sessions.spec.ts +12 -0
  197. package/src/tenancy/db.ts +6 -0
  198. package/src/tenancy/index.ts +2 -0
  199. package/src/tenancy/tenancy.ts +140 -0
  200. package/src/tenancy/tests/tenancy.spec.ts +184 -0
  201. package/src/timers/index.ts +1 -0
  202. package/src/timers/timers.ts +22 -0
  203. package/src/users/db.ts +484 -0
  204. package/src/users/events.ts +176 -0
  205. package/src/users/index.ts +4 -0
  206. package/src/users/lookup.ts +102 -0
  207. package/src/users/users.ts +276 -0
  208. package/src/users/utils.ts +55 -0
  209. package/src/utils/hashing.ts +14 -0
  210. package/src/utils/index.ts +3 -0
  211. package/src/utils/stringUtils.ts +8 -0
  212. package/src/utils/tests/utils.spec.ts +191 -0
  213. package/src/utils/utils.ts +239 -0
  214. package/tests/core/logging.ts +34 -0
  215. package/tests/core/utilities/index.ts +6 -0
  216. package/tests/core/utilities/jestUtils.ts +30 -0
  217. package/tests/core/utilities/mocks/alerts.ts +3 -0
  218. package/tests/core/utilities/mocks/date.ts +2 -0
  219. package/tests/core/utilities/mocks/events.ts +131 -0
  220. package/tests/core/utilities/mocks/fetch.ts +17 -0
  221. package/tests/core/utilities/mocks/index.ts +10 -0
  222. package/tests/core/utilities/mocks/licenses.ts +115 -0
  223. package/tests/core/utilities/mocks/posthog.ts +7 -0
  224. package/tests/core/utilities/structures/Chance.ts +20 -0
  225. package/tests/core/utilities/structures/accounts.ts +115 -0
  226. package/tests/core/utilities/structures/apps.ts +21 -0
  227. package/tests/core/utilities/structures/common.ts +7 -0
  228. package/tests/core/utilities/structures/db.ts +12 -0
  229. package/tests/core/utilities/structures/documents/index.ts +1 -0
  230. package/tests/core/utilities/structures/documents/platform/index.ts +1 -0
  231. package/tests/core/utilities/structures/documents/platform/installation.ts +12 -0
  232. package/tests/core/utilities/structures/generator.ts +2 -0
  233. package/tests/core/utilities/structures/index.ts +15 -0
  234. package/tests/core/utilities/structures/koa.ts +16 -0
  235. package/tests/core/utilities/structures/licenses.ts +167 -0
  236. package/tests/core/utilities/structures/plugins.ts +19 -0
  237. package/tests/core/utilities/structures/quotas.ts +67 -0
  238. package/tests/core/utilities/structures/scim.ts +80 -0
  239. package/tests/core/utilities/structures/shared.ts +19 -0
  240. package/tests/core/utilities/structures/sso.ts +119 -0
  241. package/tests/core/utilities/structures/tenants.ts +5 -0
  242. package/tests/core/utilities/structures/userGroups.ts +10 -0
  243. package/tests/core/utilities/structures/users.ts +73 -0
  244. package/tests/core/utilities/testContainerUtils.ts +85 -0
  245. package/tests/core/utilities/utils/index.ts +1 -0
  246. package/tests/core/utilities/utils/time.ts +3 -0
  247. package/tests/extra/DBTestConfiguration.ts +36 -0
  248. package/tests/extra/index.ts +2 -0
  249. package/tests/extra/testEnv.ts +95 -0
  250. package/tests/index.ts +1 -0
  251. package/tests/jestEnv.ts +6 -0
  252. package/tests/jestSetup.ts +28 -0
@@ -0,0 +1,202 @@
1
+ import { publishEvent } from "../events"
2
+ import {
3
+ Event,
4
+ User,
5
+ UserCreatedEvent,
6
+ UserDataCollaborationEvent,
7
+ UserDeletedEvent,
8
+ UserInviteAcceptedEvent,
9
+ UserInvitedEvent,
10
+ UserPasswordForceResetEvent,
11
+ UserPasswordResetEvent,
12
+ UserPasswordResetRequestedEvent,
13
+ UserPasswordUpdatedEvent,
14
+ UserPermissionAssignedEvent,
15
+ UserPermissionRemovedEvent,
16
+ UserUpdatedEvent,
17
+ UserOnboardingEvent,
18
+ } from "@budibase/types"
19
+ import { isScim } from "../../context"
20
+
21
+ async function created(user: User, timestamp?: number) {
22
+ const properties: UserCreatedEvent = {
23
+ userId: user._id as string,
24
+ viaScim: isScim(),
25
+ audited: {
26
+ email: user.email,
27
+ },
28
+ }
29
+ await publishEvent(Event.USER_CREATED, properties, timestamp)
30
+ }
31
+
32
+ async function updated(user: User) {
33
+ const properties: UserUpdatedEvent = {
34
+ userId: user._id as string,
35
+ viaScim: isScim(),
36
+ audited: {
37
+ email: user.email,
38
+ },
39
+ }
40
+ await publishEvent(Event.USER_UPDATED, properties)
41
+ }
42
+
43
+ async function deleted(user: User) {
44
+ const properties: UserDeletedEvent = {
45
+ userId: user._id as string,
46
+ viaScim: isScim(),
47
+ audited: {
48
+ email: user.email,
49
+ },
50
+ }
51
+ await publishEvent(Event.USER_DELETED, properties)
52
+ }
53
+
54
+ export async function onboardingComplete(user: User) {
55
+ const properties: UserOnboardingEvent = {
56
+ userId: user._id as string,
57
+ audited: {
58
+ email: user.email,
59
+ },
60
+ }
61
+ await publishEvent(Event.USER_ONBOARDING_COMPLETE, properties)
62
+ }
63
+
64
+ // PERMISSIONS
65
+
66
+ async function permissionAdminAssigned(user: User, timestamp?: number) {
67
+ const properties: UserPermissionAssignedEvent = {
68
+ userId: user._id as string,
69
+ audited: {
70
+ email: user.email,
71
+ },
72
+ }
73
+ await publishEvent(
74
+ Event.USER_PERMISSION_ADMIN_ASSIGNED,
75
+ properties,
76
+ timestamp
77
+ )
78
+ }
79
+
80
+ async function permissionAdminRemoved(user: User) {
81
+ const properties: UserPermissionRemovedEvent = {
82
+ userId: user._id as string,
83
+ audited: {
84
+ email: user.email,
85
+ },
86
+ }
87
+ await publishEvent(Event.USER_PERMISSION_ADMIN_REMOVED, properties)
88
+ }
89
+
90
+ async function permissionBuilderAssigned(user: User, timestamp?: number) {
91
+ const properties: UserPermissionAssignedEvent = {
92
+ userId: user._id as string,
93
+ audited: {
94
+ email: user.email,
95
+ },
96
+ }
97
+ await publishEvent(
98
+ Event.USER_PERMISSION_BUILDER_ASSIGNED,
99
+ properties,
100
+ timestamp
101
+ )
102
+ }
103
+
104
+ async function permissionBuilderRemoved(user: User) {
105
+ const properties: UserPermissionRemovedEvent = {
106
+ userId: user._id as string,
107
+ audited: {
108
+ email: user.email,
109
+ },
110
+ }
111
+ await publishEvent(Event.USER_PERMISSION_BUILDER_REMOVED, properties)
112
+ }
113
+
114
+ // INVITE
115
+
116
+ async function invited(email: string) {
117
+ const properties: UserInvitedEvent = {
118
+ audited: {
119
+ email,
120
+ },
121
+ }
122
+ await publishEvent(Event.USER_INVITED, properties)
123
+ }
124
+
125
+ async function inviteAccepted(user: User) {
126
+ const properties: UserInviteAcceptedEvent = {
127
+ userId: user._id as string,
128
+ audited: {
129
+ email: user.email,
130
+ },
131
+ }
132
+ await publishEvent(Event.USER_INVITED_ACCEPTED, properties)
133
+ }
134
+
135
+ // PASSWORD
136
+
137
+ async function passwordForceReset(user: User) {
138
+ const properties: UserPasswordForceResetEvent = {
139
+ userId: user._id as string,
140
+ audited: {
141
+ email: user.email,
142
+ },
143
+ }
144
+ await publishEvent(Event.USER_PASSWORD_FORCE_RESET, properties)
145
+ }
146
+
147
+ async function passwordUpdated(user: User) {
148
+ const properties: UserPasswordUpdatedEvent = {
149
+ userId: user._id as string,
150
+ audited: {
151
+ email: user.email,
152
+ },
153
+ }
154
+ await publishEvent(Event.USER_PASSWORD_UPDATED, properties)
155
+ }
156
+
157
+ async function passwordResetRequested(user: User) {
158
+ const properties: UserPasswordResetRequestedEvent = {
159
+ userId: user._id as string,
160
+ audited: {
161
+ email: user.email,
162
+ },
163
+ }
164
+ await publishEvent(Event.USER_PASSWORD_RESET_REQUESTED, properties)
165
+ }
166
+
167
+ async function passwordReset(user: User) {
168
+ const properties: UserPasswordResetEvent = {
169
+ userId: user._id as string,
170
+ audited: {
171
+ email: user.email,
172
+ },
173
+ }
174
+ await publishEvent(Event.USER_PASSWORD_RESET, properties)
175
+ }
176
+
177
+ // COLLABORATION
178
+
179
+ async function dataCollaboration(users: number) {
180
+ const properties: UserDataCollaborationEvent = {
181
+ users,
182
+ }
183
+ await publishEvent(Event.USER_DATA_COLLABORATION, properties)
184
+ }
185
+
186
+ export default {
187
+ created,
188
+ updated,
189
+ deleted,
190
+ permissionAdminAssigned,
191
+ permissionAdminRemoved,
192
+ permissionBuilderAssigned,
193
+ permissionBuilderRemoved,
194
+ onboardingComplete,
195
+ invited,
196
+ inviteAccepted,
197
+ passwordForceReset,
198
+ passwordUpdated,
199
+ passwordResetRequested,
200
+ passwordReset,
201
+ dataCollaboration,
202
+ }
@@ -0,0 +1,107 @@
1
+ import { publishEvent } from "../events"
2
+ import {
3
+ Event,
4
+ ViewCalculationCreatedEvent,
5
+ ViewCalculationDeletedEvent,
6
+ ViewCalculationUpdatedEvent,
7
+ ViewCreatedEvent,
8
+ ViewDeletedEvent,
9
+ ViewExportedEvent,
10
+ ViewFilterCreatedEvent,
11
+ ViewFilterDeletedEvent,
12
+ ViewFilterUpdatedEvent,
13
+ ViewUpdatedEvent,
14
+ View,
15
+ ViewCalculation,
16
+ Table,
17
+ TableExportFormat,
18
+ } from "@budibase/types"
19
+
20
+ /* eslint-disable */
21
+
22
+ async function created(view: View, timestamp?: string | number) {
23
+ const properties: ViewCreatedEvent = {
24
+ tableId: view.tableId,
25
+ }
26
+ await publishEvent(Event.VIEW_CREATED, properties, timestamp)
27
+ }
28
+
29
+ async function updated(view: View) {
30
+ const properties: ViewUpdatedEvent = {
31
+ tableId: view.tableId,
32
+ }
33
+ await publishEvent(Event.VIEW_UPDATED, properties)
34
+ }
35
+
36
+ async function deleted(view: View) {
37
+ const properties: ViewDeletedEvent = {
38
+ tableId: view.tableId,
39
+ }
40
+ await publishEvent(Event.VIEW_DELETED, properties)
41
+ }
42
+
43
+ async function exported(table: Table, format: TableExportFormat) {
44
+ const properties: ViewExportedEvent = {
45
+ tableId: table._id as string,
46
+ format,
47
+ }
48
+ await publishEvent(Event.VIEW_EXPORTED, properties)
49
+ }
50
+
51
+ async function filterCreated(view: View, timestamp?: string | number) {
52
+ const properties: ViewFilterCreatedEvent = {
53
+ tableId: view.tableId,
54
+ }
55
+ await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp)
56
+ }
57
+
58
+ async function filterUpdated(view: View) {
59
+ const properties: ViewFilterUpdatedEvent = {
60
+ tableId: view.tableId,
61
+ }
62
+ await publishEvent(Event.VIEW_FILTER_UPDATED, properties)
63
+ }
64
+
65
+ async function filterDeleted(view: View) {
66
+ const properties: ViewFilterDeletedEvent = {
67
+ tableId: view.tableId,
68
+ }
69
+ await publishEvent(Event.VIEW_FILTER_DELETED, properties)
70
+ }
71
+
72
+ async function calculationCreated(view: View, timestamp?: string | number) {
73
+ const properties: ViewCalculationCreatedEvent = {
74
+ tableId: view.tableId,
75
+ calculation: view.calculation as ViewCalculation,
76
+ }
77
+ await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp)
78
+ }
79
+
80
+ async function calculationUpdated(view: View) {
81
+ const properties: ViewCalculationUpdatedEvent = {
82
+ tableId: view.tableId,
83
+ calculation: view.calculation as ViewCalculation,
84
+ }
85
+ await publishEvent(Event.VIEW_CALCULATION_UPDATED, properties)
86
+ }
87
+
88
+ async function calculationDeleted(existingView: View) {
89
+ const properties: ViewCalculationDeletedEvent = {
90
+ tableId: existingView.tableId,
91
+ calculation: existingView.calculation as ViewCalculation,
92
+ }
93
+ await publishEvent(Event.VIEW_CALCULATION_DELETED, properties)
94
+ }
95
+
96
+ export default {
97
+ created,
98
+ updated,
99
+ deleted,
100
+ exported,
101
+ filterCreated,
102
+ filterUpdated,
103
+ filterDeleted,
104
+ calculationCreated,
105
+ calculationUpdated,
106
+ calculationDeleted,
107
+ }
@@ -0,0 +1,78 @@
1
+ import env from "../environment"
2
+ import * as context from "../context"
3
+ export * from "./installation"
4
+
5
+ /**
6
+ * Read the TENANT_FEATURE_FLAGS env var and return an array of features flags for each tenant.
7
+ * The env var is formatted as:
8
+ * tenant1:feature1:feature2,tenant2:feature1
9
+ */
10
+ export function buildFeatureFlags() {
11
+ if (!env.TENANT_FEATURE_FLAGS) {
12
+ return
13
+ }
14
+
15
+ const tenantFeatureFlags: Record<string, string[]> = {}
16
+
17
+ env.TENANT_FEATURE_FLAGS.split(",").forEach(tenantToFeatures => {
18
+ const [tenantId, ...features] = tenantToFeatures.split(":")
19
+
20
+ features.forEach(feature => {
21
+ if (!tenantFeatureFlags[tenantId]) {
22
+ tenantFeatureFlags[tenantId] = []
23
+ }
24
+ tenantFeatureFlags[tenantId].push(feature)
25
+ })
26
+ })
27
+
28
+ return tenantFeatureFlags
29
+ }
30
+
31
+ export function isEnabled(featureFlag: string) {
32
+ const tenantId = context.getTenantId()
33
+ const flags = getTenantFeatureFlags(tenantId)
34
+ return flags.includes(featureFlag)
35
+ }
36
+
37
+ export function getTenantFeatureFlags(tenantId: string) {
38
+ let flags: string[] = []
39
+ const envFlags = buildFeatureFlags()
40
+ if (envFlags) {
41
+ const globalFlags = envFlags["*"]
42
+ const tenantFlags = envFlags[tenantId] || []
43
+
44
+ // Explicitly exclude tenants from global features if required.
45
+ // Prefix the tenant flag with '!'
46
+ const tenantOverrides = tenantFlags.reduce(
47
+ (acc: string[], flag: string) => {
48
+ if (flag.startsWith("!")) {
49
+ let stripped = flag.substring(1)
50
+ acc.push(stripped)
51
+ }
52
+ return acc
53
+ },
54
+ []
55
+ )
56
+
57
+ if (globalFlags) {
58
+ flags.push(...globalFlags)
59
+ }
60
+ if (tenantFlags.length) {
61
+ flags.push(...tenantFlags)
62
+ }
63
+
64
+ // Purge any tenant specific overrides
65
+ flags = flags.filter(flag => {
66
+ return tenantOverrides.indexOf(flag) == -1 && !flag.startsWith("!")
67
+ })
68
+ }
69
+
70
+ return flags
71
+ }
72
+
73
+ export enum TenantFeatureFlag {
74
+ LICENSING = "LICENSING",
75
+ GOOGLE_SHEETS = "GOOGLE_SHEETS",
76
+ USER_GROUPS = "USER_GROUPS",
77
+ ONBOARDING_TOUR = "ONBOARDING_TOUR",
78
+ }
@@ -0,0 +1,17 @@
1
+ export function processFeatureEnvVar<T>(
2
+ fullList: string[],
3
+ featureList?: string
4
+ ) {
5
+ let list
6
+ if (!featureList) {
7
+ list = fullList
8
+ } else {
9
+ list = featureList.split(",")
10
+ }
11
+ for (let feature of list) {
12
+ if (!fullList.includes(feature)) {
13
+ throw new Error(`Feature: ${feature} is not an allowed option`)
14
+ }
15
+ }
16
+ return list as unknown as T[]
17
+ }
@@ -0,0 +1,85 @@
1
+ import {
2
+ TenantFeatureFlag,
3
+ buildFeatureFlags,
4
+ getTenantFeatureFlags,
5
+ } from "../"
6
+ import env from "../../environment"
7
+
8
+ const { ONBOARDING_TOUR, LICENSING, USER_GROUPS } = TenantFeatureFlag
9
+
10
+ describe("featureFlags", () => {
11
+ beforeEach(() => {
12
+ env._set("TENANT_FEATURE_FLAGS", "")
13
+ })
14
+
15
+ it("Should return no flags when the TENANT_FEATURE_FLAG is empty", async () => {
16
+ let features = buildFeatureFlags()
17
+ expect(features).toBeUndefined()
18
+ })
19
+
20
+ it("Should generate a map of global and named tenant feature flags from the env value", async () => {
21
+ env._set(
22
+ "TENANT_FEATURE_FLAGS",
23
+ `*:${ONBOARDING_TOUR},tenant1:!${ONBOARDING_TOUR},tenant2:${USER_GROUPS},tenant1:${LICENSING}`
24
+ )
25
+
26
+ const parsedFlags: Record<string, string[]> = {
27
+ "*": [ONBOARDING_TOUR],
28
+ tenant1: [`!${ONBOARDING_TOUR}`, LICENSING],
29
+ tenant2: [USER_GROUPS],
30
+ }
31
+
32
+ let features = buildFeatureFlags()
33
+
34
+ expect(features).toBeDefined()
35
+ expect(features).toEqual(parsedFlags)
36
+ })
37
+
38
+ it("Should add feature flag flag only to explicitly configured tenant", async () => {
39
+ env._set(
40
+ "TENANT_FEATURE_FLAGS",
41
+ `*:${LICENSING},*:${USER_GROUPS},tenant1:${ONBOARDING_TOUR}`
42
+ )
43
+
44
+ let tenant1Flags = getTenantFeatureFlags("tenant1")
45
+ let tenant2Flags = getTenantFeatureFlags("tenant2")
46
+
47
+ expect(tenant1Flags).toBeDefined()
48
+ expect(tenant1Flags).toEqual([LICENSING, USER_GROUPS, ONBOARDING_TOUR])
49
+
50
+ expect(tenant2Flags).toBeDefined()
51
+ expect(tenant2Flags).toEqual([LICENSING, USER_GROUPS])
52
+ })
53
+ })
54
+
55
+ it("Should exclude tenant1 from global feature flag", async () => {
56
+ env._set(
57
+ "TENANT_FEATURE_FLAGS",
58
+ `*:${LICENSING},*:${ONBOARDING_TOUR},tenant1:!${ONBOARDING_TOUR}`
59
+ )
60
+
61
+ let tenant1Flags = getTenantFeatureFlags("tenant1")
62
+ let tenant2Flags = getTenantFeatureFlags("tenant2")
63
+
64
+ expect(tenant1Flags).toBeDefined()
65
+ expect(tenant1Flags).toEqual([LICENSING])
66
+
67
+ expect(tenant2Flags).toBeDefined()
68
+ expect(tenant2Flags).toEqual([LICENSING, ONBOARDING_TOUR])
69
+ })
70
+
71
+ it("Should explicitly add flags to configured tenants only", async () => {
72
+ env._set(
73
+ "TENANT_FEATURE_FLAGS",
74
+ `tenant1:${ONBOARDING_TOUR},tenant1:${LICENSING},tenant2:${LICENSING}`
75
+ )
76
+
77
+ let tenant1Flags = getTenantFeatureFlags("tenant1")
78
+ let tenant2Flags = getTenantFeatureFlags("tenant2")
79
+
80
+ expect(tenant1Flags).toBeDefined()
81
+ expect(tenant1Flags).toEqual([ONBOARDING_TOUR, LICENSING])
82
+
83
+ expect(tenant2Flags).toBeDefined()
84
+ expect(tenant2Flags).toEqual([LICENSING])
85
+ })
package/src/helpers.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Makes sure that a URL has the correct number of slashes, while maintaining the
3
+ * http(s):// double slashes.
4
+ * @param {string} url The URL to test and remove any extra double slashes.
5
+ * @return {string} The updated url.
6
+ */
7
+ export function checkSlashesInUrl(url: string) {
8
+ return url.replace(/(https?:\/\/)|(\/)+/g, "$1$2")
9
+ }
package/src/index.ts ADDED
@@ -0,0 +1,54 @@
1
+ export * as configs from "./configs"
2
+ export * as events from "./events"
3
+ export * as migrations from "./migrations"
4
+ export * as users from "./users"
5
+ export * as roles from "./security/roles"
6
+ export * as permissions from "./security/permissions"
7
+ export * as accounts from "./accounts"
8
+ export * as installation from "./installation"
9
+ export * as featureFlags from "./features"
10
+ export * as features from "./features/installation"
11
+ export * as sessions from "./security/sessions"
12
+ export * as platform from "./platform"
13
+ export * as auth from "./auth"
14
+ export * as constants from "./constants"
15
+ export * as logging from "./logging"
16
+ export * as middleware from "./middleware"
17
+ export * as plugins from "./plugin"
18
+ export * as encryption from "./security/encryption"
19
+ export * as queue from "./queue"
20
+ export * as db from "./db"
21
+ export * as context from "./context"
22
+ export * as cache from "./cache"
23
+ export * as objectStore from "./objectStore"
24
+ export * as redis from "./redis"
25
+ export { Client as RedisClient } from "./redis"
26
+ export * as locks from "./redis/redlockImpl"
27
+ export * as utils from "./utils"
28
+ export * as errors from "./errors"
29
+ export * as timers from "./timers"
30
+ export { default as env } from "./environment"
31
+ export * as blacklist from "./blacklist"
32
+ export * as docUpdates from "./docUpdates"
33
+ export { SearchParams } from "./db"
34
+ // Add context to tenancy for backwards compatibility
35
+ // only do this for external usages to prevent internal
36
+ // circular dependencies
37
+ import * as context from "./context"
38
+ import * as _tenancy from "./tenancy"
39
+ export const tenancy = {
40
+ ..._tenancy,
41
+ ...context,
42
+ }
43
+
44
+ // expose error classes directly
45
+ export * from "./errors"
46
+
47
+ // expose constants directly
48
+ export * from "./constants"
49
+
50
+ // expose package init function
51
+ import * as db from "./db"
52
+ export const init = (opts: any = {}) => {
53
+ db.init(opts.db)
54
+ }
@@ -0,0 +1,107 @@
1
+ import { newid } from "./utils"
2
+ import * as events from "./events"
3
+ import { StaticDatabases } from "./db"
4
+ import { doWithDB } from "./db"
5
+ import { Installation, IdentityType, Database } from "@budibase/types"
6
+ import * as context from "./context"
7
+ import semver from "semver"
8
+ import { bustCache, withCache, TTL, CacheKey } from "./cache/generic"
9
+ import environment from "./environment"
10
+
11
+ export const getInstall = async (): Promise<Installation> => {
12
+ return withCache(CacheKey.INSTALLATION, TTL.ONE_DAY, getInstallFromDB, {
13
+ useTenancy: false,
14
+ })
15
+ }
16
+ async function createInstallDoc(platformDb: Database) {
17
+ const install: Installation = {
18
+ _id: StaticDatabases.PLATFORM_INFO.docs.install,
19
+ installId: newid(),
20
+ version: environment.VERSION,
21
+ }
22
+ try {
23
+ const resp = await platformDb.put(install)
24
+ install._rev = resp.rev
25
+ return install
26
+ } catch (err: any) {
27
+ if (err.status === 409) {
28
+ return getInstallFromDB()
29
+ } else {
30
+ throw err
31
+ }
32
+ }
33
+ }
34
+
35
+ export const getInstallFromDB = async (): Promise<Installation> => {
36
+ return doWithDB(
37
+ StaticDatabases.PLATFORM_INFO.name,
38
+ async (platformDb: any) => {
39
+ let install: Installation
40
+ try {
41
+ install = await platformDb.get(
42
+ StaticDatabases.PLATFORM_INFO.docs.install
43
+ )
44
+ } catch (e: any) {
45
+ if (e.status === 404) {
46
+ install = await createInstallDoc(platformDb)
47
+ } else {
48
+ throw e
49
+ }
50
+ }
51
+ return install
52
+ }
53
+ )
54
+ }
55
+
56
+ const updateVersion = async (version: string): Promise<boolean> => {
57
+ try {
58
+ await doWithDB(
59
+ StaticDatabases.PLATFORM_INFO.name,
60
+ async (platformDb: any) => {
61
+ const install = await getInstall()
62
+ install.version = version
63
+ await platformDb.put(install)
64
+ await bustCache(CacheKey.INSTALLATION)
65
+ }
66
+ )
67
+ } catch (e: any) {
68
+ if (e.status === 409) {
69
+ // do nothing - version has already been updated
70
+ // likely in clustered environment
71
+ return false
72
+ }
73
+ throw e
74
+ }
75
+ return true
76
+ }
77
+
78
+ export const checkInstallVersion = async (): Promise<void> => {
79
+ const install = await getInstall()
80
+
81
+ const currentVersion = install.version
82
+ const newVersion = environment.VERSION
83
+
84
+ if (currentVersion !== newVersion) {
85
+ const isUpgrade = semver.gt(newVersion, currentVersion)
86
+ const isDowngrade = semver.lt(newVersion, currentVersion)
87
+
88
+ const success = await updateVersion(newVersion)
89
+
90
+ if (success) {
91
+ await context.doInIdentityContext(
92
+ {
93
+ _id: install.installId,
94
+ type: IdentityType.INSTALLATION,
95
+ },
96
+ async () => {
97
+ if (isUpgrade) {
98
+ await events.installation.upgraded(currentVersion, newVersion)
99
+ } else if (isDowngrade) {
100
+ await events.installation.downgraded(currentVersion, newVersion)
101
+ }
102
+ }
103
+ )
104
+ await events.identification.identifyInstallationGroup(install.installId)
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,26 @@
1
+ const NonErrors = ["AccountError"]
2
+
3
+ function isSuppressed(e?: any) {
4
+ return e && e["suppressAlert"]
5
+ }
6
+
7
+ export function logAlert(message: string, e?: any) {
8
+ if (e && NonErrors.includes(e.name) && isSuppressed(e)) {
9
+ return
10
+ }
11
+ console.error(`bb-alert: ${message}`, e)
12
+ }
13
+
14
+ export function logAlertWithInfo(
15
+ message: string,
16
+ db: string,
17
+ id: string,
18
+ error: any
19
+ ) {
20
+ message = `${message} - db: ${db} - doc: ${id} - error: `
21
+ logAlert(message, error)
22
+ }
23
+
24
+ export function logWarn(message: string, e?: any) {
25
+ console.warn(`bb-warn: ${message}`, e)
26
+ }
@@ -0,0 +1,13 @@
1
+ import { Header } from "../../constants"
2
+ const correlator = require("correlation-id")
3
+
4
+ export const setHeader = (headers: any) => {
5
+ const correlationId = correlator.getId()
6
+ if (correlationId) {
7
+ headers[Header.CORRELATION_ID] = correlationId
8
+ }
9
+ }
10
+
11
+ export function getId() {
12
+ return correlator.getId()
13
+ }
@@ -0,0 +1 @@
1
+ export * from "./correlation"