@budibase/backend-core 2.9.19 → 2.9.21-alpha.0

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 (250) hide show
  1. package/dist/index.js +266 -324
  2. package/dist/index.js.map +4 -4
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +19 -4
  5. package/dist/plugins.js +1 -1
  6. package/dist/plugins.js.map +1 -1
  7. package/dist/plugins.js.meta.json +1 -1
  8. package/dist/src/security/permissions.d.ts +1 -1
  9. package/dist/tests.js +222 -260
  10. package/dist/tests.js.map +4 -4
  11. package/dist/tests.js.meta.json +1 -1
  12. package/package.json +19 -4
  13. package/dist/tsconfig.build.tsbuildinfo +0 -1
  14. package/src/accounts/accounts.ts +0 -82
  15. package/src/accounts/api.ts +0 -59
  16. package/src/accounts/index.ts +0 -1
  17. package/src/auth/auth.ts +0 -208
  18. package/src/auth/index.ts +0 -1
  19. package/src/auth/tests/auth.spec.ts +0 -14
  20. package/src/blacklist/blacklist.ts +0 -54
  21. package/src/blacklist/index.ts +0 -1
  22. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  23. package/src/cache/appMetadata.ts +0 -88
  24. package/src/cache/base/index.ts +0 -92
  25. package/src/cache/generic.ts +0 -30
  26. package/src/cache/index.ts +0 -5
  27. package/src/cache/tests/writethrough.spec.ts +0 -138
  28. package/src/cache/user.ts +0 -69
  29. package/src/cache/writethrough.ts +0 -133
  30. package/src/configs/configs.ts +0 -257
  31. package/src/configs/index.ts +0 -1
  32. package/src/configs/tests/configs.spec.ts +0 -184
  33. package/src/constants/db.ts +0 -63
  34. package/src/constants/index.ts +0 -2
  35. package/src/constants/misc.ts +0 -50
  36. package/src/context/Context.ts +0 -14
  37. package/src/context/identity.ts +0 -58
  38. package/src/context/index.ts +0 -3
  39. package/src/context/mainContext.ts +0 -310
  40. package/src/context/tests/index.spec.ts +0 -147
  41. package/src/context/types.ts +0 -11
  42. package/src/db/Replication.ts +0 -84
  43. package/src/db/constants.ts +0 -10
  44. package/src/db/couch/DatabaseImpl.ts +0 -238
  45. package/src/db/couch/connections.ts +0 -77
  46. package/src/db/couch/index.ts +0 -5
  47. package/src/db/couch/pouchDB.ts +0 -97
  48. package/src/db/couch/pouchDump.ts +0 -0
  49. package/src/db/couch/utils.ts +0 -50
  50. package/src/db/db.ts +0 -39
  51. package/src/db/errors.ts +0 -14
  52. package/src/db/index.ts +0 -12
  53. package/src/db/lucene.ts +0 -732
  54. package/src/db/searchIndexes/index.ts +0 -1
  55. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  56. package/src/db/tests/index.spec.js +0 -25
  57. package/src/db/tests/lucene.spec.ts +0 -298
  58. package/src/db/tests/pouch.spec.js +0 -62
  59. package/src/db/tests/utils.spec.ts +0 -63
  60. package/src/db/utils.ts +0 -207
  61. package/src/db/views.ts +0 -241
  62. package/src/docIds/conversions.ts +0 -59
  63. package/src/docIds/ids.ts +0 -113
  64. package/src/docIds/index.ts +0 -2
  65. package/src/docIds/newid.ts +0 -5
  66. package/src/docIds/params.ts +0 -174
  67. package/src/docUpdates/index.ts +0 -29
  68. package/src/environment.ts +0 -201
  69. package/src/errors/errors.ts +0 -119
  70. package/src/errors/index.ts +0 -1
  71. package/src/events/analytics.ts +0 -6
  72. package/src/events/asyncEvents/index.ts +0 -2
  73. package/src/events/asyncEvents/publisher.ts +0 -12
  74. package/src/events/asyncEvents/queue.ts +0 -22
  75. package/src/events/backfill.ts +0 -183
  76. package/src/events/documentId.ts +0 -56
  77. package/src/events/events.ts +0 -40
  78. package/src/events/identification.ts +0 -310
  79. package/src/events/index.ts +0 -14
  80. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  81. package/src/events/processors/AuditLogsProcessor.ts +0 -93
  82. package/src/events/processors/LoggingProcessor.ts +0 -37
  83. package/src/events/processors/Processors.ts +0 -52
  84. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -43
  85. package/src/events/processors/index.ts +0 -19
  86. package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
  87. package/src/events/processors/posthog/index.ts +0 -2
  88. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  89. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -168
  90. package/src/events/processors/types.ts +0 -1
  91. package/src/events/publishers/account.ts +0 -35
  92. package/src/events/publishers/app.ts +0 -155
  93. package/src/events/publishers/auditLog.ts +0 -26
  94. package/src/events/publishers/auth.ts +0 -73
  95. package/src/events/publishers/automation.ts +0 -110
  96. package/src/events/publishers/backfill.ts +0 -74
  97. package/src/events/publishers/backup.ts +0 -42
  98. package/src/events/publishers/datasource.ts +0 -48
  99. package/src/events/publishers/email.ts +0 -17
  100. package/src/events/publishers/environmentVariable.ts +0 -38
  101. package/src/events/publishers/group.ts +0 -99
  102. package/src/events/publishers/index.ts +0 -24
  103. package/src/events/publishers/installation.ts +0 -38
  104. package/src/events/publishers/layout.ts +0 -26
  105. package/src/events/publishers/license.ts +0 -84
  106. package/src/events/publishers/org.ts +0 -37
  107. package/src/events/publishers/plugin.ts +0 -47
  108. package/src/events/publishers/query.ts +0 -88
  109. package/src/events/publishers/role.ts +0 -62
  110. package/src/events/publishers/rows.ts +0 -29
  111. package/src/events/publishers/screen.ts +0 -36
  112. package/src/events/publishers/serve.ts +0 -43
  113. package/src/events/publishers/table.ts +0 -70
  114. package/src/events/publishers/user.ts +0 -202
  115. package/src/events/publishers/view.ts +0 -107
  116. package/src/featureFlags/index.ts +0 -77
  117. package/src/featureFlags/tests/featureFlags.spec.ts +0 -85
  118. package/src/helpers.ts +0 -9
  119. package/src/index.ts +0 -53
  120. package/src/installation.ts +0 -107
  121. package/src/logging/alerts.ts +0 -26
  122. package/src/logging/correlation/correlation.ts +0 -13
  123. package/src/logging/correlation/index.ts +0 -1
  124. package/src/logging/correlation/middleware.ts +0 -17
  125. package/src/logging/index.ts +0 -4
  126. package/src/logging/pino/logger.ts +0 -232
  127. package/src/logging/pino/middleware.ts +0 -45
  128. package/src/logging/system.ts +0 -81
  129. package/src/logging/tests/system.spec.ts +0 -61
  130. package/src/middleware/adminOnly.ts +0 -9
  131. package/src/middleware/auditLog.ts +0 -6
  132. package/src/middleware/authenticated.ts +0 -193
  133. package/src/middleware/builderOnly.ts +0 -20
  134. package/src/middleware/builderOrAdmin.ts +0 -20
  135. package/src/middleware/csrf.ts +0 -81
  136. package/src/middleware/errorHandling.ts +0 -29
  137. package/src/middleware/index.ts +0 -21
  138. package/src/middleware/internalApi.ts +0 -23
  139. package/src/middleware/joi-validator.ts +0 -45
  140. package/src/middleware/matchers.ts +0 -47
  141. package/src/middleware/passport/datasource/google.ts +0 -95
  142. package/src/middleware/passport/local.ts +0 -54
  143. package/src/middleware/passport/sso/google.ts +0 -77
  144. package/src/middleware/passport/sso/oidc.ts +0 -154
  145. package/src/middleware/passport/sso/sso.ts +0 -165
  146. package/src/middleware/passport/sso/tests/google.spec.ts +0 -67
  147. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -152
  148. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  149. package/src/middleware/passport/utils.ts +0 -38
  150. package/src/middleware/querystringToBody.ts +0 -28
  151. package/src/middleware/tenancy.ts +0 -36
  152. package/src/middleware/tests/builder.spec.ts +0 -180
  153. package/src/middleware/tests/matchers.spec.ts +0 -134
  154. package/src/migrations/definitions.ts +0 -40
  155. package/src/migrations/index.ts +0 -2
  156. package/src/migrations/migrations.ts +0 -191
  157. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  158. package/src/migrations/tests/migrations.spec.ts +0 -64
  159. package/src/objectStore/buckets/app.ts +0 -40
  160. package/src/objectStore/buckets/global.ts +0 -29
  161. package/src/objectStore/buckets/index.ts +0 -3
  162. package/src/objectStore/buckets/plugins.ts +0 -71
  163. package/src/objectStore/buckets/tests/app.spec.ts +0 -171
  164. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  165. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  166. package/src/objectStore/cloudfront.ts +0 -41
  167. package/src/objectStore/index.ts +0 -3
  168. package/src/objectStore/objectStore.ts +0 -440
  169. package/src/objectStore/utils.ts +0 -27
  170. package/src/platform/index.ts +0 -3
  171. package/src/platform/platformDb.ts +0 -6
  172. package/src/platform/tenants.ts +0 -101
  173. package/src/platform/tests/tenants.spec.ts +0 -26
  174. package/src/platform/users.ts +0 -90
  175. package/src/plugin/index.ts +0 -1
  176. package/src/plugin/tests/validation.spec.ts +0 -83
  177. package/src/plugin/utils.ts +0 -156
  178. package/src/queue/constants.ts +0 -6
  179. package/src/queue/inMemoryQueue.ts +0 -141
  180. package/src/queue/index.ts +0 -2
  181. package/src/queue/listeners.ts +0 -195
  182. package/src/queue/queue.ts +0 -54
  183. package/src/redis/index.ts +0 -6
  184. package/src/redis/init.ts +0 -86
  185. package/src/redis/redis.ts +0 -308
  186. package/src/redis/redlockImpl.ts +0 -139
  187. package/src/redis/utils.ts +0 -117
  188. package/src/security/encryption.ts +0 -179
  189. package/src/security/permissions.ts +0 -159
  190. package/src/security/roles.ts +0 -420
  191. package/src/security/sessions.ts +0 -120
  192. package/src/security/tests/encryption.spec.ts +0 -31
  193. package/src/security/tests/permissions.spec.ts +0 -145
  194. package/src/security/tests/sessions.spec.ts +0 -12
  195. package/src/tenancy/db.ts +0 -6
  196. package/src/tenancy/index.ts +0 -2
  197. package/src/tenancy/tenancy.ts +0 -140
  198. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  199. package/src/timers/index.ts +0 -1
  200. package/src/timers/timers.ts +0 -22
  201. package/src/users/db.ts +0 -460
  202. package/src/users/events.ts +0 -176
  203. package/src/users/index.ts +0 -4
  204. package/src/users/lookup.ts +0 -102
  205. package/src/users/users.ts +0 -276
  206. package/src/users/utils.ts +0 -55
  207. package/src/utils/hashing.ts +0 -14
  208. package/src/utils/index.ts +0 -3
  209. package/src/utils/stringUtils.ts +0 -8
  210. package/src/utils/tests/utils.spec.ts +0 -191
  211. package/src/utils/utils.ts +0 -239
  212. package/tests/core/logging.ts +0 -34
  213. package/tests/core/utilities/index.ts +0 -6
  214. package/tests/core/utilities/jestUtils.ts +0 -30
  215. package/tests/core/utilities/mocks/alerts.ts +0 -3
  216. package/tests/core/utilities/mocks/date.ts +0 -2
  217. package/tests/core/utilities/mocks/events.ts +0 -131
  218. package/tests/core/utilities/mocks/fetch.ts +0 -17
  219. package/tests/core/utilities/mocks/index.ts +0 -10
  220. package/tests/core/utilities/mocks/licenses.ts +0 -107
  221. package/tests/core/utilities/mocks/posthog.ts +0 -7
  222. package/tests/core/utilities/structures/Chance.ts +0 -20
  223. package/tests/core/utilities/structures/accounts.ts +0 -115
  224. package/tests/core/utilities/structures/apps.ts +0 -21
  225. package/tests/core/utilities/structures/common.ts +0 -7
  226. package/tests/core/utilities/structures/db.ts +0 -12
  227. package/tests/core/utilities/structures/documents/index.ts +0 -1
  228. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  229. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  230. package/tests/core/utilities/structures/generator.ts +0 -2
  231. package/tests/core/utilities/structures/index.ts +0 -15
  232. package/tests/core/utilities/structures/koa.ts +0 -16
  233. package/tests/core/utilities/structures/licenses.ts +0 -167
  234. package/tests/core/utilities/structures/plugins.ts +0 -19
  235. package/tests/core/utilities/structures/quotas.ts +0 -67
  236. package/tests/core/utilities/structures/scim.ts +0 -80
  237. package/tests/core/utilities/structures/shared.ts +0 -19
  238. package/tests/core/utilities/structures/sso.ts +0 -119
  239. package/tests/core/utilities/structures/tenants.ts +0 -5
  240. package/tests/core/utilities/structures/userGroups.ts +0 -10
  241. package/tests/core/utilities/structures/users.ts +0 -73
  242. package/tests/core/utilities/testContainerUtils.ts +0 -98
  243. package/tests/core/utilities/utils/index.ts +0 -1
  244. package/tests/core/utilities/utils/time.ts +0 -3
  245. package/tests/extra/DBTestConfiguration.ts +0 -36
  246. package/tests/extra/index.ts +0 -2
  247. package/tests/extra/testEnv.ts +0 -95
  248. package/tests/index.ts +0 -1
  249. package/tests/jestEnv.ts +0 -6
  250. package/tests/jestSetup.ts +0 -28
@@ -1,440 +0,0 @@
1
- const sanitize = require("sanitize-s3-objectkey")
2
- import AWS from "aws-sdk"
3
- import stream from "stream"
4
- import fetch from "node-fetch"
5
- import tar from "tar-fs"
6
- import zlib from "zlib"
7
- import { promisify } from "util"
8
- import { join } from "path"
9
- import fs from "fs"
10
- import env from "../environment"
11
- import { budibaseTempDir } from "./utils"
12
- import { v4 } from "uuid"
13
- import { APP_PREFIX, APP_DEV_PREFIX } from "../db"
14
-
15
- const streamPipeline = promisify(stream.pipeline)
16
- // use this as a temporary store of buckets that are being created
17
- const STATE = {
18
- bucketCreationPromises: {},
19
- }
20
-
21
- type ListParams = {
22
- ContinuationToken?: string
23
- }
24
-
25
- type UploadParams = {
26
- bucket: string
27
- filename: string
28
- path: string
29
- type?: string | null
30
- // can be undefined, we will remove it
31
- metadata?: {
32
- [key: string]: string | undefined
33
- }
34
- }
35
-
36
- const CONTENT_TYPE_MAP: any = {
37
- txt: "text/plain",
38
- html: "text/html",
39
- css: "text/css",
40
- js: "application/javascript",
41
- json: "application/json",
42
- gz: "application/gzip",
43
- }
44
-
45
- const STRING_CONTENT_TYPES = [
46
- CONTENT_TYPE_MAP.html,
47
- CONTENT_TYPE_MAP.css,
48
- CONTENT_TYPE_MAP.js,
49
- CONTENT_TYPE_MAP.json,
50
- ]
51
-
52
- // does normal sanitization and then swaps dev apps to apps
53
- export function sanitizeKey(input: string) {
54
- return sanitize(sanitizeBucket(input)).replace(/\\/g, "/")
55
- }
56
-
57
- // simply handles the dev app to app conversion
58
- export function sanitizeBucket(input: string) {
59
- return input.replace(new RegExp(APP_DEV_PREFIX, "g"), APP_PREFIX)
60
- }
61
-
62
- /**
63
- * Gets a connection to the object store using the S3 SDK.
64
- * @param {string} bucket the name of the bucket which blobs will be uploaded/retrieved from.
65
- * @param {object} opts configuration for the object store.
66
- * @return {Object} an S3 object store object, check S3 Nodejs SDK for usage.
67
- * @constructor
68
- */
69
- export const ObjectStore = (
70
- bucket: string,
71
- opts: { presigning: boolean } = { presigning: false }
72
- ) => {
73
- const config: any = {
74
- s3ForcePathStyle: true,
75
- signatureVersion: "v4",
76
- apiVersion: "2006-03-01",
77
- accessKeyId: env.MINIO_ACCESS_KEY,
78
- secretAccessKey: env.MINIO_SECRET_KEY,
79
- region: env.AWS_REGION,
80
- }
81
- if (bucket) {
82
- config.params = {
83
- Bucket: sanitizeBucket(bucket),
84
- }
85
- }
86
-
87
- // custom S3 is in use i.e. minio
88
- if (env.MINIO_URL) {
89
- if (opts.presigning && env.MINIO_ENABLED) {
90
- // IMPORTANT: Signed urls will inspect the host header of the request.
91
- // Normally a signed url will need to be generated with a specified host in mind.
92
- // To support dynamic hosts, e.g. some unknown self-hosted installation url,
93
- // use a predefined host. The host 'minio-service' is also forwarded to minio requests via nginx
94
- config.endpoint = "minio-service"
95
- } else {
96
- config.endpoint = env.MINIO_URL
97
- }
98
- }
99
-
100
- return new AWS.S3(config)
101
- }
102
-
103
- /**
104
- * Given an object store and a bucket name this will make sure the bucket exists,
105
- * if it does not exist then it will create it.
106
- */
107
- export const makeSureBucketExists = async (client: any, bucketName: string) => {
108
- bucketName = sanitizeBucket(bucketName)
109
- try {
110
- await client
111
- .headBucket({
112
- Bucket: bucketName,
113
- })
114
- .promise()
115
- } catch (err: any) {
116
- const promises: any = STATE.bucketCreationPromises
117
- const doesntExist = err.statusCode === 404,
118
- noAccess = err.statusCode === 403
119
- if (promises[bucketName]) {
120
- await promises[bucketName]
121
- } else if (doesntExist || noAccess) {
122
- if (doesntExist) {
123
- // bucket doesn't exist create it
124
- promises[bucketName] = client
125
- .createBucket({
126
- Bucket: bucketName,
127
- })
128
- .promise()
129
- await promises[bucketName]
130
- delete promises[bucketName]
131
- }
132
- } else {
133
- throw new Error("Unable to write to object store bucket.")
134
- }
135
- }
136
- }
137
-
138
- /**
139
- * Uploads the contents of a file given the required parameters, useful when
140
- * temp files in use (for example file uploaded as an attachment).
141
- */
142
- export const upload = async ({
143
- bucket: bucketName,
144
- filename,
145
- path,
146
- type,
147
- metadata,
148
- }: UploadParams) => {
149
- const extension = filename.split(".").pop()
150
- const fileBytes = fs.readFileSync(path)
151
-
152
- const objectStore = ObjectStore(bucketName)
153
- await makeSureBucketExists(objectStore, bucketName)
154
-
155
- let contentType = type
156
- if (!contentType) {
157
- contentType = extension
158
- ? CONTENT_TYPE_MAP[extension.toLowerCase()]
159
- : CONTENT_TYPE_MAP.txt
160
- }
161
- const config: any = {
162
- // windows file paths need to be converted to forward slashes for s3
163
- Key: sanitizeKey(filename),
164
- Body: fileBytes,
165
- ContentType: contentType,
166
- }
167
- if (metadata && typeof metadata === "object") {
168
- // remove any nullish keys from the metadata object, as these may be considered invalid
169
- for (let key of Object.keys(metadata)) {
170
- if (!metadata[key] || typeof metadata[key] !== "string") {
171
- delete metadata[key]
172
- }
173
- }
174
- config.Metadata = metadata
175
- }
176
- return objectStore.upload(config).promise()
177
- }
178
-
179
- /**
180
- * Similar to the upload function but can be used to send a file stream
181
- * through to the object store.
182
- */
183
- export const streamUpload = async (
184
- bucketName: string,
185
- filename: string,
186
- stream: any,
187
- extra = {}
188
- ) => {
189
- const objectStore = ObjectStore(bucketName)
190
- await makeSureBucketExists(objectStore, bucketName)
191
-
192
- // Set content type for certain known extensions
193
- if (filename?.endsWith(".js")) {
194
- extra = {
195
- ...extra,
196
- ContentType: "application/javascript",
197
- }
198
- } else if (filename?.endsWith(".svg")) {
199
- extra = {
200
- ...extra,
201
- ContentType: "image",
202
- }
203
- }
204
-
205
- const params = {
206
- Bucket: sanitizeBucket(bucketName),
207
- Key: sanitizeKey(filename),
208
- Body: stream,
209
- ...extra,
210
- }
211
- return objectStore.upload(params).promise()
212
- }
213
-
214
- /**
215
- * retrieves the contents of a file from the object store, if it is a known content type it
216
- * will be converted, otherwise it will be returned as a buffer stream.
217
- */
218
- export const retrieve = async (bucketName: string, filepath: string) => {
219
- const objectStore = ObjectStore(bucketName)
220
- const params = {
221
- Bucket: sanitizeBucket(bucketName),
222
- Key: sanitizeKey(filepath),
223
- }
224
- const response: any = await objectStore.getObject(params).promise()
225
- // currently these are all strings
226
- if (STRING_CONTENT_TYPES.includes(response.ContentType)) {
227
- return response.Body.toString("utf8")
228
- } else {
229
- return response.Body
230
- }
231
- }
232
-
233
- export const listAllObjects = async (bucketName: string, path: string) => {
234
- const objectStore = ObjectStore(bucketName)
235
- const list = (params: ListParams = {}) => {
236
- return objectStore
237
- .listObjectsV2({
238
- ...params,
239
- Bucket: sanitizeBucket(bucketName),
240
- Prefix: sanitizeKey(path),
241
- })
242
- .promise()
243
- }
244
- let isTruncated = false,
245
- token,
246
- objects: AWS.S3.Types.Object[] = []
247
- do {
248
- let params: ListParams = {}
249
- if (token) {
250
- params.ContinuationToken = token
251
- }
252
- const response = await list(params)
253
- if (response.Contents) {
254
- objects = objects.concat(response.Contents)
255
- }
256
- isTruncated = !!response.IsTruncated
257
- } while (isTruncated)
258
- return objects
259
- }
260
-
261
- /**
262
- * Generate a presigned url with a default TTL of 1 hour
263
- */
264
- export const getPresignedUrl = (
265
- bucketName: string,
266
- key: string,
267
- durationSeconds: number = 3600
268
- ) => {
269
- const objectStore = ObjectStore(bucketName, { presigning: true })
270
- const params = {
271
- Bucket: sanitizeBucket(bucketName),
272
- Key: sanitizeKey(key),
273
- Expires: durationSeconds,
274
- }
275
- const url = objectStore.getSignedUrl("getObject", params)
276
-
277
- if (!env.MINIO_ENABLED) {
278
- // return the full URL to the client
279
- return url
280
- } else {
281
- // return the path only to the client
282
- // use the presigned url route to ensure the static
283
- // hostname will be used in the request
284
- const signedUrl = new URL(url)
285
- const path = signedUrl.pathname
286
- const query = signedUrl.search
287
- return `/files/signed${path}${query}`
288
- }
289
- }
290
-
291
- /**
292
- * Same as retrieval function but puts to a temporary file.
293
- */
294
- export const retrieveToTmp = async (bucketName: string, filepath: string) => {
295
- bucketName = sanitizeBucket(bucketName)
296
- filepath = sanitizeKey(filepath)
297
- const data = await retrieve(bucketName, filepath)
298
- const outputPath = join(budibaseTempDir(), v4())
299
- fs.writeFileSync(outputPath, data)
300
- return outputPath
301
- }
302
-
303
- export const retrieveDirectory = async (bucketName: string, path: string) => {
304
- let writePath = join(budibaseTempDir(), v4())
305
- fs.mkdirSync(writePath)
306
- const objects = await listAllObjects(bucketName, path)
307
- let fullObjects = await Promise.all(
308
- objects.map(obj => retrieve(bucketName, obj.Key!))
309
- )
310
- let count = 0
311
- for (let obj of objects) {
312
- const filename = obj.Key!
313
- const data = fullObjects[count++]
314
- const possiblePath = filename.split("/")
315
- if (possiblePath.length > 1) {
316
- const dirs = possiblePath.slice(0, possiblePath.length - 1)
317
- fs.mkdirSync(join(writePath, ...dirs), { recursive: true })
318
- }
319
- fs.writeFileSync(join(writePath, ...possiblePath), data)
320
- }
321
- return writePath
322
- }
323
-
324
- /**
325
- * Delete a single file.
326
- */
327
- export const deleteFile = async (bucketName: string, filepath: string) => {
328
- const objectStore = ObjectStore(bucketName)
329
- await makeSureBucketExists(objectStore, bucketName)
330
- const params = {
331
- Bucket: bucketName,
332
- Key: sanitizeKey(filepath),
333
- }
334
- return objectStore.deleteObject(params).promise()
335
- }
336
-
337
- export const deleteFiles = async (bucketName: string, filepaths: string[]) => {
338
- const objectStore = ObjectStore(bucketName)
339
- await makeSureBucketExists(objectStore, bucketName)
340
- const params = {
341
- Bucket: bucketName,
342
- Delete: {
343
- Objects: filepaths.map((path: any) => ({ Key: sanitizeKey(path) })),
344
- },
345
- }
346
- return objectStore.deleteObjects(params).promise()
347
- }
348
-
349
- /**
350
- * Delete a path, including everything within.
351
- */
352
- export const deleteFolder = async (
353
- bucketName: string,
354
- folder: string
355
- ): Promise<any> => {
356
- bucketName = sanitizeBucket(bucketName)
357
- folder = sanitizeKey(folder)
358
- const client = ObjectStore(bucketName)
359
- const listParams = {
360
- Bucket: bucketName,
361
- Prefix: folder,
362
- }
363
-
364
- const existingObjectsResponse = await client.listObjects(listParams).promise()
365
- if (existingObjectsResponse.Contents?.length === 0) {
366
- return
367
- }
368
- const deleteParams: any = {
369
- Bucket: bucketName,
370
- Delete: {
371
- Objects: [],
372
- },
373
- }
374
-
375
- existingObjectsResponse.Contents?.forEach((content: any) => {
376
- deleteParams.Delete.Objects.push({ Key: content.Key })
377
- })
378
-
379
- const deleteResponse = await client.deleteObjects(deleteParams).promise()
380
- // can only empty 1000 items at once
381
- if (deleteResponse.Deleted?.length === 1000) {
382
- return deleteFolder(bucketName, folder)
383
- }
384
- }
385
-
386
- export const uploadDirectory = async (
387
- bucketName: string,
388
- localPath: string,
389
- bucketPath: string
390
- ) => {
391
- bucketName = sanitizeBucket(bucketName)
392
- let uploads = []
393
- const files = fs.readdirSync(localPath, { withFileTypes: true })
394
- for (let file of files) {
395
- const path = sanitizeKey(join(bucketPath, file.name))
396
- const local = join(localPath, file.name)
397
- if (file.isDirectory()) {
398
- uploads.push(uploadDirectory(bucketName, local, path))
399
- } else {
400
- uploads.push(streamUpload(bucketName, path, fs.createReadStream(local)))
401
- }
402
- }
403
- await Promise.all(uploads)
404
- return files
405
- }
406
-
407
- export const downloadTarballDirect = async (
408
- url: string,
409
- path: string,
410
- headers = {}
411
- ) => {
412
- path = sanitizeKey(path)
413
- const response = await fetch(url, { headers })
414
- if (!response.ok) {
415
- throw new Error(`unexpected response ${response.statusText}`)
416
- }
417
-
418
- await streamPipeline(response.body, zlib.createUnzip(), tar.extract(path))
419
- }
420
-
421
- export const downloadTarball = async (
422
- url: string,
423
- bucketName: string,
424
- path: string
425
- ) => {
426
- bucketName = sanitizeBucket(bucketName)
427
- path = sanitizeKey(path)
428
- const response = await fetch(url)
429
- if (!response.ok) {
430
- throw new Error(`unexpected response ${response.statusText}`)
431
- }
432
-
433
- const tmpPath = join(budibaseTempDir(), path)
434
- await streamPipeline(response.body, zlib.createUnzip(), tar.extract(tmpPath))
435
- if (!env.isTest() && env.SELF_HOSTED) {
436
- await uploadDirectory(bucketName, tmpPath, path)
437
- }
438
- // return the temporary path incase there is a use for it
439
- return tmpPath
440
- }
@@ -1,27 +0,0 @@
1
- import { join } from "path"
2
- import { tmpdir } from "os"
3
- import fs from "fs"
4
- import env from "../environment"
5
-
6
- /****************************************************
7
- * NOTE: When adding a new bucket - name *
8
- * sure that S3 usages (like budibase-infra) *
9
- * have been updated to have a unique bucket name. *
10
- ****************************************************/
11
- // can't be an enum - only numbers can be used for computed types
12
- export const ObjectStoreBuckets = {
13
- BACKUPS: env.BACKUPS_BUCKET_NAME,
14
- APPS: env.APPS_BUCKET_NAME,
15
- TEMPLATES: env.TEMPLATES_BUCKET_NAME,
16
- GLOBAL: env.GLOBAL_BUCKET_NAME,
17
- PLUGINS: env.PLUGIN_BUCKET_NAME,
18
- }
19
-
20
- const bbTmp = join(tmpdir(), ".budibase")
21
- if (!fs.existsSync(bbTmp)) {
22
- fs.mkdirSync(bbTmp)
23
- }
24
-
25
- export function budibaseTempDir() {
26
- return bbTmp
27
- }
@@ -1,3 +0,0 @@
1
- export * as users from "./users"
2
- export * as tenants from "./tenants"
3
- export * from "./platformDb"
@@ -1,6 +0,0 @@
1
- import { StaticDatabases } from "../constants"
2
- import { getDB } from "../db/db"
3
-
4
- export function getPlatformDB() {
5
- return getDB(StaticDatabases.PLATFORM_INFO.name)
6
- }
@@ -1,101 +0,0 @@
1
- import { StaticDatabases } from "../constants"
2
- import { getPlatformDB } from "./platformDb"
3
- import { LockName, LockOptions, LockType, Tenants } from "@budibase/types"
4
- import * as locks from "../redis/redlockImpl"
5
-
6
- const TENANT_DOC = StaticDatabases.PLATFORM_INFO.docs.tenants
7
-
8
- export const tenacyLockOptions: LockOptions = {
9
- type: LockType.DEFAULT,
10
- name: LockName.UPDATE_TENANTS_DOC,
11
- ttl: 10 * 1000, // auto expire after 10 seconds
12
- systemLock: true,
13
- }
14
-
15
- // READ
16
-
17
- export async function getTenantIds(): Promise<string[]> {
18
- const tenants = await getTenants()
19
- return tenants.tenantIds
20
- }
21
-
22
- async function getTenants(): Promise<Tenants> {
23
- const db = getPlatformDB()
24
- let tenants: Tenants
25
-
26
- try {
27
- tenants = await db.get(TENANT_DOC)
28
- } catch (e: any) {
29
- // doesn't exist yet - create
30
- if (e.status === 404) {
31
- tenants = await createTenantsDoc()
32
- } else {
33
- throw e
34
- }
35
- }
36
-
37
- return tenants
38
- }
39
-
40
- export async function exists(tenantId: string) {
41
- const tenants = await getTenants()
42
- return tenants.tenantIds.indexOf(tenantId) !== -1
43
- }
44
-
45
- // CREATE / UPDATE
46
-
47
- function newTenantsDoc(): Tenants {
48
- return {
49
- _id: TENANT_DOC,
50
- tenantIds: [],
51
- }
52
- }
53
-
54
- async function createTenantsDoc(): Promise<Tenants> {
55
- const db = getPlatformDB()
56
- let tenants = newTenantsDoc()
57
-
58
- try {
59
- const response = await db.put(tenants)
60
- tenants._rev = response.rev
61
- } catch (e: any) {
62
- // don't throw 409 is doc has already been created
63
- if (e.status === 409) {
64
- return db.get(TENANT_DOC)
65
- }
66
- throw e
67
- }
68
-
69
- return tenants
70
- }
71
-
72
- export async function addTenant(tenantId: string) {
73
- const db = getPlatformDB()
74
-
75
- // use a lock as tenant creation is conflict prone
76
- await locks.doWithLock(tenacyLockOptions, async () => {
77
- const tenants = await getTenants()
78
-
79
- // write the new tenant if it doesn't already exist
80
- if (tenants.tenantIds.indexOf(tenantId) === -1) {
81
- tenants.tenantIds.push(tenantId)
82
- await db.put(tenants)
83
- }
84
- })
85
- }
86
-
87
- // DELETE
88
-
89
- export async function removeTenant(tenantId: string) {
90
- try {
91
- await locks.doWithLock(tenacyLockOptions, async () => {
92
- const db = getPlatformDB()
93
- const tenants = await getTenants()
94
- tenants.tenantIds = tenants.tenantIds.filter(id => id !== tenantId)
95
- await db.put(tenants)
96
- })
97
- } catch (err) {
98
- console.error(`Error removing tenant ${tenantId} from info db`, err)
99
- throw err
100
- }
101
- }
@@ -1,26 +0,0 @@
1
- import { structures } from "../../../tests"
2
- import { DBTestConfiguration } from "../../../tests/extra"
3
- import * as tenants from "../tenants"
4
-
5
- describe("tenants", () => {
6
- const config = new DBTestConfiguration()
7
-
8
- describe("addTenant", () => {
9
- it("concurrently adds multiple tenants safely", async () => {
10
- const tenant1 = structures.tenant.id()
11
- const tenant2 = structures.tenant.id()
12
- const tenant3 = structures.tenant.id()
13
-
14
- await Promise.all([
15
- tenants.addTenant(tenant1),
16
- tenants.addTenant(tenant2),
17
- tenants.addTenant(tenant3),
18
- ])
19
-
20
- const tenantIds = await tenants.getTenantIds()
21
- expect(tenantIds.includes(tenant1)).toBe(true)
22
- expect(tenantIds.includes(tenant2)).toBe(true)
23
- expect(tenantIds.includes(tenant3)).toBe(true)
24
- })
25
- })
26
- })
@@ -1,90 +0,0 @@
1
- import { getPlatformDB } from "./platformDb"
2
- import { DEFAULT_TENANT_ID } from "../constants"
3
- import env from "../environment"
4
- import {
5
- PlatformUser,
6
- PlatformUserByEmail,
7
- PlatformUserById,
8
- User,
9
- } from "@budibase/types"
10
-
11
- // READ
12
-
13
- export async function lookupTenantId(userId: string) {
14
- if (!env.MULTI_TENANCY) {
15
- return DEFAULT_TENANT_ID
16
- }
17
-
18
- const user = await getUserDoc(userId)
19
- return user.tenantId
20
- }
21
-
22
- async function getUserDoc(emailOrId: string): Promise<PlatformUser> {
23
- const db = getPlatformDB()
24
- return db.get(emailOrId)
25
- }
26
-
27
- // CREATE
28
-
29
- function newUserIdDoc(id: string, tenantId: string): PlatformUserById {
30
- return {
31
- _id: id,
32
- tenantId,
33
- }
34
- }
35
-
36
- function newUserEmailDoc(
37
- userId: string,
38
- email: string,
39
- tenantId: string
40
- ): PlatformUserByEmail {
41
- return {
42
- _id: email,
43
- userId,
44
- tenantId,
45
- }
46
- }
47
-
48
- /**
49
- * Add a new user id or email doc if it doesn't exist.
50
- */
51
- async function addUserDoc(emailOrId: string, newDocFn: () => PlatformUser) {
52
- const db = getPlatformDB()
53
- let user: PlatformUser
54
-
55
- try {
56
- await db.get(emailOrId)
57
- } catch (e: any) {
58
- if (e.status === 404) {
59
- user = newDocFn()
60
- await db.put(user)
61
- } else {
62
- throw e
63
- }
64
- }
65
- }
66
-
67
- export async function addUser(tenantId: string, userId: string, email: string) {
68
- await Promise.all([
69
- addUserDoc(userId, () => newUserIdDoc(userId, tenantId)),
70
- addUserDoc(email, () => newUserEmailDoc(userId, email, tenantId)),
71
- ])
72
- }
73
-
74
- // DELETE
75
-
76
- export async function removeUser(user: User) {
77
- const db = getPlatformDB()
78
- const keys = [user._id!, user.email]
79
- const userDocs = await db.allDocs({
80
- keys,
81
- include_docs: true,
82
- })
83
- const toDelete = userDocs.rows.map((row: any) => {
84
- return {
85
- ...row.doc,
86
- _deleted: true,
87
- }
88
- })
89
- await db.bulkDocs(toDelete)
90
- }
@@ -1 +0,0 @@
1
- export * from "./utils"