@budibase/backend-core 3.2.5 → 3.2.7

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 (276) hide show
  1. package/dist/index.js +7 -1
  2. package/dist/index.js.map +2 -2
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +11 -4
  5. package/dist/plugins.js.meta.json +1 -1
  6. package/dist/src/environment.d.ts +1 -0
  7. package/dist/src/environment.js +6 -1
  8. package/dist/src/environment.js.map +1 -1
  9. package/package.json +11 -4
  10. package/src/accounts/accounts.ts +0 -82
  11. package/src/accounts/api.ts +0 -59
  12. package/src/accounts/index.ts +0 -1
  13. package/src/auth/auth.ts +0 -210
  14. package/src/auth/index.ts +0 -1
  15. package/src/auth/tests/auth.spec.ts +0 -14
  16. package/src/blacklist/blacklist.ts +0 -54
  17. package/src/blacklist/index.ts +0 -1
  18. package/src/blacklist/tests/blacklist.spec.ts +0 -46
  19. package/src/cache/appMetadata.ts +0 -88
  20. package/src/cache/base/index.ts +0 -150
  21. package/src/cache/docWritethrough.ts +0 -105
  22. package/src/cache/generic.ts +0 -33
  23. package/src/cache/index.ts +0 -8
  24. package/src/cache/invite.ts +0 -86
  25. package/src/cache/passwordReset.ts +0 -49
  26. package/src/cache/tests/docWritethrough.spec.ts +0 -296
  27. package/src/cache/tests/user.spec.ts +0 -145
  28. package/src/cache/tests/writethrough.spec.ts +0 -139
  29. package/src/cache/user.ts +0 -154
  30. package/src/cache/writethrough.ts +0 -133
  31. package/src/configs/configs.ts +0 -263
  32. package/src/configs/index.ts +0 -1
  33. package/src/configs/tests/configs.spec.ts +0 -184
  34. package/src/constants/db.ts +0 -75
  35. package/src/constants/index.ts +0 -2
  36. package/src/constants/misc.ts +0 -36
  37. package/src/context/Context.ts +0 -14
  38. package/src/context/identity.ts +0 -58
  39. package/src/context/index.ts +0 -3
  40. package/src/context/mainContext.ts +0 -422
  41. package/src/context/tests/index.spec.ts +0 -255
  42. package/src/context/types.ts +0 -26
  43. package/src/db/Replication.ts +0 -94
  44. package/src/db/couch/DatabaseImpl.ts +0 -511
  45. package/src/db/couch/connections.ts +0 -89
  46. package/src/db/couch/index.ts +0 -4
  47. package/src/db/couch/pouchDB.ts +0 -97
  48. package/src/db/couch/pouchDump.ts +0 -0
  49. package/src/db/couch/tests/DatabaseImpl.spec.ts +0 -118
  50. package/src/db/couch/utils.ts +0 -55
  51. package/src/db/db.ts +0 -34
  52. package/src/db/errors.ts +0 -14
  53. package/src/db/index.ts +0 -12
  54. package/src/db/instrumentation.ts +0 -199
  55. package/src/db/lucene.ts +0 -721
  56. package/src/db/searchIndexes/index.ts +0 -1
  57. package/src/db/searchIndexes/searchIndexes.ts +0 -62
  58. package/src/db/tests/DatabaseImpl.spec.ts +0 -55
  59. package/src/db/tests/connections.spec.ts +0 -22
  60. package/src/db/tests/index.spec.ts +0 -32
  61. package/src/db/tests/lucene.spec.ts +0 -400
  62. package/src/db/tests/pouch.spec.js +0 -62
  63. package/src/db/tests/utils.spec.ts +0 -63
  64. package/src/db/utils.ts +0 -208
  65. package/src/db/views.ts +0 -245
  66. package/src/docIds/conversions.ts +0 -60
  67. package/src/docIds/ids.ts +0 -126
  68. package/src/docIds/index.ts +0 -2
  69. package/src/docIds/newid.ts +0 -5
  70. package/src/docIds/params.ts +0 -189
  71. package/src/docUpdates/index.ts +0 -24
  72. package/src/environment.ts +0 -293
  73. package/src/errors/errors.ts +0 -119
  74. package/src/errors/index.ts +0 -1
  75. package/src/events/analytics.ts +0 -6
  76. package/src/events/asyncEvents/index.ts +0 -2
  77. package/src/events/asyncEvents/publisher.ts +0 -12
  78. package/src/events/asyncEvents/queue.ts +0 -22
  79. package/src/events/backfill.ts +0 -183
  80. package/src/events/documentId.ts +0 -56
  81. package/src/events/events.ts +0 -47
  82. package/src/events/identification.ts +0 -311
  83. package/src/events/index.ts +0 -15
  84. package/src/events/processors/AnalyticsProcessor.ts +0 -64
  85. package/src/events/processors/AuditLogsProcessor.ts +0 -92
  86. package/src/events/processors/LoggingProcessor.ts +0 -36
  87. package/src/events/processors/Processors.ts +0 -52
  88. package/src/events/processors/async/DocumentUpdateProcessor.ts +0 -38
  89. package/src/events/processors/index.ts +0 -19
  90. package/src/events/processors/posthog/PosthogProcessor.ts +0 -118
  91. package/src/events/processors/posthog/index.ts +0 -3
  92. package/src/events/processors/posthog/rateLimiting.ts +0 -106
  93. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +0 -164
  94. package/src/events/processors/types.ts +0 -1
  95. package/src/events/publishers/account.ts +0 -41
  96. package/src/events/publishers/ai.ts +0 -21
  97. package/src/events/publishers/app.ts +0 -168
  98. package/src/events/publishers/auditLog.ts +0 -26
  99. package/src/events/publishers/auth.ts +0 -73
  100. package/src/events/publishers/automation.ts +0 -110
  101. package/src/events/publishers/backfill.ts +0 -74
  102. package/src/events/publishers/backup.ts +0 -42
  103. package/src/events/publishers/datasource.ts +0 -48
  104. package/src/events/publishers/email.ts +0 -17
  105. package/src/events/publishers/environmentVariable.ts +0 -38
  106. package/src/events/publishers/group.ts +0 -99
  107. package/src/events/publishers/index.ts +0 -25
  108. package/src/events/publishers/installation.ts +0 -38
  109. package/src/events/publishers/layout.ts +0 -26
  110. package/src/events/publishers/license.ts +0 -84
  111. package/src/events/publishers/org.ts +0 -37
  112. package/src/events/publishers/plugin.ts +0 -47
  113. package/src/events/publishers/query.ts +0 -89
  114. package/src/events/publishers/role.ts +0 -62
  115. package/src/events/publishers/rows.ts +0 -29
  116. package/src/events/publishers/screen.ts +0 -36
  117. package/src/events/publishers/serve.ts +0 -43
  118. package/src/events/publishers/table.ts +0 -70
  119. package/src/events/publishers/user.ts +0 -202
  120. package/src/events/publishers/view.ts +0 -107
  121. package/src/features/features.ts +0 -277
  122. package/src/features/index.ts +0 -2
  123. package/src/features/tests/features.spec.ts +0 -267
  124. package/src/features/tests/utils.ts +0 -64
  125. package/src/helpers.ts +0 -9
  126. package/src/index.ts +0 -59
  127. package/src/installation.ts +0 -115
  128. package/src/logging/alerts.ts +0 -26
  129. package/src/logging/correlation/correlation.ts +0 -15
  130. package/src/logging/correlation/index.ts +0 -1
  131. package/src/logging/correlation/middleware.ts +0 -18
  132. package/src/logging/index.ts +0 -4
  133. package/src/logging/pino/logger.ts +0 -239
  134. package/src/logging/pino/middleware.ts +0 -48
  135. package/src/logging/system.ts +0 -81
  136. package/src/logging/tests/system.spec.ts +0 -61
  137. package/src/middleware/adminOnly.ts +0 -9
  138. package/src/middleware/auditLog.ts +0 -6
  139. package/src/middleware/authenticated.ts +0 -247
  140. package/src/middleware/builderOnly.ts +0 -21
  141. package/src/middleware/builderOrAdmin.ts +0 -21
  142. package/src/middleware/contentSecurityPolicy.ts +0 -113
  143. package/src/middleware/csrf.ts +0 -81
  144. package/src/middleware/errorHandling.ts +0 -43
  145. package/src/middleware/index.ts +0 -24
  146. package/src/middleware/internalApi.ts +0 -23
  147. package/src/middleware/ip.ts +0 -12
  148. package/src/middleware/joi-validator.ts +0 -58
  149. package/src/middleware/matchers.ts +0 -39
  150. package/src/middleware/passport/datasource/google.ts +0 -102
  151. package/src/middleware/passport/local.ts +0 -54
  152. package/src/middleware/passport/sso/google.ts +0 -77
  153. package/src/middleware/passport/sso/oidc.ts +0 -152
  154. package/src/middleware/passport/sso/sso.ts +0 -138
  155. package/src/middleware/passport/sso/tests/google.spec.ts +0 -68
  156. package/src/middleware/passport/sso/tests/oidc.spec.ts +0 -144
  157. package/src/middleware/passport/sso/tests/sso.spec.ts +0 -197
  158. package/src/middleware/passport/utils.ts +0 -38
  159. package/src/middleware/querystringToBody.ts +0 -28
  160. package/src/middleware/tenancy.ts +0 -36
  161. package/src/middleware/tests/builder.spec.ts +0 -181
  162. package/src/middleware/tests/contentSecurityPolicy.spec.ts +0 -75
  163. package/src/middleware/tests/matchers.spec.ts +0 -100
  164. package/src/migrations/definitions.ts +0 -40
  165. package/src/migrations/index.ts +0 -2
  166. package/src/migrations/migrations.ts +0 -186
  167. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +0 -11
  168. package/src/migrations/tests/migrations.spec.ts +0 -64
  169. package/src/objectStore/buckets/app.ts +0 -53
  170. package/src/objectStore/buckets/global.ts +0 -29
  171. package/src/objectStore/buckets/index.ts +0 -3
  172. package/src/objectStore/buckets/plugins.ts +0 -71
  173. package/src/objectStore/buckets/tests/app.spec.ts +0 -161
  174. package/src/objectStore/buckets/tests/global.spec.ts +0 -74
  175. package/src/objectStore/buckets/tests/plugins.spec.ts +0 -111
  176. package/src/objectStore/cloudfront.ts +0 -41
  177. package/src/objectStore/index.ts +0 -3
  178. package/src/objectStore/objectStore.ts +0 -585
  179. package/src/objectStore/utils.ts +0 -113
  180. package/src/platform/index.ts +0 -3
  181. package/src/platform/platformDb.ts +0 -6
  182. package/src/platform/tenants.ts +0 -101
  183. package/src/platform/tests/tenants.spec.ts +0 -26
  184. package/src/platform/users.ts +0 -129
  185. package/src/plugin/index.ts +0 -1
  186. package/src/plugin/tests/validation.spec.ts +0 -209
  187. package/src/plugin/utils.ts +0 -175
  188. package/src/queue/constants.ts +0 -8
  189. package/src/queue/inMemoryQueue.ts +0 -189
  190. package/src/queue/index.ts +0 -2
  191. package/src/queue/listeners.ts +0 -199
  192. package/src/queue/queue.ts +0 -84
  193. package/src/redis/index.ts +0 -6
  194. package/src/redis/init.ts +0 -118
  195. package/src/redis/redis.ts +0 -358
  196. package/src/redis/redlockImpl.ts +0 -155
  197. package/src/redis/tests/redis.spec.ts +0 -207
  198. package/src/redis/tests/redlockImpl.spec.ts +0 -105
  199. package/src/redis/utils.ts +0 -128
  200. package/src/security/auth.ts +0 -24
  201. package/src/security/encryption.ts +0 -185
  202. package/src/security/index.ts +0 -1
  203. package/src/security/permissions.ts +0 -166
  204. package/src/security/roles.ts +0 -655
  205. package/src/security/secrets.ts +0 -20
  206. package/src/security/sessions.ts +0 -123
  207. package/src/security/tests/auth.spec.ts +0 -45
  208. package/src/security/tests/encryption.spec.ts +0 -31
  209. package/src/security/tests/permissions.spec.ts +0 -146
  210. package/src/security/tests/secrets.spec.ts +0 -35
  211. package/src/security/tests/sessions.spec.ts +0 -12
  212. package/src/sql/designDoc.ts +0 -17
  213. package/src/sql/index.ts +0 -5
  214. package/src/sql/sql.ts +0 -1854
  215. package/src/sql/sqlTable.ts +0 -319
  216. package/src/sql/utils.ts +0 -193
  217. package/src/tenancy/db.ts +0 -6
  218. package/src/tenancy/index.ts +0 -2
  219. package/src/tenancy/tenancy.ts +0 -148
  220. package/src/tenancy/tests/tenancy.spec.ts +0 -184
  221. package/src/timers/index.ts +0 -1
  222. package/src/timers/timers.ts +0 -22
  223. package/src/users/db.ts +0 -582
  224. package/src/users/events.ts +0 -176
  225. package/src/users/index.ts +0 -4
  226. package/src/users/lookup.ts +0 -99
  227. package/src/users/test/db.spec.ts +0 -188
  228. package/src/users/test/utils.spec.ts +0 -67
  229. package/src/users/users.ts +0 -353
  230. package/src/users/utils.ts +0 -81
  231. package/src/utils/Duration.ts +0 -56
  232. package/src/utils/hashing.ts +0 -15
  233. package/src/utils/index.ts +0 -4
  234. package/src/utils/stringUtils.ts +0 -8
  235. package/src/utils/tests/Duration.spec.ts +0 -19
  236. package/src/utils/tests/utils.spec.ts +0 -204
  237. package/src/utils/utils.ts +0 -249
  238. package/tests/core/logging.ts +0 -34
  239. package/tests/core/users/users.spec.js +0 -53
  240. package/tests/core/utilities/index.ts +0 -7
  241. package/tests/core/utilities/jestUtils.ts +0 -33
  242. package/tests/core/utilities/mocks/alerts.ts +0 -4
  243. package/tests/core/utilities/mocks/date.ts +0 -3
  244. package/tests/core/utilities/mocks/events.ts +0 -132
  245. package/tests/core/utilities/mocks/index.ts +0 -9
  246. package/tests/core/utilities/mocks/licenses.ts +0 -119
  247. package/tests/core/utilities/queue.ts +0 -9
  248. package/tests/core/utilities/structures/Chance.ts +0 -20
  249. package/tests/core/utilities/structures/accounts.ts +0 -80
  250. package/tests/core/utilities/structures/apps.ts +0 -21
  251. package/tests/core/utilities/structures/common.ts +0 -7
  252. package/tests/core/utilities/structures/db.ts +0 -12
  253. package/tests/core/utilities/structures/documents/index.ts +0 -1
  254. package/tests/core/utilities/structures/documents/platform/index.ts +0 -1
  255. package/tests/core/utilities/structures/documents/platform/installation.ts +0 -12
  256. package/tests/core/utilities/structures/generator.ts +0 -3
  257. package/tests/core/utilities/structures/index.ts +0 -15
  258. package/tests/core/utilities/structures/koa.ts +0 -16
  259. package/tests/core/utilities/structures/licenses.ts +0 -190
  260. package/tests/core/utilities/structures/plugins.ts +0 -19
  261. package/tests/core/utilities/structures/quotas.ts +0 -72
  262. package/tests/core/utilities/structures/scim.ts +0 -80
  263. package/tests/core/utilities/structures/sso.ts +0 -118
  264. package/tests/core/utilities/structures/tenants.ts +0 -5
  265. package/tests/core/utilities/structures/userGroups.ts +0 -10
  266. package/tests/core/utilities/structures/users.ts +0 -89
  267. package/tests/core/utilities/testContainerUtils.ts +0 -165
  268. package/tests/core/utilities/utils/index.ts +0 -2
  269. package/tests/core/utilities/utils/queue.ts +0 -27
  270. package/tests/core/utilities/utils/time.ts +0 -3
  271. package/tests/extra/DBTestConfiguration.ts +0 -36
  272. package/tests/extra/index.ts +0 -2
  273. package/tests/extra/testEnv.ts +0 -95
  274. package/tests/index.ts +0 -2
  275. package/tests/jestEnv.ts +0 -10
  276. 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
- }