@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.
- package/dist/index.js +5 -4
- package/dist/index.js.map +2 -2
- package/dist/index.js.meta.json +1 -1
- package/dist/package.json +6 -6
- package/dist/src/cache/appMetadata.js +1 -1
- package/dist/src/cache/appMetadata.js.map +1 -1
- package/dist/src/constants/misc.d.ts +0 -2
- package/dist/src/constants/misc.js +0 -2
- package/dist/src/constants/misc.js.map +1 -1
- package/dist/src/environment.js +5 -4
- package/dist/src/environment.js.map +1 -1
- package/dist/src/logging/system.d.ts +1 -1
- package/dist/src/timers/timers.d.ts +1 -1
- package/package.json +6 -6
- package/src/accounts/accounts.ts +82 -0
- package/src/accounts/api.ts +59 -0
- package/src/accounts/index.ts +1 -0
- package/src/auth/auth.ts +208 -0
- package/src/auth/index.ts +1 -0
- package/src/auth/tests/auth.spec.ts +14 -0
- package/src/blacklist/blacklist.ts +54 -0
- package/src/blacklist/index.ts +1 -0
- package/src/blacklist/tests/blacklist.spec.ts +46 -0
- package/src/cache/appMetadata.ts +88 -0
- package/src/cache/base/index.ts +92 -0
- package/src/cache/generic.ts +30 -0
- package/src/cache/index.ts +5 -0
- package/src/cache/tests/writethrough.spec.ts +138 -0
- package/src/cache/user.ts +83 -0
- package/src/cache/writethrough.ts +133 -0
- package/src/configs/configs.ts +257 -0
- package/src/configs/index.ts +1 -0
- package/src/configs/tests/configs.spec.ts +184 -0
- package/src/constants/db.ts +63 -0
- package/src/constants/index.ts +2 -0
- package/src/constants/misc.ts +50 -0
- package/src/context/Context.ts +14 -0
- package/src/context/identity.ts +58 -0
- package/src/context/index.ts +3 -0
- package/src/context/mainContext.ts +310 -0
- package/src/context/tests/index.spec.ts +147 -0
- package/src/context/types.ts +11 -0
- package/src/db/Replication.ts +84 -0
- package/src/db/constants.ts +10 -0
- package/src/db/couch/DatabaseImpl.ts +238 -0
- package/src/db/couch/connections.ts +77 -0
- package/src/db/couch/index.ts +5 -0
- package/src/db/couch/pouchDB.ts +97 -0
- package/src/db/couch/pouchDump.ts +0 -0
- package/src/db/couch/utils.ts +50 -0
- package/src/db/db.ts +43 -0
- package/src/db/errors.ts +14 -0
- package/src/db/index.ts +12 -0
- package/src/db/lucene.ts +750 -0
- package/src/db/searchIndexes/index.ts +1 -0
- package/src/db/searchIndexes/searchIndexes.ts +62 -0
- package/src/db/tests/index.spec.js +25 -0
- package/src/db/tests/lucene.spec.ts +368 -0
- package/src/db/tests/pouch.spec.js +62 -0
- package/src/db/tests/utils.spec.ts +63 -0
- package/src/db/utils.ts +207 -0
- package/src/db/views.ts +241 -0
- package/src/docIds/conversions.ts +59 -0
- package/src/docIds/ids.ts +113 -0
- package/src/docIds/index.ts +2 -0
- package/src/docIds/newid.ts +5 -0
- package/src/docIds/params.ts +174 -0
- package/src/docUpdates/index.ts +29 -0
- package/src/environment.ts +201 -0
- package/src/errors/errors.ts +119 -0
- package/src/errors/index.ts +1 -0
- package/src/events/analytics.ts +6 -0
- package/src/events/asyncEvents/index.ts +2 -0
- package/src/events/asyncEvents/publisher.ts +12 -0
- package/src/events/asyncEvents/queue.ts +22 -0
- package/src/events/backfill.ts +183 -0
- package/src/events/documentId.ts +56 -0
- package/src/events/events.ts +40 -0
- package/src/events/identification.ts +310 -0
- package/src/events/index.ts +14 -0
- package/src/events/processors/AnalyticsProcessor.ts +64 -0
- package/src/events/processors/AuditLogsProcessor.ts +93 -0
- package/src/events/processors/LoggingProcessor.ts +37 -0
- package/src/events/processors/Processors.ts +52 -0
- package/src/events/processors/async/DocumentUpdateProcessor.ts +43 -0
- package/src/events/processors/index.ts +19 -0
- package/src/events/processors/posthog/PosthogProcessor.ts +118 -0
- package/src/events/processors/posthog/index.ts +2 -0
- package/src/events/processors/posthog/rateLimiting.ts +106 -0
- package/src/events/processors/posthog/tests/PosthogProcessor.spec.ts +168 -0
- package/src/events/processors/types.ts +1 -0
- package/src/events/publishers/account.ts +35 -0
- package/src/events/publishers/app.ts +155 -0
- package/src/events/publishers/auditLog.ts +26 -0
- package/src/events/publishers/auth.ts +73 -0
- package/src/events/publishers/automation.ts +110 -0
- package/src/events/publishers/backfill.ts +74 -0
- package/src/events/publishers/backup.ts +42 -0
- package/src/events/publishers/datasource.ts +48 -0
- package/src/events/publishers/email.ts +17 -0
- package/src/events/publishers/environmentVariable.ts +38 -0
- package/src/events/publishers/group.ts +99 -0
- package/src/events/publishers/index.ts +24 -0
- package/src/events/publishers/installation.ts +38 -0
- package/src/events/publishers/layout.ts +26 -0
- package/src/events/publishers/license.ts +84 -0
- package/src/events/publishers/org.ts +37 -0
- package/src/events/publishers/plugin.ts +47 -0
- package/src/events/publishers/query.ts +88 -0
- package/src/events/publishers/role.ts +62 -0
- package/src/events/publishers/rows.ts +29 -0
- package/src/events/publishers/screen.ts +36 -0
- package/src/events/publishers/serve.ts +43 -0
- package/src/events/publishers/table.ts +70 -0
- package/src/events/publishers/user.ts +202 -0
- package/src/events/publishers/view.ts +107 -0
- package/src/features/index.ts +78 -0
- package/src/features/installation.ts +17 -0
- package/src/features/tests/featureFlags.spec.ts +85 -0
- package/src/helpers.ts +9 -0
- package/src/index.ts +54 -0
- package/src/installation.ts +107 -0
- package/src/logging/alerts.ts +26 -0
- package/src/logging/correlation/correlation.ts +13 -0
- package/src/logging/correlation/index.ts +1 -0
- package/src/logging/correlation/middleware.ts +17 -0
- package/src/logging/index.ts +4 -0
- package/src/logging/pino/logger.ts +232 -0
- package/src/logging/pino/middleware.ts +45 -0
- package/src/logging/system.ts +81 -0
- package/src/logging/tests/system.spec.ts +61 -0
- package/src/middleware/adminOnly.ts +9 -0
- package/src/middleware/auditLog.ts +6 -0
- package/src/middleware/authenticated.ts +193 -0
- package/src/middleware/builderOnly.ts +21 -0
- package/src/middleware/builderOrAdmin.ts +21 -0
- package/src/middleware/csrf.ts +81 -0
- package/src/middleware/errorHandling.ts +29 -0
- package/src/middleware/index.ts +21 -0
- package/src/middleware/internalApi.ts +23 -0
- package/src/middleware/joi-validator.ts +45 -0
- package/src/middleware/matchers.ts +47 -0
- package/src/middleware/passport/datasource/google.ts +95 -0
- package/src/middleware/passport/local.ts +54 -0
- package/src/middleware/passport/sso/google.ts +77 -0
- package/src/middleware/passport/sso/oidc.ts +154 -0
- package/src/middleware/passport/sso/sso.ts +165 -0
- package/src/middleware/passport/sso/tests/google.spec.ts +67 -0
- package/src/middleware/passport/sso/tests/oidc.spec.ts +152 -0
- package/src/middleware/passport/sso/tests/sso.spec.ts +197 -0
- package/src/middleware/passport/utils.ts +38 -0
- package/src/middleware/querystringToBody.ts +28 -0
- package/src/middleware/tenancy.ts +36 -0
- package/src/middleware/tests/builder.spec.ts +180 -0
- package/src/middleware/tests/matchers.spec.ts +134 -0
- package/src/migrations/definitions.ts +40 -0
- package/src/migrations/index.ts +2 -0
- package/src/migrations/migrations.ts +191 -0
- package/src/migrations/tests/__snapshots__/migrations.spec.ts.snap +11 -0
- package/src/migrations/tests/migrations.spec.ts +64 -0
- package/src/objectStore/buckets/app.ts +40 -0
- package/src/objectStore/buckets/global.ts +29 -0
- package/src/objectStore/buckets/index.ts +3 -0
- package/src/objectStore/buckets/plugins.ts +71 -0
- package/src/objectStore/buckets/tests/app.spec.ts +171 -0
- package/src/objectStore/buckets/tests/global.spec.ts +74 -0
- package/src/objectStore/buckets/tests/plugins.spec.ts +111 -0
- package/src/objectStore/cloudfront.ts +41 -0
- package/src/objectStore/index.ts +3 -0
- package/src/objectStore/objectStore.ts +440 -0
- package/src/objectStore/utils.ts +27 -0
- package/src/platform/index.ts +3 -0
- package/src/platform/platformDb.ts +6 -0
- package/src/platform/tenants.ts +101 -0
- package/src/platform/tests/tenants.spec.ts +26 -0
- package/src/platform/users.ts +90 -0
- package/src/plugin/index.ts +1 -0
- package/src/plugin/tests/validation.spec.ts +83 -0
- package/src/plugin/utils.ts +156 -0
- package/src/queue/constants.ts +6 -0
- package/src/queue/inMemoryQueue.ts +141 -0
- package/src/queue/index.ts +2 -0
- package/src/queue/listeners.ts +195 -0
- package/src/queue/queue.ts +54 -0
- package/src/redis/index.ts +6 -0
- package/src/redis/init.ts +86 -0
- package/src/redis/redis.ts +308 -0
- package/src/redis/redlockImpl.ts +139 -0
- package/src/redis/utils.ts +117 -0
- package/src/security/encryption.ts +179 -0
- package/src/security/permissions.ts +158 -0
- package/src/security/roles.ts +389 -0
- package/src/security/sessions.ts +120 -0
- package/src/security/tests/encryption.spec.ts +31 -0
- package/src/security/tests/permissions.spec.ts +145 -0
- package/src/security/tests/sessions.spec.ts +12 -0
- package/src/tenancy/db.ts +6 -0
- package/src/tenancy/index.ts +2 -0
- package/src/tenancy/tenancy.ts +140 -0
- package/src/tenancy/tests/tenancy.spec.ts +184 -0
- package/src/timers/index.ts +1 -0
- package/src/timers/timers.ts +22 -0
- package/src/users/db.ts +484 -0
- package/src/users/events.ts +176 -0
- package/src/users/index.ts +4 -0
- package/src/users/lookup.ts +102 -0
- package/src/users/users.ts +276 -0
- package/src/users/utils.ts +55 -0
- package/src/utils/hashing.ts +14 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/stringUtils.ts +8 -0
- package/src/utils/tests/utils.spec.ts +191 -0
- package/src/utils/utils.ts +239 -0
- package/tests/core/logging.ts +34 -0
- package/tests/core/utilities/index.ts +6 -0
- package/tests/core/utilities/jestUtils.ts +30 -0
- package/tests/core/utilities/mocks/alerts.ts +3 -0
- package/tests/core/utilities/mocks/date.ts +2 -0
- package/tests/core/utilities/mocks/events.ts +131 -0
- package/tests/core/utilities/mocks/fetch.ts +17 -0
- package/tests/core/utilities/mocks/index.ts +10 -0
- package/tests/core/utilities/mocks/licenses.ts +115 -0
- package/tests/core/utilities/mocks/posthog.ts +7 -0
- package/tests/core/utilities/structures/Chance.ts +20 -0
- package/tests/core/utilities/structures/accounts.ts +115 -0
- package/tests/core/utilities/structures/apps.ts +21 -0
- package/tests/core/utilities/structures/common.ts +7 -0
- package/tests/core/utilities/structures/db.ts +12 -0
- package/tests/core/utilities/structures/documents/index.ts +1 -0
- package/tests/core/utilities/structures/documents/platform/index.ts +1 -0
- package/tests/core/utilities/structures/documents/platform/installation.ts +12 -0
- package/tests/core/utilities/structures/generator.ts +2 -0
- package/tests/core/utilities/structures/index.ts +15 -0
- package/tests/core/utilities/structures/koa.ts +16 -0
- package/tests/core/utilities/structures/licenses.ts +167 -0
- package/tests/core/utilities/structures/plugins.ts +19 -0
- package/tests/core/utilities/structures/quotas.ts +67 -0
- package/tests/core/utilities/structures/scim.ts +80 -0
- package/tests/core/utilities/structures/shared.ts +19 -0
- package/tests/core/utilities/structures/sso.ts +119 -0
- package/tests/core/utilities/structures/tenants.ts +5 -0
- package/tests/core/utilities/structures/userGroups.ts +10 -0
- package/tests/core/utilities/structures/users.ts +73 -0
- package/tests/core/utilities/testContainerUtils.ts +85 -0
- package/tests/core/utilities/utils/index.ts +1 -0
- package/tests/core/utilities/utils/time.ts +3 -0
- package/tests/extra/DBTestConfiguration.ts +36 -0
- package/tests/extra/index.ts +2 -0
- package/tests/extra/testEnv.ts +95 -0
- package/tests/index.ts +1 -0
- package/tests/jestEnv.ts +6 -0
- 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,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
|
+
}
|
package/src/db/errors.ts
ADDED
|
@@ -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
|
+
}
|
package/src/db/index.ts
ADDED
|
@@ -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"
|