@budibase/backend-core 3.2.4 → 3.2.6

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 (272) hide show
  1. package/dist/index.js.map +1 -1
  2. package/dist/index.js.meta.json +1 -1
  3. package/dist/package.json +11 -4
  4. package/dist/plugins.js.meta.json +1 -1
  5. package/package.json +11 -4
  6. package/src/accounts/accounts.ts +0 -82
  7. package/src/accounts/api.ts +0 -59
  8. package/src/accounts/index.ts +0 -1
  9. package/src/auth/auth.ts +0 -210
  10. package/src/auth/index.ts +0 -1
  11. package/src/auth/tests/auth.spec.ts +0 -14
  12. package/src/blacklist/blacklist.ts +0 -54
  13. package/src/blacklist/index.ts +0 -1
  14. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  15. package/src/cache/appMetadata.ts +0 -88
  16. package/src/cache/base/index.ts +0 -150
  17. package/src/cache/docWritethrough.ts +0 -105
  18. package/src/cache/generic.ts +0 -33
  19. package/src/cache/index.ts +0 -8
  20. package/src/cache/invite.ts +0 -86
  21. package/src/cache/passwordReset.ts +0 -49
  22. package/src/cache/tests/docWritethrough.spec.ts +0 -296
  23. package/src/cache/tests/user.spec.ts +0 -145
  24. package/src/cache/tests/writethrough.spec.ts +0 -139
  25. package/src/cache/user.ts +0 -154
  26. package/src/cache/writethrough.ts +0 -133
  27. package/src/configs/configs.ts +0 -263
  28. package/src/configs/index.ts +0 -1
  29. package/src/configs/tests/configs.spec.ts +0 -184
  30. package/src/constants/db.ts +0 -75
  31. package/src/constants/index.ts +0 -2
  32. package/src/constants/misc.ts +0 -36
  33. package/src/context/Context.ts +0 -14
  34. package/src/context/identity.ts +0 -58
  35. package/src/context/index.ts +0 -3
  36. package/src/context/mainContext.ts +0 -422
  37. package/src/context/tests/index.spec.ts +0 -255
  38. package/src/context/types.ts +0 -26
  39. package/src/db/Replication.ts +0 -94
  40. package/src/db/couch/DatabaseImpl.ts +0 -511
  41. package/src/db/couch/connections.ts +0 -89
  42. package/src/db/couch/index.ts +0 -4
  43. package/src/db/couch/pouchDB.ts +0 -97
  44. package/src/db/couch/pouchDump.ts +0 -0
  45. package/src/db/couch/tests/DatabaseImpl.spec.ts +0 -118
  46. package/src/db/couch/utils.ts +0 -55
  47. package/src/db/db.ts +0 -34
  48. package/src/db/errors.ts +0 -14
  49. package/src/db/index.ts +0 -12
  50. package/src/db/instrumentation.ts +0 -199
  51. package/src/db/lucene.ts +0 -721
  52. package/src/db/searchIndexes/index.ts +0 -1
  53. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  54. package/src/db/tests/DatabaseImpl.spec.ts +0 -55
  55. package/src/db/tests/connections.spec.ts +0 -22
  56. package/src/db/tests/index.spec.ts +0 -32
  57. package/src/db/tests/lucene.spec.ts +0 -400
  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 -208
  61. package/src/db/views.ts +0 -245
  62. package/src/docIds/conversions.ts +0 -60
  63. package/src/docIds/ids.ts +0 -126
  64. package/src/docIds/index.ts +0 -2
  65. package/src/docIds/newid.ts +0 -5
  66. package/src/docIds/params.ts +0 -189
  67. package/src/docUpdates/index.ts +0 -24
  68. package/src/environment.ts +0 -293
  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 -47
  78. package/src/events/identification.ts +0 -311
  79. package/src/events/index.ts +0 -15
  80. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  81. package/src/events/processors/AuditLogsProcessor.ts +0 -92
  82. package/src/events/processors/LoggingProcessor.ts +0 -36
  83. package/src/events/processors/Processors.ts +0 -52
  84. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -38
  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 -3
  88. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  89. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -164
  90. package/src/events/processors/types.ts +0 -1
  91. package/src/events/publishers/account.ts +0 -41
  92. package/src/events/publishers/ai.ts +0 -21
  93. package/src/events/publishers/app.ts +0 -168
  94. package/src/events/publishers/auditLog.ts +0 -26
  95. package/src/events/publishers/auth.ts +0 -73
  96. package/src/events/publishers/automation.ts +0 -110
  97. package/src/events/publishers/backfill.ts +0 -74
  98. package/src/events/publishers/backup.ts +0 -42
  99. package/src/events/publishers/datasource.ts +0 -48
  100. package/src/events/publishers/email.ts +0 -17
  101. package/src/events/publishers/environmentVariable.ts +0 -38
  102. package/src/events/publishers/group.ts +0 -99
  103. package/src/events/publishers/index.ts +0 -25
  104. package/src/events/publishers/installation.ts +0 -38
  105. package/src/events/publishers/layout.ts +0 -26
  106. package/src/events/publishers/license.ts +0 -84
  107. package/src/events/publishers/org.ts +0 -37
  108. package/src/events/publishers/plugin.ts +0 -47
  109. package/src/events/publishers/query.ts +0 -89
  110. package/src/events/publishers/role.ts +0 -62
  111. package/src/events/publishers/rows.ts +0 -29
  112. package/src/events/publishers/screen.ts +0 -36
  113. package/src/events/publishers/serve.ts +0 -43
  114. package/src/events/publishers/table.ts +0 -70
  115. package/src/events/publishers/user.ts +0 -202
  116. package/src/events/publishers/view.ts +0 -107
  117. package/src/features/features.ts +0 -277
  118. package/src/features/index.ts +0 -2
  119. package/src/features/tests/features.spec.ts +0 -267
  120. package/src/features/tests/utils.ts +0 -64
  121. package/src/helpers.ts +0 -9
  122. package/src/index.ts +0 -59
  123. package/src/installation.ts +0 -115
  124. package/src/logging/alerts.ts +0 -26
  125. package/src/logging/correlation/correlation.ts +0 -15
  126. package/src/logging/correlation/index.ts +0 -1
  127. package/src/logging/correlation/middleware.ts +0 -18
  128. package/src/logging/index.ts +0 -4
  129. package/src/logging/pino/logger.ts +0 -239
  130. package/src/logging/pino/middleware.ts +0 -48
  131. package/src/logging/system.ts +0 -81
  132. package/src/logging/tests/system.spec.ts +0 -61
  133. package/src/middleware/adminOnly.ts +0 -9
  134. package/src/middleware/auditLog.ts +0 -6
  135. package/src/middleware/authenticated.ts +0 -247
  136. package/src/middleware/builderOnly.ts +0 -21
  137. package/src/middleware/builderOrAdmin.ts +0 -21
  138. package/src/middleware/contentSecurityPolicy.ts +0 -113
  139. package/src/middleware/csrf.ts +0 -81
  140. package/src/middleware/errorHandling.ts +0 -43
  141. package/src/middleware/index.ts +0 -24
  142. package/src/middleware/internalApi.ts +0 -23
  143. package/src/middleware/ip.ts +0 -12
  144. package/src/middleware/joi-validator.ts +0 -58
  145. package/src/middleware/matchers.ts +0 -39
  146. package/src/middleware/passport/datasource/google.ts +0 -102
  147. package/src/middleware/passport/local.ts +0 -54
  148. package/src/middleware/passport/sso/google.ts +0 -77
  149. package/src/middleware/passport/sso/oidc.ts +0 -152
  150. package/src/middleware/passport/sso/sso.ts +0 -138
  151. package/src/middleware/passport/sso/tests/google.spec.ts +0 -68
  152. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -144
  153. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  154. package/src/middleware/passport/utils.ts +0 -38
  155. package/src/middleware/querystringToBody.ts +0 -28
  156. package/src/middleware/tenancy.ts +0 -36
  157. package/src/middleware/tests/builder.spec.ts +0 -181
  158. package/src/middleware/tests/contentSecurityPolicy.spec.ts +0 -75
  159. package/src/middleware/tests/matchers.spec.ts +0 -100
  160. package/src/migrations/definitions.ts +0 -40
  161. package/src/migrations/index.ts +0 -2
  162. package/src/migrations/migrations.ts +0 -186
  163. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  164. package/src/migrations/tests/migrations.spec.ts +0 -64
  165. package/src/objectStore/buckets/app.ts +0 -53
  166. package/src/objectStore/buckets/global.ts +0 -29
  167. package/src/objectStore/buckets/index.ts +0 -3
  168. package/src/objectStore/buckets/plugins.ts +0 -71
  169. package/src/objectStore/buckets/tests/app.spec.ts +0 -161
  170. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  171. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  172. package/src/objectStore/cloudfront.ts +0 -41
  173. package/src/objectStore/index.ts +0 -3
  174. package/src/objectStore/objectStore.ts +0 -585
  175. package/src/objectStore/utils.ts +0 -113
  176. package/src/platform/index.ts +0 -3
  177. package/src/platform/platformDb.ts +0 -6
  178. package/src/platform/tenants.ts +0 -101
  179. package/src/platform/tests/tenants.spec.ts +0 -26
  180. package/src/platform/users.ts +0 -129
  181. package/src/plugin/index.ts +0 -1
  182. package/src/plugin/tests/validation.spec.ts +0 -209
  183. package/src/plugin/utils.ts +0 -175
  184. package/src/queue/constants.ts +0 -8
  185. package/src/queue/inMemoryQueue.ts +0 -189
  186. package/src/queue/index.ts +0 -2
  187. package/src/queue/listeners.ts +0 -199
  188. package/src/queue/queue.ts +0 -84
  189. package/src/redis/index.ts +0 -6
  190. package/src/redis/init.ts +0 -118
  191. package/src/redis/redis.ts +0 -358
  192. package/src/redis/redlockImpl.ts +0 -155
  193. package/src/redis/tests/redis.spec.ts +0 -207
  194. package/src/redis/tests/redlockImpl.spec.ts +0 -105
  195. package/src/redis/utils.ts +0 -128
  196. package/src/security/auth.ts +0 -24
  197. package/src/security/encryption.ts +0 -185
  198. package/src/security/index.ts +0 -1
  199. package/src/security/permissions.ts +0 -166
  200. package/src/security/roles.ts +0 -655
  201. package/src/security/secrets.ts +0 -20
  202. package/src/security/sessions.ts +0 -123
  203. package/src/security/tests/auth.spec.ts +0 -45
  204. package/src/security/tests/encryption.spec.ts +0 -31
  205. package/src/security/tests/permissions.spec.ts +0 -146
  206. package/src/security/tests/secrets.spec.ts +0 -35
  207. package/src/security/tests/sessions.spec.ts +0 -12
  208. package/src/sql/designDoc.ts +0 -17
  209. package/src/sql/index.ts +0 -5
  210. package/src/sql/sql.ts +0 -1854
  211. package/src/sql/sqlTable.ts +0 -319
  212. package/src/sql/utils.ts +0 -193
  213. package/src/tenancy/db.ts +0 -6
  214. package/src/tenancy/index.ts +0 -2
  215. package/src/tenancy/tenancy.ts +0 -148
  216. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  217. package/src/timers/index.ts +0 -1
  218. package/src/timers/timers.ts +0 -22
  219. package/src/users/db.ts +0 -582
  220. package/src/users/events.ts +0 -176
  221. package/src/users/index.ts +0 -4
  222. package/src/users/lookup.ts +0 -99
  223. package/src/users/test/db.spec.ts +0 -188
  224. package/src/users/test/utils.spec.ts +0 -67
  225. package/src/users/users.ts +0 -353
  226. package/src/users/utils.ts +0 -81
  227. package/src/utils/Duration.ts +0 -56
  228. package/src/utils/hashing.ts +0 -15
  229. package/src/utils/index.ts +0 -4
  230. package/src/utils/stringUtils.ts +0 -8
  231. package/src/utils/tests/Duration.spec.ts +0 -19
  232. package/src/utils/tests/utils.spec.ts +0 -204
  233. package/src/utils/utils.ts +0 -249
  234. package/tests/core/logging.ts +0 -34
  235. package/tests/core/users/users.spec.js +0 -53
  236. package/tests/core/utilities/index.ts +0 -7
  237. package/tests/core/utilities/jestUtils.ts +0 -33
  238. package/tests/core/utilities/mocks/alerts.ts +0 -4
  239. package/tests/core/utilities/mocks/date.ts +0 -3
  240. package/tests/core/utilities/mocks/events.ts +0 -132
  241. package/tests/core/utilities/mocks/index.ts +0 -9
  242. package/tests/core/utilities/mocks/licenses.ts +0 -119
  243. package/tests/core/utilities/queue.ts +0 -9
  244. package/tests/core/utilities/structures/Chance.ts +0 -20
  245. package/tests/core/utilities/structures/accounts.ts +0 -80
  246. package/tests/core/utilities/structures/apps.ts +0 -21
  247. package/tests/core/utilities/structures/common.ts +0 -7
  248. package/tests/core/utilities/structures/db.ts +0 -12
  249. package/tests/core/utilities/structures/documents/index.ts +0 -1
  250. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  251. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  252. package/tests/core/utilities/structures/generator.ts +0 -3
  253. package/tests/core/utilities/structures/index.ts +0 -15
  254. package/tests/core/utilities/structures/koa.ts +0 -16
  255. package/tests/core/utilities/structures/licenses.ts +0 -190
  256. package/tests/core/utilities/structures/plugins.ts +0 -19
  257. package/tests/core/utilities/structures/quotas.ts +0 -72
  258. package/tests/core/utilities/structures/scim.ts +0 -80
  259. package/tests/core/utilities/structures/sso.ts +0 -118
  260. package/tests/core/utilities/structures/tenants.ts +0 -5
  261. package/tests/core/utilities/structures/userGroups.ts +0 -10
  262. package/tests/core/utilities/structures/users.ts +0 -89
  263. package/tests/core/utilities/testContainerUtils.ts +0 -165
  264. package/tests/core/utilities/utils/index.ts +0 -2
  265. package/tests/core/utilities/utils/queue.ts +0 -27
  266. package/tests/core/utilities/utils/time.ts +0 -3
  267. package/tests/extra/DBTestConfiguration.ts +0 -36
  268. package/tests/extra/index.ts +0 -2
  269. package/tests/extra/testEnv.ts +0 -95
  270. package/tests/index.ts +0 -2
  271. package/tests/jestEnv.ts +0 -10
  272. package/tests/jestSetup.ts +0 -36
@@ -1,585 +0,0 @@
1
- const sanitize = require("sanitize-s3-objectkey")
2
-
3
- import AWS from "aws-sdk"
4
- import stream, { Readable } from "stream"
5
- import fetch from "node-fetch"
6
- import tar from "tar-fs"
7
- import zlib from "zlib"
8
- import { promisify } from "util"
9
- import { join } from "path"
10
- import fs, { PathLike, ReadStream } from "fs"
11
- import env from "../environment"
12
- import { bucketTTLConfig, budibaseTempDir } from "./utils"
13
- import { v4 } from "uuid"
14
- import { APP_PREFIX, APP_DEV_PREFIX } from "../db"
15
- import fsp from "fs/promises"
16
- import { HeadObjectOutput } from "aws-sdk/clients/s3"
17
- import { ReadableStream } from "stream/web"
18
-
19
- const streamPipeline = promisify(stream.pipeline)
20
- // use this as a temporary store of buckets that are being created
21
- const STATE = {
22
- bucketCreationPromises: {},
23
- }
24
- export const SIGNED_FILE_PREFIX = "/files/signed"
25
-
26
- type ListParams = {
27
- ContinuationToken?: string
28
- }
29
-
30
- type BaseUploadParams = {
31
- bucket: string
32
- filename: string
33
- type?: string | null
34
- metadata?: { [key: string]: string | undefined }
35
- body?: ReadableStream | Buffer
36
- ttl?: number
37
- addTTL?: boolean
38
- extra?: any
39
- }
40
-
41
- type UploadParams = BaseUploadParams & {
42
- path?: string | PathLike
43
- }
44
-
45
- export type StreamTypes = ReadStream | NodeJS.ReadableStream
46
-
47
- export type StreamUploadParams = BaseUploadParams & {
48
- stream?: StreamTypes
49
- }
50
-
51
- const CONTENT_TYPE_MAP: any = {
52
- txt: "text/plain",
53
- html: "text/html",
54
- css: "text/css",
55
- js: "application/javascript",
56
- json: "application/json",
57
- gz: "application/gzip",
58
- svg: "image/svg+xml",
59
- form: "multipart/form-data",
60
- }
61
-
62
- const STRING_CONTENT_TYPES = [
63
- CONTENT_TYPE_MAP.html,
64
- CONTENT_TYPE_MAP.css,
65
- CONTENT_TYPE_MAP.js,
66
- CONTENT_TYPE_MAP.json,
67
- ]
68
-
69
- // does normal sanitization and then swaps dev apps to apps
70
- export function sanitizeKey(input: string) {
71
- return sanitize(sanitizeBucket(input)).replace(/\\/g, "/")
72
- }
73
-
74
- // simply handles the dev app to app conversion
75
- export function sanitizeBucket(input: string) {
76
- return input.replace(new RegExp(APP_DEV_PREFIX, "g"), APP_PREFIX)
77
- }
78
-
79
- /**
80
- * Gets a connection to the object store using the S3 SDK.
81
- * @param bucket the name of the bucket which blobs will be uploaded/retrieved from.
82
- * @param opts configuration for the object store.
83
- * @return an S3 object store object, check S3 Nodejs SDK for usage.
84
- * @constructor
85
- */
86
- export function ObjectStore(
87
- bucket: string,
88
- opts: { presigning: boolean } = { presigning: false }
89
- ) {
90
- const config: AWS.S3.ClientConfiguration = {
91
- s3ForcePathStyle: true,
92
- signatureVersion: "v4",
93
- apiVersion: "2006-03-01",
94
- accessKeyId: env.MINIO_ACCESS_KEY,
95
- secretAccessKey: env.MINIO_SECRET_KEY,
96
- region: env.AWS_REGION,
97
- }
98
- if (bucket) {
99
- config.params = {
100
- Bucket: sanitizeBucket(bucket),
101
- }
102
- }
103
-
104
- // for AWS Credentials using temporary session token
105
- if (!env.MINIO_ENABLED && env.AWS_SESSION_TOKEN) {
106
- config.sessionToken = env.AWS_SESSION_TOKEN
107
- }
108
-
109
- // custom S3 is in use i.e. minio
110
- if (env.MINIO_URL) {
111
- if (opts.presigning && env.MINIO_ENABLED) {
112
- // IMPORTANT: Signed urls will inspect the host header of the request.
113
- // Normally a signed url will need to be generated with a specified host in mind.
114
- // To support dynamic hosts, e.g. some unknown self-hosted installation url,
115
- // use a predefined host. The host 'minio-service' is also forwarded to minio requests via nginx
116
- config.endpoint = "minio-service"
117
- } else {
118
- config.endpoint = env.MINIO_URL
119
- }
120
- }
121
-
122
- return new AWS.S3(config)
123
- }
124
-
125
- /**
126
- * Given an object store and a bucket name this will make sure the bucket exists,
127
- * if it does not exist then it will create it.
128
- */
129
- export async function createBucketIfNotExists(
130
- client: any,
131
- bucketName: string
132
- ): Promise<{ created: boolean; exists: boolean }> {
133
- bucketName = sanitizeBucket(bucketName)
134
- try {
135
- await client
136
- .headBucket({
137
- Bucket: bucketName,
138
- })
139
- .promise()
140
- return { created: false, exists: true }
141
- } catch (err: any) {
142
- const promises: any = STATE.bucketCreationPromises
143
- const doesntExist = err.statusCode === 404,
144
- noAccess = err.statusCode === 403
145
- if (promises[bucketName]) {
146
- await promises[bucketName]
147
- return { created: false, exists: true }
148
- } else if (doesntExist || noAccess) {
149
- if (doesntExist) {
150
- promises[bucketName] = client
151
- .createBucket({
152
- Bucket: bucketName,
153
- })
154
- .promise()
155
- await promises[bucketName]
156
- delete promises[bucketName]
157
- return { created: true, exists: false }
158
- } else {
159
- throw new Error("Access denied to object store bucket." + err)
160
- }
161
- } else {
162
- throw new Error("Unable to write to object store bucket.")
163
- }
164
- }
165
- }
166
- /**
167
- * Uploads the contents of a file given the required parameters, useful when
168
- * temp files in use (for example file uploaded as an attachment).
169
- */
170
- export async function upload({
171
- bucket: bucketName,
172
- filename,
173
- path,
174
- type,
175
- metadata,
176
- body,
177
- ttl,
178
- }: UploadParams) {
179
- const extension = filename.split(".").pop()
180
-
181
- const fileBytes = path ? (await fsp.open(path)).createReadStream() : body
182
-
183
- const objectStore = ObjectStore(bucketName)
184
- const bucketCreated = await createBucketIfNotExists(objectStore, bucketName)
185
-
186
- if (ttl && bucketCreated.created) {
187
- let ttlConfig = bucketTTLConfig(bucketName, ttl)
188
- await objectStore.putBucketLifecycleConfiguration(ttlConfig).promise()
189
- }
190
-
191
- let contentType = type
192
- if (!contentType) {
193
- contentType = extension
194
- ? CONTENT_TYPE_MAP[extension.toLowerCase()]
195
- : CONTENT_TYPE_MAP.txt
196
- }
197
- const config: any = {
198
- // windows file paths need to be converted to forward slashes for s3
199
- Key: sanitizeKey(filename),
200
- Body: fileBytes,
201
- ContentType: contentType,
202
- }
203
- if (metadata && typeof metadata === "object") {
204
- // remove any nullish keys from the metadata object, as these may be considered invalid
205
- for (let key of Object.keys(metadata)) {
206
- if (!metadata[key] || typeof metadata[key] !== "string") {
207
- delete metadata[key]
208
- }
209
- }
210
- config.Metadata = metadata
211
- }
212
-
213
- return objectStore.upload(config).promise()
214
- }
215
-
216
- /**
217
- * Similar to the upload function but can be used to send a file stream
218
- * through to the object store.
219
- */
220
- export async function streamUpload({
221
- bucket: bucketName,
222
- stream,
223
- filename,
224
- type,
225
- extra,
226
- ttl,
227
- }: StreamUploadParams) {
228
- if (!stream) {
229
- throw new Error("Stream to upload is invalid/undefined")
230
- }
231
- const extension = filename.split(".").pop()
232
- const objectStore = ObjectStore(bucketName)
233
- const bucketCreated = await createBucketIfNotExists(objectStore, bucketName)
234
-
235
- if (ttl && bucketCreated.created) {
236
- let ttlConfig = bucketTTLConfig(bucketName, ttl)
237
- await objectStore.putBucketLifecycleConfiguration(ttlConfig).promise()
238
- }
239
-
240
- // Set content type for certain known extensions
241
- if (filename?.endsWith(".js")) {
242
- extra = {
243
- ...extra,
244
- ContentType: "application/javascript",
245
- }
246
- } else if (filename?.endsWith(".svg")) {
247
- extra = {
248
- ...extra,
249
- ContentType: "image",
250
- }
251
- }
252
-
253
- let contentType = type
254
- if (!contentType) {
255
- contentType = extension
256
- ? CONTENT_TYPE_MAP[extension.toLowerCase()]
257
- : CONTENT_TYPE_MAP.txt
258
- }
259
-
260
- const bucket = sanitizeBucket(bucketName),
261
- objKey = sanitizeKey(filename)
262
- const params = {
263
- Bucket: bucket,
264
- Key: objKey,
265
- Body: stream,
266
- ContentType: contentType,
267
- ...extra,
268
- }
269
-
270
- const details = await objectStore.upload(params).promise()
271
- const headDetails = await objectStore
272
- .headObject({
273
- Bucket: bucket,
274
- Key: objKey,
275
- })
276
- .promise()
277
- return {
278
- ...details,
279
- ContentLength: headDetails.ContentLength,
280
- }
281
- }
282
-
283
- /**
284
- * retrieves the contents of a file from the object store, if it is a known content type it
285
- * will be converted, otherwise it will be returned as a buffer stream.
286
- */
287
- export async function retrieve(bucketName: string, filepath: string) {
288
- const objectStore = ObjectStore(bucketName)
289
- const params = {
290
- Bucket: sanitizeBucket(bucketName),
291
- Key: sanitizeKey(filepath),
292
- }
293
- const response: any = await objectStore.getObject(params).promise()
294
- // currently these are all strings
295
- if (STRING_CONTENT_TYPES.includes(response.ContentType)) {
296
- return response.Body.toString("utf8")
297
- } else {
298
- return response.Body
299
- }
300
- }
301
-
302
- export async function listAllObjects(bucketName: string, path: string) {
303
- const objectStore = ObjectStore(bucketName)
304
- const list = (params: ListParams = {}) => {
305
- return objectStore
306
- .listObjectsV2({
307
- ...params,
308
- Bucket: sanitizeBucket(bucketName),
309
- Prefix: sanitizeKey(path),
310
- })
311
- .promise()
312
- }
313
- let isTruncated = false,
314
- token,
315
- objects: AWS.S3.Types.Object[] = []
316
- do {
317
- let params: ListParams = {}
318
- if (token) {
319
- params.ContinuationToken = token
320
- }
321
- const response = await list(params)
322
- if (response.Contents) {
323
- objects = objects.concat(response.Contents)
324
- }
325
- isTruncated = !!response.IsTruncated
326
- token = response.NextContinuationToken
327
- } while (isTruncated && token)
328
- return objects
329
- }
330
-
331
- /**
332
- * Generate a presigned url with a default TTL of 1 hour
333
- */
334
- export function getPresignedUrl(
335
- bucketName: string,
336
- key: string,
337
- durationSeconds: number = 3600
338
- ) {
339
- const objectStore = ObjectStore(bucketName, { presigning: true })
340
- const params = {
341
- Bucket: sanitizeBucket(bucketName),
342
- Key: sanitizeKey(key),
343
- Expires: durationSeconds,
344
- }
345
- const url = objectStore.getSignedUrl("getObject", params)
346
-
347
- if (!env.MINIO_ENABLED) {
348
- // return the full URL to the client
349
- return url
350
- } else {
351
- // return the path only to the client
352
- // use the presigned url route to ensure the static
353
- // hostname will be used in the request
354
- const signedUrl = new URL(url)
355
- const path = signedUrl.pathname
356
- const query = signedUrl.search
357
- return `${SIGNED_FILE_PREFIX}${path}${query}`
358
- }
359
- }
360
-
361
- /**
362
- * Same as retrieval function but puts to a temporary file.
363
- */
364
- export async function retrieveToTmp(bucketName: string, filepath: string) {
365
- bucketName = sanitizeBucket(bucketName)
366
- filepath = sanitizeKey(filepath)
367
- const data = await retrieve(bucketName, filepath)
368
- const outputPath = join(budibaseTempDir(), v4())
369
- fs.writeFileSync(outputPath, data)
370
- return outputPath
371
- }
372
-
373
- export async function retrieveDirectory(bucketName: string, path: string) {
374
- let writePath = join(budibaseTempDir(), v4())
375
- fs.mkdirSync(writePath)
376
- const objects = await listAllObjects(bucketName, path)
377
- let streams = await Promise.all(
378
- objects.map(obj => getReadStream(bucketName, obj.Key!))
379
- )
380
- let count = 0
381
- const writePromises: Promise<Error>[] = []
382
- for (let obj of objects) {
383
- const filename = obj.Key!
384
- const stream = streams[count++]
385
- const possiblePath = filename.split("/")
386
- const dirs = possiblePath.slice(0, possiblePath.length - 1)
387
- const possibleDir = join(writePath, ...dirs)
388
- if (possiblePath.length > 1 && !fs.existsSync(possibleDir)) {
389
- fs.mkdirSync(possibleDir, { recursive: true })
390
- }
391
- const writeStream = fs.createWriteStream(join(writePath, ...possiblePath), {
392
- mode: 0o644,
393
- })
394
- stream.pipe(writeStream)
395
- writePromises.push(
396
- new Promise((resolve, reject) => {
397
- stream.on("finish", resolve)
398
- stream.on("error", reject)
399
- writeStream.on("error", reject)
400
- })
401
- )
402
- }
403
- await Promise.all(writePromises)
404
- return writePath
405
- }
406
-
407
- /**
408
- * Delete a single file.
409
- */
410
- export async function deleteFile(bucketName: string, filepath: string) {
411
- const objectStore = ObjectStore(bucketName)
412
- await createBucketIfNotExists(objectStore, bucketName)
413
- const params = {
414
- Bucket: bucketName,
415
- Key: sanitizeKey(filepath),
416
- }
417
- return objectStore.deleteObject(params).promise()
418
- }
419
-
420
- export async function deleteFiles(bucketName: string, filepaths: string[]) {
421
- const objectStore = ObjectStore(bucketName)
422
- await createBucketIfNotExists(objectStore, bucketName)
423
- const params = {
424
- Bucket: bucketName,
425
- Delete: {
426
- Objects: filepaths.map((path: any) => ({ Key: sanitizeKey(path) })),
427
- },
428
- }
429
- return objectStore.deleteObjects(params).promise()
430
- }
431
-
432
- /**
433
- * Delete a path, including everything within.
434
- */
435
- export async function deleteFolder(
436
- bucketName: string,
437
- folder: string
438
- ): Promise<any> {
439
- bucketName = sanitizeBucket(bucketName)
440
- folder = sanitizeKey(folder)
441
- const client = ObjectStore(bucketName)
442
- const listParams = {
443
- Bucket: bucketName,
444
- Prefix: folder,
445
- }
446
-
447
- const existingObjectsResponse = await client.listObjects(listParams).promise()
448
- if (existingObjectsResponse.Contents?.length === 0) {
449
- return
450
- }
451
- const deleteParams: any = {
452
- Bucket: bucketName,
453
- Delete: {
454
- Objects: [],
455
- },
456
- }
457
-
458
- existingObjectsResponse.Contents?.forEach((content: any) => {
459
- deleteParams.Delete.Objects.push({ Key: content.Key })
460
- })
461
-
462
- const deleteResponse = await client.deleteObjects(deleteParams).promise()
463
- // can only empty 1000 items at once
464
- if (deleteResponse.Deleted?.length === 1000) {
465
- return deleteFolder(bucketName, folder)
466
- }
467
- }
468
-
469
- export async function uploadDirectory(
470
- bucketName: string,
471
- localPath: string,
472
- bucketPath: string
473
- ) {
474
- bucketName = sanitizeBucket(bucketName)
475
- let uploads = []
476
- const files = fs.readdirSync(localPath, { withFileTypes: true })
477
- for (let file of files) {
478
- const path = sanitizeKey(join(bucketPath, file.name))
479
- const local = join(localPath, file.name)
480
- if (file.isDirectory()) {
481
- uploads.push(uploadDirectory(bucketName, local, path))
482
- } else {
483
- uploads.push(
484
- streamUpload({
485
- bucket: bucketName,
486
- filename: path,
487
- stream: fs.createReadStream(local),
488
- })
489
- )
490
- }
491
- }
492
- await Promise.all(uploads)
493
- return files
494
- }
495
-
496
- export async function downloadTarballDirect(
497
- url: string,
498
- path: string,
499
- headers = {}
500
- ) {
501
- path = sanitizeKey(path)
502
- const response = await fetch(url, { headers })
503
- if (!response.ok) {
504
- throw new Error(`unexpected response ${response.statusText}`)
505
- }
506
-
507
- await streamPipeline(response.body, zlib.createUnzip(), tar.extract(path))
508
- }
509
-
510
- export async function downloadTarball(
511
- url: string,
512
- bucketName: string,
513
- path: string
514
- ) {
515
- bucketName = sanitizeBucket(bucketName)
516
- path = sanitizeKey(path)
517
- const response = await fetch(url)
518
- if (!response.ok) {
519
- throw new Error(`unexpected response ${response.statusText}`)
520
- }
521
-
522
- const tmpPath = join(budibaseTempDir(), path)
523
- await streamPipeline(response.body, zlib.createUnzip(), tar.extract(tmpPath))
524
- if (!env.isTest() && env.SELF_HOSTED) {
525
- await uploadDirectory(bucketName, tmpPath, path)
526
- }
527
- // return the temporary path incase there is a use for it
528
- return tmpPath
529
- }
530
-
531
- export async function getReadStream(
532
- bucketName: string,
533
- path: string
534
- ): Promise<Readable> {
535
- bucketName = sanitizeBucket(bucketName)
536
- path = sanitizeKey(path)
537
- const client = ObjectStore(bucketName)
538
- const params = {
539
- Bucket: bucketName,
540
- Key: path,
541
- }
542
- return client.getObject(params).createReadStream()
543
- }
544
-
545
- export async function getObjectMetadata(
546
- bucket: string,
547
- path: string
548
- ): Promise<HeadObjectOutput> {
549
- bucket = sanitizeBucket(bucket)
550
- path = sanitizeKey(path)
551
-
552
- const client = ObjectStore(bucket)
553
- const params = {
554
- Bucket: bucket,
555
- Key: path,
556
- }
557
-
558
- try {
559
- return await client.headObject(params).promise()
560
- } catch (err: any) {
561
- throw new Error("Unable to retrieve metadata from object")
562
- }
563
- }
564
-
565
- /*
566
- Given a signed url like '/files/signed/tmp-files-attachments/app_123456/myfile.txt' extract
567
- the bucket and the path from it
568
- */
569
- export function extractBucketAndPath(
570
- url: string
571
- ): { bucket: string; path: string } | null {
572
- const baseUrl = url.split("?")[0]
573
-
574
- const regex = new RegExp(
575
- `^${SIGNED_FILE_PREFIX}/(?<bucket>[^/]+)/(?<path>.+)$`
576
- )
577
- const match = baseUrl.match(regex)
578
-
579
- if (match && match.groups) {
580
- const { bucket, path } = match.groups
581
- return { bucket, path }
582
- }
583
-
584
- return null
585
- }
@@ -1,113 +0,0 @@
1
- import path, { join } from "path"
2
- import { tmpdir } from "os"
3
- import fs from "fs"
4
- import env from "../environment"
5
- import { PutBucketLifecycleConfigurationRequest } from "aws-sdk/clients/s3"
6
- import * as objectStore from "./objectStore"
7
- import {
8
- AutomationAttachment,
9
- AutomationAttachmentContent,
10
- BucketedContent,
11
- } from "@budibase/types"
12
- import stream from "stream"
13
- import streamWeb from "node:stream/web"
14
-
15
- /****************************************************
16
- * NOTE: When adding a new bucket - name *
17
- * sure that S3 usages (like budibase-infra) *
18
- * have been updated to have a unique bucket name. *
19
- ****************************************************/
20
- // can't be an enum - only numbers can be used for computed types
21
- export const ObjectStoreBuckets = {
22
- BACKUPS: env.BACKUPS_BUCKET_NAME,
23
- APPS: env.APPS_BUCKET_NAME,
24
- TEMPLATES: env.TEMPLATES_BUCKET_NAME,
25
- GLOBAL: env.GLOBAL_BUCKET_NAME,
26
- PLUGINS: env.PLUGIN_BUCKET_NAME,
27
- TEMP: env.TEMP_BUCKET_NAME,
28
- }
29
-
30
- const bbTmp = join(tmpdir(), ".budibase")
31
- try {
32
- fs.mkdirSync(bbTmp)
33
- } catch (e: any) {
34
- if (e.code !== "EEXIST") {
35
- throw e
36
- }
37
- }
38
-
39
- export function budibaseTempDir() {
40
- return bbTmp
41
- }
42
-
43
- export const bucketTTLConfig = (
44
- bucketName: string,
45
- days: number
46
- ): PutBucketLifecycleConfigurationRequest => {
47
- const lifecycleRule = {
48
- ID: `${bucketName}-ExpireAfter${days}days`,
49
- Prefix: "",
50
- Status: "Enabled",
51
- Expiration: {
52
- Days: days,
53
- },
54
- }
55
- const lifecycleConfiguration = {
56
- Rules: [lifecycleRule],
57
- }
58
-
59
- return {
60
- Bucket: bucketName,
61
- LifecycleConfiguration: lifecycleConfiguration,
62
- }
63
- }
64
-
65
- async function processUrlAttachment(
66
- attachment: AutomationAttachment
67
- ): Promise<AutomationAttachmentContent> {
68
- const response = await fetch(attachment.url)
69
- if (!response.ok || !response.body) {
70
- throw new Error(`Unexpected response ${response.statusText}`)
71
- }
72
- const fallbackFilename = path.basename(new URL(attachment.url).pathname)
73
- if (!response.body) {
74
- throw new Error("No response received for attachment")
75
- }
76
- return {
77
- filename: attachment.filename || fallbackFilename,
78
- content: stream.Readable.fromWeb(response.body as streamWeb.ReadableStream),
79
- }
80
- }
81
-
82
- export async function processObjectStoreAttachment(
83
- attachment: AutomationAttachment
84
- ): Promise<BucketedContent> {
85
- const result = objectStore.extractBucketAndPath(attachment.url)
86
-
87
- if (result === null) {
88
- throw new Error("Invalid signed URL")
89
- }
90
-
91
- const { bucket, path: objectPath } = result
92
- const readStream = await objectStore.getReadStream(bucket, objectPath)
93
- const fallbackFilename = path.basename(objectPath)
94
- return {
95
- bucket,
96
- path: objectPath,
97
- filename: attachment.filename || fallbackFilename,
98
- content: readStream,
99
- }
100
- }
101
-
102
- export async function processAutomationAttachment(
103
- attachment: AutomationAttachment
104
- ): Promise<AutomationAttachmentContent | BucketedContent> {
105
- const isFullyFormedUrl =
106
- attachment.url?.startsWith("http://") ||
107
- attachment.url?.startsWith("https://")
108
- if (isFullyFormedUrl) {
109
- return await processUrlAttachment(attachment)
110
- } else {
111
- return await processObjectStoreAttachment(attachment)
112
- }
113
- }
@@ -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
- }