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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (252) hide show
  1. package/dist/index.js +5 -4
  2. package/dist/index.js.map +2 -2
  3. package/dist/index.js.meta.json +1 -1
  4. package/dist/package.json +6 -6
  5. package/dist/src/cache/appMetadata.js +1 -1
  6. package/dist/src/cache/appMetadata.js.map +1 -1
  7. package/dist/src/constants/misc.d.ts +0 -2
  8. package/dist/src/constants/misc.js +0 -2
  9. package/dist/src/constants/misc.js.map +1 -1
  10. package/dist/src/environment.js +5 -4
  11. package/dist/src/environment.js.map +1 -1
  12. package/dist/src/logging/system.d.ts +1 -1
  13. package/dist/src/timers/timers.d.ts +1 -1
  14. package/package.json +6 -6
  15. package/src/accounts/accounts.ts +82 -0
  16. package/src/accounts/api.ts +59 -0
  17. package/src/accounts/index.ts +1 -0
  18. package/src/auth/auth.ts +208 -0
  19. package/src/auth/index.ts +1 -0
  20. package/src/auth/tests/auth.spec.ts +14 -0
  21. package/src/blacklist/blacklist.ts +54 -0
  22. package/src/blacklist/index.ts +1 -0
  23. package/src/blacklist/tests/blacklist.spec.ts +46 -0
  24. package/src/cache/appMetadata.ts +88 -0
  25. package/src/cache/base/index.ts +92 -0
  26. package/src/cache/generic.ts +30 -0
  27. package/src/cache/index.ts +5 -0
  28. package/src/cache/tests/writethrough.spec.ts +138 -0
  29. package/src/cache/user.ts +83 -0
  30. package/src/cache/writethrough.ts +133 -0
  31. package/src/configs/configs.ts +257 -0
  32. package/src/configs/index.ts +1 -0
  33. package/src/configs/tests/configs.spec.ts +184 -0
  34. package/src/constants/db.ts +63 -0
  35. package/src/constants/index.ts +2 -0
  36. package/src/constants/misc.ts +50 -0
  37. package/src/context/Context.ts +14 -0
  38. package/src/context/identity.ts +58 -0
  39. package/src/context/index.ts +3 -0
  40. package/src/context/mainContext.ts +310 -0
  41. package/src/context/tests/index.spec.ts +147 -0
  42. package/src/context/types.ts +11 -0
  43. package/src/db/Replication.ts +84 -0
  44. package/src/db/constants.ts +10 -0
  45. package/src/db/couch/DatabaseImpl.ts +238 -0
  46. package/src/db/couch/connections.ts +77 -0
  47. package/src/db/couch/index.ts +5 -0
  48. package/src/db/couch/pouchDB.ts +97 -0
  49. package/src/db/couch/pouchDump.ts +0 -0
  50. package/src/db/couch/utils.ts +50 -0
  51. package/src/db/db.ts +43 -0
  52. package/src/db/errors.ts +14 -0
  53. package/src/db/index.ts +12 -0
  54. package/src/db/lucene.ts +750 -0
  55. package/src/db/searchIndexes/index.ts +1 -0
  56. package/src/db/searchIndexes/searchIndexes.ts +62 -0
  57. package/src/db/tests/index.spec.js +25 -0
  58. package/src/db/tests/lucene.spec.ts +368 -0
  59. package/src/db/tests/pouch.spec.js +62 -0
  60. package/src/db/tests/utils.spec.ts +63 -0
  61. package/src/db/utils.ts +207 -0
  62. package/src/db/views.ts +241 -0
  63. package/src/docIds/conversions.ts +59 -0
  64. package/src/docIds/ids.ts +113 -0
  65. package/src/docIds/index.ts +2 -0
  66. package/src/docIds/newid.ts +5 -0
  67. package/src/docIds/params.ts +174 -0
  68. package/src/docUpdates/index.ts +29 -0
  69. package/src/environment.ts +201 -0
  70. package/src/errors/errors.ts +119 -0
  71. package/src/errors/index.ts +1 -0
  72. package/src/events/analytics.ts +6 -0
  73. package/src/events/asyncEvents/index.ts +2 -0
  74. package/src/events/asyncEvents/publisher.ts +12 -0
  75. package/src/events/asyncEvents/queue.ts +22 -0
  76. package/src/events/backfill.ts +183 -0
  77. package/src/events/documentId.ts +56 -0
  78. package/src/events/events.ts +40 -0
  79. package/src/events/identification.ts +310 -0
  80. package/src/events/index.ts +14 -0
  81. package/src/events/processors/AnalyticsProcessor.ts +64 -0
  82. package/src/events/processors/AuditLogsProcessor.ts +93 -0
  83. package/src/events/processors/LoggingProcessor.ts +37 -0
  84. package/src/events/processors/Processors.ts +52 -0
  85. package/src/events/processors/async/DocumentUpdateProcessor.ts +43 -0
  86. package/src/events/processors/index.ts +19 -0
  87. package/src/events/processors/posthog/PosthogProcessor.ts +118 -0
  88. package/src/events/processors/posthog/index.ts +2 -0
  89. package/src/events/processors/posthog/rateLimiting.ts +106 -0
  90. package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +168 -0
  91. package/src/events/processors/types.ts +1 -0
  92. package/src/events/publishers/account.ts +35 -0
  93. package/src/events/publishers/app.ts +155 -0
  94. package/src/events/publishers/auditLog.ts +26 -0
  95. package/src/events/publishers/auth.ts +73 -0
  96. package/src/events/publishers/automation.ts +110 -0
  97. package/src/events/publishers/backfill.ts +74 -0
  98. package/src/events/publishers/backup.ts +42 -0
  99. package/src/events/publishers/datasource.ts +48 -0
  100. package/src/events/publishers/email.ts +17 -0
  101. package/src/events/publishers/environmentVariable.ts +38 -0
  102. package/src/events/publishers/group.ts +99 -0
  103. package/src/events/publishers/index.ts +24 -0
  104. package/src/events/publishers/installation.ts +38 -0
  105. package/src/events/publishers/layout.ts +26 -0
  106. package/src/events/publishers/license.ts +84 -0
  107. package/src/events/publishers/org.ts +37 -0
  108. package/src/events/publishers/plugin.ts +47 -0
  109. package/src/events/publishers/query.ts +88 -0
  110. package/src/events/publishers/role.ts +62 -0
  111. package/src/events/publishers/rows.ts +29 -0
  112. package/src/events/publishers/screen.ts +36 -0
  113. package/src/events/publishers/serve.ts +43 -0
  114. package/src/events/publishers/table.ts +70 -0
  115. package/src/events/publishers/user.ts +202 -0
  116. package/src/events/publishers/view.ts +107 -0
  117. package/src/features/index.ts +78 -0
  118. package/src/features/installation.ts +17 -0
  119. package/src/features/tests/featureFlags.spec.ts +85 -0
  120. package/src/helpers.ts +9 -0
  121. package/src/index.ts +54 -0
  122. package/src/installation.ts +107 -0
  123. package/src/logging/alerts.ts +26 -0
  124. package/src/logging/correlation/correlation.ts +13 -0
  125. package/src/logging/correlation/index.ts +1 -0
  126. package/src/logging/correlation/middleware.ts +17 -0
  127. package/src/logging/index.ts +4 -0
  128. package/src/logging/pino/logger.ts +232 -0
  129. package/src/logging/pino/middleware.ts +45 -0
  130. package/src/logging/system.ts +81 -0
  131. package/src/logging/tests/system.spec.ts +61 -0
  132. package/src/middleware/adminOnly.ts +9 -0
  133. package/src/middleware/auditLog.ts +6 -0
  134. package/src/middleware/authenticated.ts +193 -0
  135. package/src/middleware/builderOnly.ts +21 -0
  136. package/src/middleware/builderOrAdmin.ts +21 -0
  137. package/src/middleware/csrf.ts +81 -0
  138. package/src/middleware/errorHandling.ts +29 -0
  139. package/src/middleware/index.ts +21 -0
  140. package/src/middleware/internalApi.ts +23 -0
  141. package/src/middleware/joi-validator.ts +45 -0
  142. package/src/middleware/matchers.ts +47 -0
  143. package/src/middleware/passport/datasource/google.ts +95 -0
  144. package/src/middleware/passport/local.ts +54 -0
  145. package/src/middleware/passport/sso/google.ts +77 -0
  146. package/src/middleware/passport/sso/oidc.ts +154 -0
  147. package/src/middleware/passport/sso/sso.ts +165 -0
  148. package/src/middleware/passport/sso/tests/google.spec.ts +67 -0
  149. package/src/middleware/passport/sso/tests/oidc.spec.ts +152 -0
  150. package/src/middleware/passport/sso/tests/sso.spec.ts +197 -0
  151. package/src/middleware/passport/utils.ts +38 -0
  152. package/src/middleware/querystringToBody.ts +28 -0
  153. package/src/middleware/tenancy.ts +36 -0
  154. package/src/middleware/tests/builder.spec.ts +180 -0
  155. package/src/middleware/tests/matchers.spec.ts +134 -0
  156. package/src/migrations/definitions.ts +40 -0
  157. package/src/migrations/index.ts +2 -0
  158. package/src/migrations/migrations.ts +191 -0
  159. package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +11 -0
  160. package/src/migrations/tests/migrations.spec.ts +64 -0
  161. package/src/objectStore/buckets/app.ts +40 -0
  162. package/src/objectStore/buckets/global.ts +29 -0
  163. package/src/objectStore/buckets/index.ts +3 -0
  164. package/src/objectStore/buckets/plugins.ts +71 -0
  165. package/src/objectStore/buckets/tests/app.spec.ts +171 -0
  166. package/src/objectStore/buckets/tests/global.spec.ts +74 -0
  167. package/src/objectStore/buckets/tests/plugins.spec.ts +111 -0
  168. package/src/objectStore/cloudfront.ts +41 -0
  169. package/src/objectStore/index.ts +3 -0
  170. package/src/objectStore/objectStore.ts +440 -0
  171. package/src/objectStore/utils.ts +27 -0
  172. package/src/platform/index.ts +3 -0
  173. package/src/platform/platformDb.ts +6 -0
  174. package/src/platform/tenants.ts +101 -0
  175. package/src/platform/tests/tenants.spec.ts +26 -0
  176. package/src/platform/users.ts +90 -0
  177. package/src/plugin/index.ts +1 -0
  178. package/src/plugin/tests/validation.spec.ts +83 -0
  179. package/src/plugin/utils.ts +156 -0
  180. package/src/queue/constants.ts +6 -0
  181. package/src/queue/inMemoryQueue.ts +141 -0
  182. package/src/queue/index.ts +2 -0
  183. package/src/queue/listeners.ts +195 -0
  184. package/src/queue/queue.ts +54 -0
  185. package/src/redis/index.ts +6 -0
  186. package/src/redis/init.ts +86 -0
  187. package/src/redis/redis.ts +308 -0
  188. package/src/redis/redlockImpl.ts +139 -0
  189. package/src/redis/utils.ts +117 -0
  190. package/src/security/encryption.ts +179 -0
  191. package/src/security/permissions.ts +158 -0
  192. package/src/security/roles.ts +389 -0
  193. package/src/security/sessions.ts +120 -0
  194. package/src/security/tests/encryption.spec.ts +31 -0
  195. package/src/security/tests/permissions.spec.ts +145 -0
  196. package/src/security/tests/sessions.spec.ts +12 -0
  197. package/src/tenancy/db.ts +6 -0
  198. package/src/tenancy/index.ts +2 -0
  199. package/src/tenancy/tenancy.ts +140 -0
  200. package/src/tenancy/tests/tenancy.spec.ts +184 -0
  201. package/src/timers/index.ts +1 -0
  202. package/src/timers/timers.ts +22 -0
  203. package/src/users/db.ts +484 -0
  204. package/src/users/events.ts +176 -0
  205. package/src/users/index.ts +4 -0
  206. package/src/users/lookup.ts +102 -0
  207. package/src/users/users.ts +276 -0
  208. package/src/users/utils.ts +55 -0
  209. package/src/utils/hashing.ts +14 -0
  210. package/src/utils/index.ts +3 -0
  211. package/src/utils/stringUtils.ts +8 -0
  212. package/src/utils/tests/utils.spec.ts +191 -0
  213. package/src/utils/utils.ts +239 -0
  214. package/tests/core/logging.ts +34 -0
  215. package/tests/core/utilities/index.ts +6 -0
  216. package/tests/core/utilities/jestUtils.ts +30 -0
  217. package/tests/core/utilities/mocks/alerts.ts +3 -0
  218. package/tests/core/utilities/mocks/date.ts +2 -0
  219. package/tests/core/utilities/mocks/events.ts +131 -0
  220. package/tests/core/utilities/mocks/fetch.ts +17 -0
  221. package/tests/core/utilities/mocks/index.ts +10 -0
  222. package/tests/core/utilities/mocks/licenses.ts +115 -0
  223. package/tests/core/utilities/mocks/posthog.ts +7 -0
  224. package/tests/core/utilities/structures/Chance.ts +20 -0
  225. package/tests/core/utilities/structures/accounts.ts +115 -0
  226. package/tests/core/utilities/structures/apps.ts +21 -0
  227. package/tests/core/utilities/structures/common.ts +7 -0
  228. package/tests/core/utilities/structures/db.ts +12 -0
  229. package/tests/core/utilities/structures/documents/index.ts +1 -0
  230. package/tests/core/utilities/structures/documents/platform/index.ts +1 -0
  231. package/tests/core/utilities/structures/documents/platform/installation.ts +12 -0
  232. package/tests/core/utilities/structures/generator.ts +2 -0
  233. package/tests/core/utilities/structures/index.ts +15 -0
  234. package/tests/core/utilities/structures/koa.ts +16 -0
  235. package/tests/core/utilities/structures/licenses.ts +167 -0
  236. package/tests/core/utilities/structures/plugins.ts +19 -0
  237. package/tests/core/utilities/structures/quotas.ts +67 -0
  238. package/tests/core/utilities/structures/scim.ts +80 -0
  239. package/tests/core/utilities/structures/shared.ts +19 -0
  240. package/tests/core/utilities/structures/sso.ts +119 -0
  241. package/tests/core/utilities/structures/tenants.ts +5 -0
  242. package/tests/core/utilities/structures/userGroups.ts +10 -0
  243. package/tests/core/utilities/structures/users.ts +73 -0
  244. package/tests/core/utilities/testContainerUtils.ts +85 -0
  245. package/tests/core/utilities/utils/index.ts +1 -0
  246. package/tests/core/utilities/utils/time.ts +3 -0
  247. package/tests/extra/DBTestConfiguration.ts +36 -0
  248. package/tests/extra/index.ts +2 -0
  249. package/tests/extra/testEnv.ts +95 -0
  250. package/tests/index.ts +1 -0
  251. package/tests/jestEnv.ts +6 -0
  252. package/tests/jestSetup.ts +28 -0
@@ -0,0 +1,238 @@
1
+ import Nano from "@budibase/nano"
2
+ import {
3
+ AllDocsResponse,
4
+ AnyDocument,
5
+ Database,
6
+ DatabaseOpts,
7
+ DatabaseQueryOpts,
8
+ DatabasePutOpts,
9
+ DatabaseCreateIndexOpts,
10
+ DatabaseDeleteIndexOpts,
11
+ Document,
12
+ isDocument,
13
+ } from "@budibase/types"
14
+ import { getCouchInfo } from "./connections"
15
+ import { directCouchUrlCall } from "./utils"
16
+ import { getPouchDB } from "./pouchDB"
17
+ import { WriteStream, ReadStream } from "fs"
18
+ import { newid } from "../../docIds/newid"
19
+
20
+ function buildNano(couchInfo: { url: string; cookie: string }) {
21
+ return Nano({
22
+ url: couchInfo.url,
23
+ requestDefaults: {
24
+ headers: {
25
+ Authorization: couchInfo.cookie,
26
+ },
27
+ },
28
+ parseUrl: false,
29
+ })
30
+ }
31
+
32
+ export function DatabaseWithConnection(
33
+ dbName: string,
34
+ connection: string,
35
+ opts?: DatabaseOpts
36
+ ) {
37
+ if (!connection) {
38
+ throw new Error("Must provide connection details")
39
+ }
40
+ return new DatabaseImpl(dbName, opts, connection)
41
+ }
42
+
43
+ export class DatabaseImpl implements Database {
44
+ public readonly name: string
45
+ private static nano: Nano.ServerScope
46
+ private readonly instanceNano?: Nano.ServerScope
47
+ private readonly pouchOpts: DatabaseOpts
48
+
49
+ private readonly couchInfo = getCouchInfo()
50
+
51
+ constructor(dbName?: string, opts?: DatabaseOpts, connection?: string) {
52
+ if (dbName == null) {
53
+ throw new Error("Database name cannot be undefined.")
54
+ }
55
+ this.name = dbName
56
+ this.pouchOpts = opts || {}
57
+ if (connection) {
58
+ this.couchInfo = getCouchInfo(connection)
59
+ this.instanceNano = buildNano(this.couchInfo)
60
+ }
61
+ if (!DatabaseImpl.nano) {
62
+ DatabaseImpl.init()
63
+ }
64
+ }
65
+
66
+ static init() {
67
+ const couchInfo = getCouchInfo()
68
+ DatabaseImpl.nano = buildNano(couchInfo)
69
+ }
70
+
71
+ async exists() {
72
+ const response = await directCouchUrlCall({
73
+ url: `${this.couchInfo.url}/${this.name}`,
74
+ method: "HEAD",
75
+ cookie: this.couchInfo.cookie,
76
+ })
77
+ return response.status === 200
78
+ }
79
+
80
+ private nano() {
81
+ return this.instanceNano || DatabaseImpl.nano
82
+ }
83
+
84
+ async checkSetup() {
85
+ let shouldCreate = !this.pouchOpts?.skip_setup
86
+ // check exists in a lightweight fashion
87
+ let exists = await this.exists()
88
+ if (!shouldCreate && !exists) {
89
+ throw new Error("DB does not exist")
90
+ }
91
+ if (!exists) {
92
+ try {
93
+ await this.nano().db.create(this.name)
94
+ } catch (err: any) {
95
+ // Handling race conditions
96
+ if (err.statusCode !== 412) {
97
+ throw err
98
+ }
99
+ }
100
+ }
101
+ return this.nano().db.use(this.name)
102
+ }
103
+
104
+ private async updateOutput(fnc: any) {
105
+ try {
106
+ return await fnc()
107
+ } catch (err: any) {
108
+ if (err.statusCode) {
109
+ err.status = err.statusCode
110
+ }
111
+ throw err
112
+ }
113
+ }
114
+
115
+ async get<T>(id?: string): Promise<T | any> {
116
+ const db = await this.checkSetup()
117
+ if (!id) {
118
+ throw new Error("Unable to get doc without a valid _id.")
119
+ }
120
+ return this.updateOutput(() => db.get(id))
121
+ }
122
+
123
+ async remove(idOrDoc: string | Document, rev?: string) {
124
+ const db = await this.checkSetup()
125
+ let _id: string
126
+ let _rev: string
127
+
128
+ if (isDocument(idOrDoc)) {
129
+ _id = idOrDoc._id!
130
+ _rev = idOrDoc._rev!
131
+ } else {
132
+ _id = idOrDoc
133
+ _rev = rev!
134
+ }
135
+
136
+ if (!_id || !_rev) {
137
+ throw new Error("Unable to remove doc without a valid _id and _rev.")
138
+ }
139
+ return this.updateOutput(() => db.destroy(_id, _rev))
140
+ }
141
+
142
+ async post(document: AnyDocument, opts?: DatabasePutOpts) {
143
+ if (!document._id) {
144
+ document._id = newid()
145
+ }
146
+ return this.put(document, opts)
147
+ }
148
+
149
+ async put(document: AnyDocument, opts?: DatabasePutOpts) {
150
+ if (!document._id) {
151
+ throw new Error("Cannot store document without _id field.")
152
+ }
153
+ const db = await this.checkSetup()
154
+ if (!document.createdAt) {
155
+ document.createdAt = new Date().toISOString()
156
+ }
157
+ document.updatedAt = new Date().toISOString()
158
+ if (opts?.force && document._id) {
159
+ try {
160
+ const existing = await this.get(document._id)
161
+ if (existing) {
162
+ document._rev = existing._rev
163
+ }
164
+ } catch (err: any) {
165
+ if (err.status !== 404) {
166
+ throw err
167
+ }
168
+ }
169
+ }
170
+ return this.updateOutput(() => db.insert(document))
171
+ }
172
+
173
+ async bulkDocs(documents: AnyDocument[]) {
174
+ const db = await this.checkSetup()
175
+ return this.updateOutput(() => db.bulk({ docs: documents }))
176
+ }
177
+
178
+ async allDocs<T>(params: DatabaseQueryOpts): Promise<AllDocsResponse<T>> {
179
+ const db = await this.checkSetup()
180
+ return this.updateOutput(() => db.list(params))
181
+ }
182
+
183
+ async query<T>(
184
+ viewName: string,
185
+ params: DatabaseQueryOpts
186
+ ): Promise<AllDocsResponse<T>> {
187
+ const db = await this.checkSetup()
188
+ const [database, view] = viewName.split("/")
189
+ return this.updateOutput(() => db.view(database, view, params))
190
+ }
191
+
192
+ async destroy() {
193
+ try {
194
+ return await this.nano().db.destroy(this.name)
195
+ } catch (err: any) {
196
+ // didn't exist, don't worry
197
+ if (err.statusCode === 404) {
198
+ return
199
+ } else {
200
+ throw { ...err, status: err.statusCode }
201
+ }
202
+ }
203
+ }
204
+
205
+ async compact() {
206
+ const db = await this.checkSetup()
207
+ return this.updateOutput(() => db.compact())
208
+ }
209
+
210
+ // All below functions are in-frequently called, just utilise PouchDB
211
+ // for them as it implements them better than we can
212
+ async dump(stream: WriteStream, opts?: { filter?: any }) {
213
+ const pouch = getPouchDB(this.name)
214
+ // @ts-ignore
215
+ return pouch.dump(stream, opts)
216
+ }
217
+
218
+ async load(stream: ReadStream) {
219
+ const pouch = getPouchDB(this.name)
220
+ // @ts-ignore
221
+ return pouch.load(stream)
222
+ }
223
+
224
+ async createIndex(opts: DatabaseCreateIndexOpts) {
225
+ const pouch = getPouchDB(this.name)
226
+ return pouch.createIndex(opts)
227
+ }
228
+
229
+ async deleteIndex(opts: DatabaseDeleteIndexOpts) {
230
+ const pouch = getPouchDB(this.name)
231
+ return pouch.deleteIndex(opts)
232
+ }
233
+
234
+ async getIndexes() {
235
+ const pouch = getPouchDB(this.name)
236
+ return pouch.getIndexes()
237
+ }
238
+ }
@@ -0,0 +1,77 @@
1
+ import env from "../../environment"
2
+
3
+ export const getCouchInfo = (connection?: string) => {
4
+ const urlInfo = getUrlInfo(connection)
5
+ let username
6
+ let password
7
+ if (urlInfo.auth?.username) {
8
+ // set from url
9
+ username = urlInfo.auth.username
10
+ } else if (env.COUCH_DB_USERNAME) {
11
+ // set from env
12
+ username = env.COUCH_DB_USERNAME
13
+ } else if (!env.isTest()) {
14
+ throw new Error("CouchDB username not set")
15
+ }
16
+ if (urlInfo.auth?.password) {
17
+ // set from url
18
+ password = urlInfo.auth.password
19
+ } else if (env.COUCH_DB_PASSWORD) {
20
+ // set from env
21
+ password = env.COUCH_DB_PASSWORD
22
+ } else if (!env.isTest()) {
23
+ throw new Error("CouchDB password not set")
24
+ }
25
+ const authCookie = Buffer.from(`${username}:${password}`).toString("base64")
26
+ return {
27
+ url: urlInfo.url!,
28
+ auth: {
29
+ username: username,
30
+ password: password,
31
+ },
32
+ cookie: `Basic ${authCookie}`,
33
+ }
34
+ }
35
+
36
+ export const getUrlInfo = (url = env.COUCH_DB_URL) => {
37
+ let cleanUrl, username, password, host
38
+ if (url) {
39
+ // Ensure the URL starts with a protocol
40
+ const protoRegex = /^https?:\/\//i
41
+ if (!protoRegex.test(url)) {
42
+ url = `http://${url}`
43
+ }
44
+
45
+ // Split into protocol and remainder
46
+ const split = url.split("://")
47
+ const protocol = split[0]
48
+ const rest = split.slice(1).join("://")
49
+
50
+ // Extract auth if specified
51
+ if (url.includes("@")) {
52
+ // Split into host and remainder
53
+ let parts = rest.split("@")
54
+ host = parts[parts.length - 1]
55
+ let auth = parts.slice(0, -1).join("@")
56
+
57
+ // Split auth into username and password
58
+ if (auth.includes(":")) {
59
+ const authParts = auth.split(":")
60
+ username = authParts[0]
61
+ password = authParts.slice(1).join(":")
62
+ } else {
63
+ username = auth
64
+ }
65
+ } else {
66
+ host = rest
67
+ }
68
+ cleanUrl = `${protocol}://${host}`
69
+ }
70
+ return {
71
+ url: cleanUrl,
72
+ auth: {
73
+ username,
74
+ password,
75
+ },
76
+ }
77
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./connections"
2
+ export * from "./DatabaseImpl"
3
+ export * from "./utils"
4
+ export { init, getPouch, getPouchDB, closePouchDB } from "./pouchDB"
5
+ export * from "../constants"
@@ -0,0 +1,97 @@
1
+ import PouchDB from "pouchdb"
2
+ import env from "../../environment"
3
+ import { PouchOptions } from "@budibase/types"
4
+ import { getCouchInfo } from "./connections"
5
+
6
+ let Pouch: any
7
+ let initialised = false
8
+
9
+ /**
10
+ * Return a constructor for PouchDB.
11
+ * This should be rarely used outside of the main application config.
12
+ * Exposed for exceptional cases such as in-memory views.
13
+ */
14
+ export const getPouch = (opts: PouchOptions = {}) => {
15
+ let { url, cookie } = getCouchInfo()
16
+ let POUCH_DB_DEFAULTS = {
17
+ prefix: url,
18
+ fetch: (url: string, opts: any) => {
19
+ // use a specific authorization cookie - be very explicit about how we authenticate
20
+ opts.headers.set("Authorization", cookie)
21
+ return PouchDB.fetch(url, opts)
22
+ },
23
+ }
24
+
25
+ if (opts.inMemory) {
26
+ const inMemory = require("pouchdb-adapter-memory")
27
+ PouchDB.plugin(inMemory)
28
+ POUCH_DB_DEFAULTS = {
29
+ // @ts-ignore
30
+ adapter: "memory",
31
+ }
32
+ }
33
+
34
+ if (opts.onDisk) {
35
+ POUCH_DB_DEFAULTS = {
36
+ // @ts-ignore
37
+ adapter: "leveldb",
38
+ }
39
+ }
40
+
41
+ if (opts.replication) {
42
+ const replicationStream = require("@budibase/pouchdb-replication-stream")
43
+ PouchDB.plugin(replicationStream.plugin)
44
+ // @ts-ignore
45
+ PouchDB.adapter("writableStream", replicationStream.adapters.writableStream)
46
+ }
47
+
48
+ if (opts.find) {
49
+ const find = require("pouchdb-find")
50
+ PouchDB.plugin(find)
51
+ }
52
+
53
+ return PouchDB.defaults(POUCH_DB_DEFAULTS)
54
+ }
55
+
56
+ export function init(opts?: PouchOptions) {
57
+ Pouch = getPouch(opts)
58
+ initialised = true
59
+ }
60
+
61
+ const checkInitialised = () => {
62
+ if (!initialised) {
63
+ throw new Error("init has not been called")
64
+ }
65
+ }
66
+
67
+ export function getPouchDB(dbName: string, opts?: any): PouchDB.Database {
68
+ checkInitialised()
69
+ const db = new Pouch(dbName, opts)
70
+ const dbPut = db.put
71
+ db.put = async (doc: any, options = {}) => {
72
+ if (!doc.createdAt) {
73
+ doc.createdAt = new Date().toISOString()
74
+ }
75
+ doc.updatedAt = new Date().toISOString()
76
+ return dbPut(doc, options)
77
+ }
78
+ db.exists = async () => {
79
+ const info = await db.info()
80
+ return !info.error
81
+ }
82
+ return db
83
+ }
84
+
85
+ // use this function if you have called getPouchDB - close
86
+ // the databases you've opened once finished
87
+ export async function closePouchDB(db: PouchDB.Database) {
88
+ if (!db || env.isTest()) {
89
+ return
90
+ }
91
+ try {
92
+ // specifically await so that if there is an error, it can be ignored
93
+ return await db.close()
94
+ } catch (err) {
95
+ // ignore error, already closed
96
+ }
97
+ }
File without changes
@@ -0,0 +1,50 @@
1
+ import { getCouchInfo } from "./connections"
2
+ import fetch from "node-fetch"
3
+ import { checkSlashesInUrl } from "../../helpers"
4
+
5
+ export async function directCouchCall(
6
+ path: string,
7
+ method: string = "GET",
8
+ body?: any
9
+ ) {
10
+ let { url, cookie } = getCouchInfo()
11
+ const couchUrl = `${url}/${path}`
12
+ return await directCouchUrlCall({ url: couchUrl, cookie, method, body })
13
+ }
14
+
15
+ export async function directCouchUrlCall({
16
+ url,
17
+ cookie,
18
+ method,
19
+ body,
20
+ }: {
21
+ url: string
22
+ cookie: string
23
+ method: string
24
+ body?: any
25
+ }) {
26
+ const params: any = {
27
+ method: method,
28
+ headers: {
29
+ Authorization: cookie,
30
+ },
31
+ }
32
+ if (body && method !== "GET") {
33
+ params.body = JSON.stringify(body)
34
+ params.headers["Content-Type"] = "application/json"
35
+ }
36
+ return await fetch(checkSlashesInUrl(encodeURI(url)), params)
37
+ }
38
+
39
+ export async function directCouchQuery(
40
+ path: string,
41
+ method: string = "GET",
42
+ body?: any
43
+ ) {
44
+ const response = await directCouchCall(path, method, body)
45
+ if (response.status < 300) {
46
+ return await response.json()
47
+ } else {
48
+ throw "Cannot connect to CouchDB instance"
49
+ }
50
+ }
package/src/db/db.ts ADDED
@@ -0,0 +1,43 @@
1
+ import env from "../environment"
2
+ import { directCouchQuery, DatabaseImpl } from "./couch"
3
+ import { CouchFindOptions, Database } from "@budibase/types"
4
+
5
+ const dbList = new Set()
6
+
7
+ export function getDB(dbName?: string, opts?: any): Database {
8
+ return new DatabaseImpl(dbName, opts)
9
+ }
10
+
11
+ // we have to use a callback for this so that we can close
12
+ // the DB when we're done, without this manual requests would
13
+ // need to close the database when done with it to avoid memory leaks
14
+ export async function doWithDB<T>(
15
+ dbName: string,
16
+ cb: (db: Database) => Promise<T>,
17
+ opts = {}
18
+ ) {
19
+ const db = getDB(dbName, opts)
20
+ // need this to be async so that we can correctly close DB after all
21
+ // async operations have been completed
22
+ return await cb(db)
23
+ }
24
+
25
+ export function allDbs() {
26
+ if (!env.isTest()) {
27
+ throw new Error("Cannot be used outside test environment.")
28
+ }
29
+ return [...dbList]
30
+ }
31
+
32
+ export async function directCouchAllDbs(queryString?: string) {
33
+ let couchPath = "/_all_dbs"
34
+ if (queryString) {
35
+ couchPath += `?${queryString}`
36
+ }
37
+ return await directCouchQuery(couchPath)
38
+ }
39
+
40
+ export async function directCouchFind(dbName: string, opts: CouchFindOptions) {
41
+ const json = await directCouchQuery(`${dbName}/_find`, "POST", opts)
42
+ return { rows: json.docs, bookmark: json.bookmark }
43
+ }
@@ -0,0 +1,14 @@
1
+ export function checkErrorCode(error: any, code: number) {
2
+ const stringCode = code.toString()
3
+ if (typeof error === "object") {
4
+ return error.status === code || error.message?.includes(stringCode)
5
+ } else if (typeof error === "number") {
6
+ return error === code
7
+ } else if (typeof error === "string") {
8
+ return error.includes(stringCode)
9
+ }
10
+ }
11
+
12
+ export function isDocumentConflictError(error: any) {
13
+ return checkErrorCode(error, 409)
14
+ }
@@ -0,0 +1,12 @@
1
+ export * from "./couch"
2
+ export * from "./db"
3
+ export * from "./utils"
4
+ export * from "./views"
5
+ export * from "../docIds/conversions"
6
+ export { default as Replication } from "./Replication"
7
+ // exports to support old export structure
8
+ export * from "../constants/db"
9
+ export { getGlobalDBName, baseGlobalDBName } from "../context"
10
+ export * from "./lucene"
11
+ export * as searchIndexes from "./searchIndexes"
12
+ export * from "./errors"