backd-js 0.1.15 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -201
- package/README.md +32 -26
- package/package.json +47 -40
- package/src/admin.js +597 -0
- package/src/auth.js +374 -0
- package/src/client.js +214 -0
- package/src/data.js +293 -0
- package/src/errors.js +135 -0
- package/src/functions.js +143 -0
- package/src/index.js +51 -0
- package/src/storage.js +43 -0
- package/types/admin.d.ts +709 -0
- package/types/auth.d.ts +281 -0
- package/types/client.d.ts +132 -0
- package/types/data.d.ts +277 -0
- package/types/errors.d.ts +106 -0
- package/types/functions.d.ts +135 -0
- package/types/index.d.ts +59 -0
- package/types/storage.d.ts +26 -0
- package/.babelrc +0 -4
- package/.editorconfig +0 -12
- package/.eslintrc.js +0 -28
- package/.npmignore +0 -9
- package/.nvmrc +0 -1
- package/.travis.yml +0 -33
- package/lib/backd.js +0 -13625
- package/lib/backd.js.map +0 -1
- package/lib/backd.min.js +0 -7
package/src/admin.js
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
import { Job } from './functions.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {import('./client.js').Client} Client
|
|
5
|
+
* @typedef {import('./client.js').RequestOptions} RequestOptions
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A user as the admin API shows it.
|
|
10
|
+
* @typedef {object} AdminUser
|
|
11
|
+
* @property {string} id
|
|
12
|
+
* @property {string} email
|
|
13
|
+
* @property {boolean} email_verified
|
|
14
|
+
* @property {string} locale
|
|
15
|
+
* @property {string[]} roles
|
|
16
|
+
* @property {boolean} disabled
|
|
17
|
+
* @property {string[]} admin_networks CIDR networks the user's admin requests must come from; empty: no own restriction.
|
|
18
|
+
* @property {string[]} login_networks CIDR networks the user's login and session must be used from; empty: anywhere.
|
|
19
|
+
* @property {string} created_at
|
|
20
|
+
* @property {string} updated_at
|
|
21
|
+
* @property {string | null} erased_at When the user was erased; a tombstone keeps only its id (and a placeholder email).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} UserPage
|
|
26
|
+
* @property {AdminUser[]} items
|
|
27
|
+
* @property {number} limit
|
|
28
|
+
* @property {number} skip
|
|
29
|
+
* @property {boolean} has_more
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {object} Invitation
|
|
34
|
+
* @property {string} id
|
|
35
|
+
* @property {string | null} email Only this email may use it; null for anyone.
|
|
36
|
+
* @property {string} created_by
|
|
37
|
+
* @property {string} created_at
|
|
38
|
+
* @property {string} expires_at
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* What erasing a user would do, for the collections that declare a policy
|
|
43
|
+
* (`collection.yaml`); the others are only named in `without_policy`.
|
|
44
|
+
* @typedef {object} OwnedReport
|
|
45
|
+
* @property {{ id: string, status: 'active' | 'deactivated' | 'erased' }} user
|
|
46
|
+
* @property {Array<{ database: string, collection: string, action: 'delete' | 'anonymize' | null, owned: number, remove?: string[], replace?: string[], pull?: Record<string, number>, unset?: Record<string, number> }>} collections
|
|
47
|
+
* `owned` counts the documents the user owns; `pull` and `unset` count, per field, the documents that hold the user.
|
|
48
|
+
* @property {string[]} without_policy `<database>.<collection>` of the collections an erase leaves alone.
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @typedef {Invitation & { token: string }} NewInvitation
|
|
53
|
+
* `token` (`bdi_…`) is returned only when the invitation is created.
|
|
54
|
+
*/
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @typedef {Invitation & { sent: true }} SentInvitation
|
|
58
|
+
* An invitation that was emailed: nobody holds its token.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* An API key as the admin API lists it (never the key itself).
|
|
63
|
+
* @typedef {object} APIKeyInfo
|
|
64
|
+
* @property {string} name
|
|
65
|
+
* @property {'data' | 'admin'} role
|
|
66
|
+
* @property {string} prefix First characters of the key.
|
|
67
|
+
* @property {string[]} networks Where it may be used from; empty: anywhere.
|
|
68
|
+
* @property {string} created_at
|
|
69
|
+
* @property {string | null} last_used_at
|
|
70
|
+
* @property {string | null} expires_at
|
|
71
|
+
*/
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @typedef {APIKeyInfo & { key: string }} NewAPIKey
|
|
75
|
+
* `key` (`bdk_…`) is returned only when the key is created.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* One entry of the realm's audit trail. Never holds secrets or emails.
|
|
80
|
+
* @typedef {object} AuditRecord
|
|
81
|
+
* @property {string} id
|
|
82
|
+
* @property {string} at
|
|
83
|
+
* @property {string} action Such as `user.create`, `role.add`, `apikey.create`, `admin.login`.
|
|
84
|
+
* @property {string} actor `user:<id>`, `key:<name>`, `anonymous`, `config:realm.yaml` or `cli:bootstrap`.
|
|
85
|
+
* @property {string | null} target `user:<id>`, `key:<name>`, `invitation:<id>`, or null.
|
|
86
|
+
* @property {Record<string, unknown>} details
|
|
87
|
+
* @property {string | null} request_id
|
|
88
|
+
* @property {string | null} client_ip
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* A function secret's metadata, as listed: never its value.
|
|
93
|
+
* @typedef {object} SecretInfo
|
|
94
|
+
* @property {string} database Its database scope; empty means the realm scope.
|
|
95
|
+
* @property {string} name
|
|
96
|
+
* @property {string} created_at
|
|
97
|
+
* @property {string} updated_at
|
|
98
|
+
* @property {string} updated_by Who last set it, such as `user:<id>` or `key:<name>`.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* One function call in the invocation history: what happened and the
|
|
103
|
+
* function's own console lines, never its input or output.
|
|
104
|
+
* @typedef {object} InvocationRecord
|
|
105
|
+
* @property {string} id
|
|
106
|
+
* @property {string} at
|
|
107
|
+
* @property {string} function `<database>/<name>`.
|
|
108
|
+
* @property {string} actor `user:<id>`, `key:<name>`, `anonymous`, ...
|
|
109
|
+
* @property {string} mode `sync` or `async`.
|
|
110
|
+
* @property {string} status `ok`, `function_error`, `timeout`, `memory`, `cpu`, `crash`, `output_too_large`, `busy` or `bundle`.
|
|
111
|
+
* @property {string | null} code The function's own error code.
|
|
112
|
+
* @property {number} duration_ms
|
|
113
|
+
* @property {string | null} request_id
|
|
114
|
+
* @property {string | null} job_id The async job this call ran for.
|
|
115
|
+
* @property {string | null} parent_id The invocation that called this one with `ctx.call`.
|
|
116
|
+
* @property {string | null} origin `http`, `function`, `cron`, `admin` or `backd:<event>`.
|
|
117
|
+
* @property {{ level: string, line: string }[]} logs
|
|
118
|
+
*/
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @typedef {object} InvocationsPage
|
|
122
|
+
* @property {InvocationRecord[]} items Newest first.
|
|
123
|
+
* @property {number} limit
|
|
124
|
+
* @property {number} skip
|
|
125
|
+
* @property {boolean} has_more
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @typedef {object} AuditPage
|
|
130
|
+
* @property {AuditRecord[]} items Newest first.
|
|
131
|
+
* @property {number} limit
|
|
132
|
+
* @property {number} skip
|
|
133
|
+
* @property {boolean} has_more
|
|
134
|
+
*/
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* An async or scheduled job as the admin listing shows it: its state and
|
|
138
|
+
* how it ended, never its input or output.
|
|
139
|
+
* @typedef {object} JobSummary
|
|
140
|
+
* @property {string} id For cron runs, `cron_<database>_<function>_<yyyymmddhhmm>` (UTC).
|
|
141
|
+
* @property {string} function `<database>/<name>`.
|
|
142
|
+
* @property {'queued' | 'running' | 'done'} status
|
|
143
|
+
* @property {boolean} scheduled True for a cron run.
|
|
144
|
+
* @property {number} attempts More than 1 after a worker was lost mid-run.
|
|
145
|
+
* @property {string} created_at
|
|
146
|
+
* @property {string | null} completed_at
|
|
147
|
+
* @property {{ status: string, code: string | null, duration_ms: number } | null} result Null until `done`.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* @typedef {object} JobsPage
|
|
152
|
+
* @property {JobSummary[]} items Newest first.
|
|
153
|
+
* @property {number} limit
|
|
154
|
+
* @property {number} skip
|
|
155
|
+
* @property {boolean} has_more
|
|
156
|
+
*/
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The admin API: `client.admin`. Needs a client created with an admin API
|
|
160
|
+
* key, or signed in as a user holding one of the realm's admin roles.
|
|
161
|
+
*/
|
|
162
|
+
export class Admin {
|
|
163
|
+
/** @param {Client} client */
|
|
164
|
+
constructor(client) {
|
|
165
|
+
/** @internal */
|
|
166
|
+
this.client = client
|
|
167
|
+
/** Users and their roles. */
|
|
168
|
+
this.users = new AdminUsers(this)
|
|
169
|
+
/** Invitations for realms with `signup: invite`. */
|
|
170
|
+
this.invitations = new AdminInvitations(this)
|
|
171
|
+
/** The realm's API keys. */
|
|
172
|
+
this.apiKeys = new AdminAPIKeys(this)
|
|
173
|
+
/** The realm's audit trail (read-only). */
|
|
174
|
+
this.audit = new AdminAudit(this)
|
|
175
|
+
/** The realm's async and scheduled jobs (read-only). */
|
|
176
|
+
this.jobs = new AdminJobs(this)
|
|
177
|
+
/** Function secrets: set and delete their values, list their metadata. */
|
|
178
|
+
this.secrets = new AdminSecrets(this)
|
|
179
|
+
/** The realm's function invocation history (read-only). */
|
|
180
|
+
this.invocations = new AdminInvocations(this)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Runs a function by hand, internal ones included: to re-run a clean-up
|
|
185
|
+
* that failed, or to test a scheduled function. `function` is
|
|
186
|
+
* `<database>/<name>`. With `as` (a user's email) the function runs with
|
|
187
|
+
* that user as `ctx.user`; without it there is no user. Returns the
|
|
188
|
+
* output of a `sync` function, and a `Job` handle for an `async` one,
|
|
189
|
+
* like `db.fn()`. The function's `invoke` rule and `rate_limit` don't
|
|
190
|
+
* apply; every run is audited.
|
|
191
|
+
* @param {string} fn
|
|
192
|
+
* @param {{ input?: unknown, as?: string, idempotencyKey?: string }} [params]
|
|
193
|
+
* @param {RequestOptions} [opts]
|
|
194
|
+
* @returns {Promise<unknown | Job>}
|
|
195
|
+
*/
|
|
196
|
+
async invokeFunction(fn, { input, as, idempotencyKey } = {}, opts) {
|
|
197
|
+
const [database, name, ...rest] = fn.split('/')
|
|
198
|
+
if (!database || !name || rest.length > 0) throw new TypeError('invokeFunction: the function must be "<database>/<name>"')
|
|
199
|
+
/** @type {Record<string, unknown>} */
|
|
200
|
+
const body = { input: input === undefined ? null : input }
|
|
201
|
+
if (as !== undefined) body.as = as
|
|
202
|
+
const headers = idempotencyKey === undefined ? undefined : { 'Idempotency-Key': idempotencyKey }
|
|
203
|
+
const { status, data } = await this._request({ method: 'POST', path: ['functions', database, name, 'invoke'], body, headers, ...opts })
|
|
204
|
+
if (status === 202) return new Job(this.client, database, /** @type {import('./functions.js').JobData} */ (data))
|
|
205
|
+
return data
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @internal
|
|
210
|
+
* @param {import('./client.js').RequestInit} req
|
|
211
|
+
*/
|
|
212
|
+
async _request(req) {
|
|
213
|
+
if (!this.client.hasApiKey && !(await this.client.auth.token())) {
|
|
214
|
+
throw new Error('client.admin needs a client created with an `apiKey` (admin role), or a signed-in user with an admin role')
|
|
215
|
+
}
|
|
216
|
+
return this.client.request({ ...req, path: ['_admin', ...req.path] })
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
class AdminUsers {
|
|
221
|
+
/** @param {Admin} admin */
|
|
222
|
+
constructor(admin) {
|
|
223
|
+
/** @internal */
|
|
224
|
+
this.admin = admin
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A page of users, sorted by email.
|
|
229
|
+
* @param {{ limit?: number, skip?: number }} [params]
|
|
230
|
+
* @param {RequestOptions} [opts]
|
|
231
|
+
* @returns {Promise<UserPage>}
|
|
232
|
+
*/
|
|
233
|
+
async list(params = {}, opts) {
|
|
234
|
+
return (await this.admin._request({ method: 'GET', path: ['users'], query: { limit: params.limit, skip: params.skip }, ...opts })).data
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The user with this email (case-insensitive), or null.
|
|
239
|
+
* @param {string} email
|
|
240
|
+
* @param {RequestOptions} [opts]
|
|
241
|
+
* @returns {Promise<AdminUser | null>}
|
|
242
|
+
*/
|
|
243
|
+
async find(email, opts) {
|
|
244
|
+
const { data } = await this.admin._request({ method: 'GET', path: ['users'], query: { email }, ...opts })
|
|
245
|
+
return data.items[0] ?? null
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param {string} id
|
|
250
|
+
* @param {RequestOptions} [opts]
|
|
251
|
+
* @returns {Promise<AdminUser>}
|
|
252
|
+
*/
|
|
253
|
+
async get(id, opts) {
|
|
254
|
+
return (await this.admin._request({ method: 'GET', path: ['users', id], ...opts })).data
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Creates a user. Without a password they can't sign in until one is set.
|
|
259
|
+
* @param {{ email: string, password?: string }} input
|
|
260
|
+
* @param {RequestOptions} [opts]
|
|
261
|
+
* @returns {Promise<AdminUser>}
|
|
262
|
+
*/
|
|
263
|
+
async create({ email, password }, opts) {
|
|
264
|
+
/** @type {Record<string, string>} */
|
|
265
|
+
const body = { email }
|
|
266
|
+
if (password !== undefined) body.password = password
|
|
267
|
+
return (await this.admin._request({ method: 'POST', path: ['users'], body, ...opts })).data
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Changes a user's flags. Disabling ends all their sessions. Emails can't change.
|
|
272
|
+
* @param {string} id
|
|
273
|
+
* @param {{ emailVerified?: boolean, disabled?: boolean }} changes
|
|
274
|
+
* @param {RequestOptions} [opts]
|
|
275
|
+
* @returns {Promise<AdminUser>}
|
|
276
|
+
*/
|
|
277
|
+
async update(id, { emailVerified, disabled }, opts) {
|
|
278
|
+
/** @type {Record<string, boolean>} */
|
|
279
|
+
const body = {}
|
|
280
|
+
if (emailVerified !== undefined) body.email_verified = emailVerified
|
|
281
|
+
if (disabled !== undefined) body.disabled = disabled
|
|
282
|
+
return (await this.admin._request({ method: 'PATCH', path: ['users', id], body, ...opts })).data
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Erases a user. **Irreversible.** The user becomes a tombstone at once (the
|
|
287
|
+
* id stays, the email becomes `erased-<id>@erased.invalid`, and their
|
|
288
|
+
* sessions, sign-in methods and email tokens are deleted); a worker then
|
|
289
|
+
* applies the `collection.yaml` policy of every collection that declares one.
|
|
290
|
+
* Resolves with the erase job (`origin: backd:account.erase` in
|
|
291
|
+
* `admin.jobs.list()`); the counts land in the audit trail as `user.erased`.
|
|
292
|
+
* To keep the data, deactivate with `update(id, { disabled: true })`.
|
|
293
|
+
* @param {string} id
|
|
294
|
+
* @param {RequestOptions} [opts]
|
|
295
|
+
* @returns {Promise<{ id: string, status: 'queued' | 'running' | 'done' }>}
|
|
296
|
+
*/
|
|
297
|
+
async delete(id, opts) {
|
|
298
|
+
return (await this.admin._request({ method: 'DELETE', path: ['users', id], ...opts })).data
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Sets a user's password and ends all their sessions.
|
|
303
|
+
* @param {string} id
|
|
304
|
+
* @param {string} password
|
|
305
|
+
* @param {RequestOptions} [opts]
|
|
306
|
+
* @returns {Promise<void>}
|
|
307
|
+
*/
|
|
308
|
+
async setPassword(id, password, opts) {
|
|
309
|
+
await this.admin._request({ method: 'POST', path: ['users', id, 'password'], body: { password }, ...opts })
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* What erasing a user would do: counts per collection that declares a policy,
|
|
314
|
+
* and the collections an erase leaves alone. No document content.
|
|
315
|
+
* @param {string} id
|
|
316
|
+
* @param {RequestOptions} [opts]
|
|
317
|
+
* @returns {Promise<OwnedReport>}
|
|
318
|
+
*/
|
|
319
|
+
async owned(id, opts) {
|
|
320
|
+
return (await this.admin._request({ method: 'GET', path: ['users', id, 'owned'], ...opts })).data
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Changes a user's email address at once, in a realm with `email`: the new
|
|
325
|
+
* address counts as verified, the user's sessions end, the old address is
|
|
326
|
+
* sent a link to undo the change and the new one is told.
|
|
327
|
+
* @param {string} id
|
|
328
|
+
* @param {string} email
|
|
329
|
+
* @param {RequestOptions} [opts]
|
|
330
|
+
* @returns {Promise<AdminUser>}
|
|
331
|
+
*/
|
|
332
|
+
async changeEmail(id, email, opts) {
|
|
333
|
+
return (await this.admin._request({ method: 'POST', path: ['users', id, 'email'], body: { email }, ...opts })).data
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Assigns a role declared in realm.yaml.
|
|
338
|
+
* @param {string} id
|
|
339
|
+
* @param {string} role
|
|
340
|
+
* @param {RequestOptions} [opts]
|
|
341
|
+
* @returns {Promise<AdminUser>}
|
|
342
|
+
*/
|
|
343
|
+
async addRole(id, role, opts) {
|
|
344
|
+
return (await this.admin._request({ method: 'PUT', path: ['users', id, 'roles', role], ...opts })).data
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Takes a role away.
|
|
349
|
+
* @param {string} id
|
|
350
|
+
* @param {string} role
|
|
351
|
+
* @param {RequestOptions} [opts]
|
|
352
|
+
* @returns {Promise<AdminUser>}
|
|
353
|
+
*/
|
|
354
|
+
async removeRole(id, role, opts) {
|
|
355
|
+
return (await this.admin._request({ method: 'DELETE', path: ['users', id, 'roles', role], ...opts })).data
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Replaces the user's network restrictions (IP addresses or CIDR
|
|
360
|
+
* networks); empty lists remove them. Settings in realm.yaml win at the
|
|
361
|
+
* next startup.
|
|
362
|
+
* @param {string} id
|
|
363
|
+
* @param {{ adminNetworks?: string[], loginNetworks?: string[] }} networks
|
|
364
|
+
* @param {RequestOptions} [opts]
|
|
365
|
+
* @returns {Promise<AdminUser>}
|
|
366
|
+
*/
|
|
367
|
+
async setNetworks(id, { adminNetworks = [], loginNetworks = [] }, opts) {
|
|
368
|
+
const body = { admin_networks: adminNetworks, login_networks: loginNetworks }
|
|
369
|
+
return (await this.admin._request({ method: 'PUT', path: ['users', id, 'networks'], body, ...opts })).data
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
class AdminAPIKeys {
|
|
374
|
+
/** @param {Admin} admin */
|
|
375
|
+
constructor(admin) {
|
|
376
|
+
/** @internal */
|
|
377
|
+
this.admin = admin
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The realm's API keys, by name; never the keys themselves.
|
|
382
|
+
* @param {RequestOptions} [opts]
|
|
383
|
+
* @returns {Promise<APIKeyInfo[]>}
|
|
384
|
+
*/
|
|
385
|
+
async list(opts) {
|
|
386
|
+
return (await this.admin._request({ method: 'GET', path: ['apikeys'], ...opts })).data.items
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Creates a key; store its `key` now, it is never shown again.
|
|
391
|
+
* @param {{ name: string, role?: 'data' | 'admin', expiresIn?: string, networks?: string[] }} input
|
|
392
|
+
* `role` defaults to data; `expiresIn`: days (`90d`) or Go durations (`12h`).
|
|
393
|
+
* @param {RequestOptions} [opts]
|
|
394
|
+
* @returns {Promise<NewAPIKey>}
|
|
395
|
+
*/
|
|
396
|
+
async create({ name, role, expiresIn, networks }, opts) {
|
|
397
|
+
/** @type {Record<string, unknown>} */
|
|
398
|
+
const body = { name }
|
|
399
|
+
if (role !== undefined) body.role = role
|
|
400
|
+
if (expiresIn !== undefined) body.expires_in = expiresIn
|
|
401
|
+
if (networks !== undefined) body.networks = networks
|
|
402
|
+
return (await this.admin._request({ method: 'POST', path: ['apikeys'], body, ...opts })).data
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Revokes a key: it stops working at once.
|
|
407
|
+
* @param {string} name
|
|
408
|
+
* @param {RequestOptions} [opts]
|
|
409
|
+
* @returns {Promise<void>}
|
|
410
|
+
*/
|
|
411
|
+
async revoke(name, opts) {
|
|
412
|
+
await this.admin._request({ method: 'DELETE', path: ['apikeys', name], ...opts })
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
class AdminAudit {
|
|
417
|
+
/** @param {Admin} admin */
|
|
418
|
+
constructor(admin) {
|
|
419
|
+
/** @internal */
|
|
420
|
+
this.admin = admin
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* A page of the audit trail, newest first. `since` and `until` are dates
|
|
425
|
+
* or RFC 3339 strings.
|
|
426
|
+
* @param {{ action?: string, actor?: string, target?: string, since?: Date | string, until?: Date | string, limit?: number, skip?: number }} [params]
|
|
427
|
+
* @param {RequestOptions} [opts]
|
|
428
|
+
* @returns {Promise<AuditPage>}
|
|
429
|
+
*/
|
|
430
|
+
async list(params = {}, opts) {
|
|
431
|
+
const time = (/** @type {Date | string | undefined} */ t) => (t instanceof Date ? t.toISOString() : t)
|
|
432
|
+
const query = {
|
|
433
|
+
action: params.action, actor: params.actor, target: params.target,
|
|
434
|
+
since: time(params.since), until: time(params.until), limit: params.limit, skip: params.skip,
|
|
435
|
+
}
|
|
436
|
+
return (await this.admin._request({ method: 'GET', path: ['audit'], query, ...opts })).data
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
class AdminSecrets {
|
|
441
|
+
/** @param {Admin} admin */
|
|
442
|
+
constructor(admin) {
|
|
443
|
+
/** @internal */
|
|
444
|
+
this.admin = admin
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* The realm's secrets: scope, name and who last changed them. Values are
|
|
449
|
+
* write-only and never returned.
|
|
450
|
+
* @param {RequestOptions} [opts]
|
|
451
|
+
* @returns {Promise<SecretInfo[]>}
|
|
452
|
+
*/
|
|
453
|
+
async list(opts) {
|
|
454
|
+
return (await this.admin._request({ method: 'GET', path: ['secrets'], ...opts })).data.items
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Creates a secret or replaces its value; functions read it as
|
|
459
|
+
* `ctx.secrets` within about a minute. Without `database` it is the
|
|
460
|
+
* realm's secret (`realm.NAME` in function.yaml); with it, that
|
|
461
|
+
* database's (`NAME`).
|
|
462
|
+
* @param {string} name Upper-case letters, digits and `_`.
|
|
463
|
+
* @param {string} value
|
|
464
|
+
* @param {{ database?: string }} [scope]
|
|
465
|
+
* @param {RequestOptions} [opts]
|
|
466
|
+
* @returns {Promise<void>}
|
|
467
|
+
*/
|
|
468
|
+
async set(name, value, { database } = {}, opts) {
|
|
469
|
+
/** @type {Record<string, string>} */
|
|
470
|
+
const body = { value }
|
|
471
|
+
if (database) body.database = database
|
|
472
|
+
await this.admin._request({ method: 'PUT', path: ['secrets', name], body, ...opts })
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Removes a secret's value: a function that declares it answers
|
|
477
|
+
* `secret_missing` again.
|
|
478
|
+
* @param {string} name
|
|
479
|
+
* @param {{ database?: string }} [scope]
|
|
480
|
+
* @param {RequestOptions} [opts]
|
|
481
|
+
* @returns {Promise<void>}
|
|
482
|
+
*/
|
|
483
|
+
async delete(name, { database } = {}, opts) {
|
|
484
|
+
await this.admin._request({ method: 'DELETE', path: ['secrets', name], query: { database }, ...opts })
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
class AdminInvocations {
|
|
489
|
+
/** @param {Admin} admin */
|
|
490
|
+
constructor(admin) {
|
|
491
|
+
/** @internal */
|
|
492
|
+
this.admin = admin
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* A page of the function invocation history, newest first. `function` is
|
|
497
|
+
* `<database>/<name>`; `since` and `until` are dates or RFC 3339 strings.
|
|
498
|
+
* @param {{ function?: string, requestId?: string, since?: Date | string, until?: Date | string, limit?: number, skip?: number }} [params]
|
|
499
|
+
* @param {RequestOptions} [opts]
|
|
500
|
+
* @returns {Promise<InvocationsPage>}
|
|
501
|
+
*/
|
|
502
|
+
async list(params = {}, opts) {
|
|
503
|
+
const time = (/** @type {Date | string | undefined} */ t) => (t instanceof Date ? t.toISOString() : t)
|
|
504
|
+
const query = {
|
|
505
|
+
function: params.function, request_id: params.requestId,
|
|
506
|
+
since: time(params.since), until: time(params.until), limit: params.limit, skip: params.skip,
|
|
507
|
+
}
|
|
508
|
+
return (await this.admin._request({ method: 'GET', path: ['invocations'], query, ...opts })).data
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
class AdminJobs {
|
|
513
|
+
/** @param {Admin} admin */
|
|
514
|
+
constructor(admin) {
|
|
515
|
+
/** @internal */
|
|
516
|
+
this.admin = admin
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* A page of jobs, newest first: their state and outcome, never their
|
|
521
|
+
* input or output (read one job in full with `Job.status()`/`wait()`).
|
|
522
|
+
* `function` is `<database>/<name>`; `since` and `until` are dates or
|
|
523
|
+
* RFC 3339 strings, on the job's creation time.
|
|
524
|
+
* @param {{ function?: string, status?: 'queued' | 'running' | 'done', scheduled?: boolean, since?: Date | string, until?: Date | string, limit?: number, skip?: number }} [params]
|
|
525
|
+
* @param {RequestOptions} [opts]
|
|
526
|
+
* @returns {Promise<JobsPage>}
|
|
527
|
+
*/
|
|
528
|
+
async list(params = {}, opts) {
|
|
529
|
+
const time = (/** @type {Date | string | undefined} */ t) => (t instanceof Date ? t.toISOString() : t)
|
|
530
|
+
const query = {
|
|
531
|
+
function: params.function, status: params.status, scheduled: params.scheduled,
|
|
532
|
+
since: time(params.since), until: time(params.until), limit: params.limit, skip: params.skip,
|
|
533
|
+
}
|
|
534
|
+
return (await this.admin._request({ method: 'GET', path: ['jobs'], query, ...opts })).data
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
class AdminInvitations {
|
|
539
|
+
/** @param {Admin} admin */
|
|
540
|
+
constructor(admin) {
|
|
541
|
+
/** @internal */
|
|
542
|
+
this.admin = admin
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Creates an invitation; deliver its `token` to the invitee.
|
|
547
|
+
* @param {{ email?: string, expiresIn?: string }} [input] `expiresIn`: days (`7d`) or Go durations (`12h`).
|
|
548
|
+
* @param {RequestOptions} [opts]
|
|
549
|
+
* @returns {Promise<NewInvitation>}
|
|
550
|
+
*/
|
|
551
|
+
async create({ email, expiresIn } = {}, opts) {
|
|
552
|
+
/** @type {Record<string, string>} */
|
|
553
|
+
const body = {}
|
|
554
|
+
if (email !== undefined) body.email = email
|
|
555
|
+
if (expiresIn !== undefined) body.expires_in = expiresIn
|
|
556
|
+
return (await this.admin._request({ method: 'POST', path: ['invitations'], body, ...opts })).data
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Creates an invitation and has `backd` email it to `email` (needs `email`
|
|
561
|
+
* in the realm's `realm.yaml`): the link opens a page where the person
|
|
562
|
+
* chooses a password, or your own page with `email.links.invitation` (see
|
|
563
|
+
* `auth.acceptInvitation`). There is no token to deliver. `redirectTo` is
|
|
564
|
+
* where the page after accepting may send them (within
|
|
565
|
+
* `email.allowed_redirects`); `locale` is the language of the email.
|
|
566
|
+
* @param {{ email: string, expiresIn?: string, redirectTo?: string, locale?: string }} input
|
|
567
|
+
* @param {RequestOptions} [opts]
|
|
568
|
+
* @returns {Promise<SentInvitation>}
|
|
569
|
+
*/
|
|
570
|
+
async send({ email, expiresIn, redirectTo, locale }, opts) {
|
|
571
|
+
/** @type {Record<string, unknown>} */
|
|
572
|
+
const body = { email, send: true }
|
|
573
|
+
if (expiresIn !== undefined) body.expires_in = expiresIn
|
|
574
|
+
if (redirectTo !== undefined) body.redirect_to = redirectTo
|
|
575
|
+
if (locale !== undefined) body.locale = locale
|
|
576
|
+
return (await this.admin._request({ method: 'POST', path: ['invitations'], body, ...opts })).data
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Unused, unexpired invitations, newest first. Tokens are never listed.
|
|
581
|
+
* @param {RequestOptions} [opts]
|
|
582
|
+
* @returns {Promise<Invitation[]>}
|
|
583
|
+
*/
|
|
584
|
+
async list(opts) {
|
|
585
|
+
return (await this.admin._request({ method: 'GET', path: ['invitations'], ...opts })).data.items
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* Revokes an invitation.
|
|
590
|
+
* @param {string} id
|
|
591
|
+
* @param {RequestOptions} [opts]
|
|
592
|
+
* @returns {Promise<void>}
|
|
593
|
+
*/
|
|
594
|
+
async revoke(id, opts) {
|
|
595
|
+
await this.admin._request({ method: 'DELETE', path: ['invitations', id], ...opts })
|
|
596
|
+
}
|
|
597
|
+
}
|