@dereekb/firebase-server 12.6.21 → 13.0.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 +1 -1
- package/index.cjs.js +2937 -0
- package/index.esm.js +2775 -0
- package/mailgun/index.cjs.js +48 -0
- package/mailgun/index.esm.js +46 -0
- package/mailgun/package.json +26 -6
- package/model/index.cjs.js +5298 -0
- package/model/index.d.ts +1 -0
- package/model/index.esm.js +5161 -0
- package/model/package.json +31 -3
- package/model/src/lib/notification/notification.module.d.ts +10 -10
- package/model/src/lib/storagefile/storagefile.task.service.handler.d.ts +3 -14
- package/package.json +48 -27
- package/src/lib/auth/auth.context.d.ts +1 -1
- package/src/lib/auth/auth.service.d.ts +1 -1
- package/src/lib/function/assert.d.ts +2 -2
- package/src/lib/function/context.d.ts +1 -1
- package/src/lib/index.d.ts +1 -0
- package/src/lib/nest/app.d.ts +1 -1
- package/src/lib/nest/development/development.app.function.d.ts +1 -2
- package/src/lib/nest/function/call.d.ts +1 -1
- package/src/lib/nest/function/context.d.ts +0 -4
- package/src/lib/nest/function/index.d.ts +0 -1
- package/src/lib/nest/function/nest.d.ts +1 -1
- package/src/lib/nest/function/v2/blocking.d.ts +3 -2
- package/src/lib/nest/model/crud.assert.function.d.ts +0 -4
- package/src/lib/storage/storage.d.ts +1 -1
- package/src/lib/type.d.ts +9 -0
- package/test/index.cjs.js +1324 -0
- package/test/index.d.ts +1 -0
- package/test/index.esm.js +1247 -0
- package/test/package.json +34 -3
- package/test/src/lib/firebase/firebase.admin.auth.d.ts +22 -13
- package/test/src/lib/firebase/firebase.admin.collection.d.ts +6 -6
- package/test/src/lib/firebase/firebase.admin.d.ts +10 -10
- package/test/src/lib/firebase/firebase.admin.function.d.ts +9 -9
- package/test/src/lib/firebase/firebase.admin.nest.d.ts +8 -8
- package/test/src/lib/firebase/firebase.admin.nest.function.d.ts +9 -9
- package/test/src/lib/firebase/firebase.test.d.ts +30 -0
- package/test/src/lib/firebase/index.d.ts +1 -1
- package/test/src/lib/firestore/firestore.admin.d.ts +3 -3
- package/test/src/lib/firestore/firestore.d.ts +2 -2
- package/test/src/lib/storage/storage.admin.d.ts +3 -3
- package/test/src/lib/storage/storage.d.ts +2 -2
- package/zoho/LICENSE +1 -1
- package/zoho/index.d.ts +1 -0
- package/zoho/index.esm.js +2 -2
- package/zoho/package.json +12 -8
- package/CHANGELOG.md +0 -2233
- package/mailgun/src/index.js +0 -5
- package/mailgun/src/index.js.map +0 -1
- package/mailgun/src/lib/auth.mailgun.js +0 -41
- package/mailgun/src/lib/auth.mailgun.js.map +0 -1
- package/mailgun/src/lib/index.js +0 -5
- package/mailgun/src/lib/index.js.map +0 -1
- package/model/src/index.js +0 -5
- package/model/src/index.js.map +0 -1
- package/model/src/lib/index.js +0 -7
- package/model/src/lib/index.js.map +0 -1
- package/model/src/lib/mailgun/index.js +0 -5
- package/model/src/lib/mailgun/index.js.map +0 -1
- package/model/src/lib/mailgun/notification.send.service.mailgun.js +0 -68
- package/model/src/lib/mailgun/notification.send.service.mailgun.js.map +0 -1
- package/model/src/lib/notification/index.js +0 -20
- package/model/src/lib/notification/index.js.map +0 -1
- package/model/src/lib/notification/notification.action.init.service.js +0 -230
- package/model/src/lib/notification/notification.action.init.service.js.map +0 -1
- package/model/src/lib/notification/notification.action.service.js +0 -1487
- package/model/src/lib/notification/notification.action.service.js.map +0 -1
- package/model/src/lib/notification/notification.config.js +0 -13
- package/model/src/lib/notification/notification.config.js.map +0 -1
- package/model/src/lib/notification/notification.config.service.js +0 -60
- package/model/src/lib/notification/notification.config.service.js.map +0 -1
- package/model/src/lib/notification/notification.create.run.js +0 -59
- package/model/src/lib/notification/notification.create.run.js.map +0 -1
- package/model/src/lib/notification/notification.error.js +0 -87
- package/model/src/lib/notification/notification.error.js.map +0 -1
- package/model/src/lib/notification/notification.expedite.service.js +0 -112
- package/model/src/lib/notification/notification.expedite.service.js.map +0 -1
- package/model/src/lib/notification/notification.module.js +0 -106
- package/model/src/lib/notification/notification.module.js.map +0 -1
- package/model/src/lib/notification/notification.send.js +0 -3
- package/model/src/lib/notification/notification.send.js.map +0 -1
- package/model/src/lib/notification/notification.send.service.js +0 -10
- package/model/src/lib/notification/notification.send.service.js.map +0 -1
- package/model/src/lib/notification/notification.send.service.notificationsummary.js +0 -104
- package/model/src/lib/notification/notification.send.service.notificationsummary.js.map +0 -1
- package/model/src/lib/notification/notification.send.service.text.js +0 -29
- package/model/src/lib/notification/notification.send.service.text.js.map +0 -1
- package/model/src/lib/notification/notification.task.service.handler.js +0 -65
- package/model/src/lib/notification/notification.task.service.handler.js.map +0 -1
- package/model/src/lib/notification/notification.task.service.js +0 -10
- package/model/src/lib/notification/notification.task.service.js.map +0 -1
- package/model/src/lib/notification/notification.task.service.util.js +0 -27
- package/model/src/lib/notification/notification.task.service.util.js.map +0 -1
- package/model/src/lib/notification/notification.task.subtask.handler.js +0 -256
- package/model/src/lib/notification/notification.task.subtask.handler.js.map +0 -1
- package/model/src/lib/notification/notification.util.js +0 -478
- package/model/src/lib/notification/notification.util.js.map +0 -1
- package/model/src/lib/storagefile/index.js +0 -12
- package/model/src/lib/storagefile/index.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.action.init.service.js +0 -155
- package/model/src/lib/storagefile/storagefile.action.init.service.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.action.server.js +0 -797
- package/model/src/lib/storagefile/storagefile.action.server.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.error.js +0 -106
- package/model/src/lib/storagefile/storagefile.error.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.module.js +0 -64
- package/model/src/lib/storagefile/storagefile.module.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.task.service.handler.js +0 -287
- package/model/src/lib/storagefile/storagefile.task.service.handler.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.upload.service.initializer.js +0 -180
- package/model/src/lib/storagefile/storagefile.upload.service.initializer.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.upload.service.js +0 -10
- package/model/src/lib/storagefile/storagefile.upload.service.js.map +0 -1
- package/model/src/lib/storagefile/storagefile.util.js +0 -54
- package/model/src/lib/storagefile/storagefile.util.js.map +0 -1
- package/src/index.js +0 -5
- package/src/index.js.map +0 -1
- package/src/lib/auth/auth.context.js +0 -13
- package/src/lib/auth/auth.context.js.map +0 -1
- package/src/lib/auth/auth.service.error.js +0 -34
- package/src/lib/auth/auth.service.error.js.map +0 -1
- package/src/lib/auth/auth.service.js +0 -427
- package/src/lib/auth/auth.service.js.map +0 -1
- package/src/lib/auth/auth.util.js +0 -23
- package/src/lib/auth/auth.util.js.map +0 -1
- package/src/lib/auth/index.js +0 -8
- package/src/lib/auth/index.js.map +0 -1
- package/src/lib/env/env.service.js +0 -7
- package/src/lib/env/env.service.js.map +0 -1
- package/src/lib/env/index.js +0 -5
- package/src/lib/env/index.js.map +0 -1
- package/src/lib/firestore/array.js +0 -34
- package/src/lib/firestore/array.js.map +0 -1
- package/src/lib/firestore/driver.accessor.batch.js +0 -93
- package/src/lib/firestore/driver.accessor.batch.js.map +0 -1
- package/src/lib/firestore/driver.accessor.default.js +0 -62
- package/src/lib/firestore/driver.accessor.default.js.map +0 -1
- package/src/lib/firestore/driver.accessor.js +0 -50
- package/src/lib/firestore/driver.accessor.js.map +0 -1
- package/src/lib/firestore/driver.accessor.transaction.js +0 -96
- package/src/lib/firestore/driver.accessor.transaction.js.map +0 -1
- package/src/lib/firestore/driver.js +0 -14
- package/src/lib/firestore/driver.js.map +0 -1
- package/src/lib/firestore/driver.query.js +0 -55
- package/src/lib/firestore/driver.query.js.map +0 -1
- package/src/lib/firestore/firestore.js +0 -10
- package/src/lib/firestore/firestore.js.map +0 -1
- package/src/lib/firestore/increment.js +0 -17
- package/src/lib/firestore/increment.js.map +0 -1
- package/src/lib/firestore/index.js +0 -9
- package/src/lib/firestore/index.js.map +0 -1
- package/src/lib/function/assert.js +0 -68
- package/src/lib/function/assert.js.map +0 -1
- package/src/lib/function/context.js +0 -14
- package/src/lib/function/context.js.map +0 -1
- package/src/lib/function/error.auth.js +0 -25
- package/src/lib/function/error.auth.js.map +0 -1
- package/src/lib/function/error.js +0 -221
- package/src/lib/function/error.js.map +0 -1
- package/src/lib/function/index.js +0 -9
- package/src/lib/function/index.js.map +0 -1
- package/src/lib/function/type.js +0 -3
- package/src/lib/function/type.js.map +0 -1
- package/src/lib/index.js +0 -11
- package/src/lib/index.js.map +0 -1
- package/src/lib/nest/app.js +0 -114
- package/src/lib/nest/app.js.map +0 -1
- package/src/lib/nest/auth/auth.module.js +0 -60
- package/src/lib/nest/auth/auth.module.js.map +0 -1
- package/src/lib/nest/auth/auth.util.js +0 -72
- package/src/lib/nest/auth/auth.util.js.map +0 -1
- package/src/lib/nest/auth/index.js +0 -6
- package/src/lib/nest/auth/index.js.map +0 -1
- package/src/lib/nest/development/development.app.function.js +0 -38
- package/src/lib/nest/development/development.app.function.js.map +0 -1
- package/src/lib/nest/development/development.assert.function.js +0 -3
- package/src/lib/nest/development/development.assert.function.js.map +0 -1
- package/src/lib/nest/development/development.function.js +0 -41
- package/src/lib/nest/development/development.function.js.map +0 -1
- package/src/lib/nest/development/development.schedule.function.error.js +0 -35
- package/src/lib/nest/development/development.schedule.function.error.js.map +0 -1
- package/src/lib/nest/development/development.schedule.function.js +0 -54
- package/src/lib/nest/development/development.schedule.function.js.map +0 -1
- package/src/lib/nest/development/index.js +0 -9
- package/src/lib/nest/development/index.js.map +0 -1
- package/src/lib/nest/env/env.service.js +0 -19
- package/src/lib/nest/env/env.service.js.map +0 -1
- package/src/lib/nest/env/env.util.js +0 -12
- package/src/lib/nest/env/env.util.js.map +0 -1
- package/src/lib/nest/env/index.js +0 -6
- package/src/lib/nest/env/index.js.map +0 -1
- package/src/lib/nest/firebase/firebase.module.js +0 -17
- package/src/lib/nest/firebase/firebase.module.js.map +0 -1
- package/src/lib/nest/firebase/index.js +0 -5
- package/src/lib/nest/firebase/index.js.map +0 -1
- package/src/lib/nest/firestore/firestore.module.js +0 -86
- package/src/lib/nest/firestore/firestore.module.js.map +0 -1
- package/src/lib/nest/firestore/index.js +0 -5
- package/src/lib/nest/firestore/index.js.map +0 -1
- package/src/lib/nest/function/call.js +0 -46
- package/src/lib/nest/function/call.js.map +0 -1
- package/src/lib/nest/function/context.js +0 -79
- package/src/lib/nest/function/context.js.map +0 -1
- package/src/lib/nest/function/index.js +0 -10
- package/src/lib/nest/function/index.js.map +0 -1
- package/src/lib/nest/function/nest.js +0 -17
- package/src/lib/nest/function/nest.js.map +0 -1
- package/src/lib/nest/function/schedule.js +0 -8
- package/src/lib/nest/function/schedule.js.map +0 -1
- package/src/lib/nest/function/v1/call.d.ts +0 -59
- package/src/lib/nest/function/v1/call.js +0 -55
- package/src/lib/nest/function/v1/call.js.map +0 -1
- package/src/lib/nest/function/v1/event.d.ts +0 -80
- package/src/lib/nest/function/v1/event.js +0 -52
- package/src/lib/nest/function/v1/event.js.map +0 -1
- package/src/lib/nest/function/v1/index.d.ts +0 -3
- package/src/lib/nest/function/v1/index.js +0 -7
- package/src/lib/nest/function/v1/index.js.map +0 -1
- package/src/lib/nest/function/v1/schedule.d.ts +0 -47
- package/src/lib/nest/function/v1/schedule.js +0 -68
- package/src/lib/nest/function/v1/schedule.js.map +0 -1
- package/src/lib/nest/function/v2/blocking.js +0 -38
- package/src/lib/nest/function/v2/blocking.js.map +0 -1
- package/src/lib/nest/function/v2/call.js +0 -31
- package/src/lib/nest/function/v2/call.js.map +0 -1
- package/src/lib/nest/function/v2/event.js +0 -25
- package/src/lib/nest/function/v2/event.js.map +0 -1
- package/src/lib/nest/function/v2/index.js +0 -9
- package/src/lib/nest/function/v2/index.js.map +0 -1
- package/src/lib/nest/function/v2/schedule.js +0 -56
- package/src/lib/nest/function/v2/schedule.js.map +0 -1
- package/src/lib/nest/function/v2/taskqueue.js +0 -26
- package/src/lib/nest/function/v2/taskqueue.js.map +0 -1
- package/src/lib/nest/index.js +0 -15
- package/src/lib/nest/index.js.map +0 -1
- package/src/lib/nest/middleware/appcheck.decorator.js +0 -12
- package/src/lib/nest/middleware/appcheck.decorator.js.map +0 -1
- package/src/lib/nest/middleware/appcheck.js +0 -3
- package/src/lib/nest/middleware/appcheck.js.map +0 -1
- package/src/lib/nest/middleware/appcheck.middleware.js +0 -74
- package/src/lib/nest/middleware/appcheck.middleware.js.map +0 -1
- package/src/lib/nest/middleware/appcheck.module.js +0 -21
- package/src/lib/nest/middleware/appcheck.module.js.map +0 -1
- package/src/lib/nest/middleware/globalprefix.js +0 -11
- package/src/lib/nest/middleware/globalprefix.js.map +0 -1
- package/src/lib/nest/middleware/index.js +0 -10
- package/src/lib/nest/middleware/index.js.map +0 -1
- package/src/lib/nest/middleware/rawbody.middleware.js +0 -16
- package/src/lib/nest/middleware/rawbody.middleware.js.map +0 -1
- package/src/lib/nest/middleware/webhook.js +0 -24
- package/src/lib/nest/middleware/webhook.js.map +0 -1
- package/src/lib/nest/model/call.model.function.js +0 -73
- package/src/lib/nest/model/call.model.function.js.map +0 -1
- package/src/lib/nest/model/create.model.function.js +0 -27
- package/src/lib/nest/model/create.model.function.js.map +0 -1
- package/src/lib/nest/model/crud.assert.function.js +0 -3
- package/src/lib/nest/model/crud.assert.function.js.map +0 -1
- package/src/lib/nest/model/delete.model.function.js +0 -27
- package/src/lib/nest/model/delete.model.function.js.map +0 -1
- package/src/lib/nest/model/index.js +0 -11
- package/src/lib/nest/model/index.js.map +0 -1
- package/src/lib/nest/model/permission.error.js +0 -24
- package/src/lib/nest/model/permission.error.js.map +0 -1
- package/src/lib/nest/model/read.model.function.js +0 -27
- package/src/lib/nest/model/read.model.function.js.map +0 -1
- package/src/lib/nest/model/specifier.function.js +0 -35
- package/src/lib/nest/model/specifier.function.js.map +0 -1
- package/src/lib/nest/model/update.model.function.js +0 -27
- package/src/lib/nest/model/update.model.function.js.map +0 -1
- package/src/lib/nest/nest.provider.js +0 -89
- package/src/lib/nest/nest.provider.js.map +0 -1
- package/src/lib/nest/storage/index.js +0 -5
- package/src/lib/nest/storage/index.js.map +0 -1
- package/src/lib/nest/storage/storage.module.js +0 -112
- package/src/lib/nest/storage/storage.module.js.map +0 -1
- package/src/lib/storage/driver.accessor.js +0 -299
- package/src/lib/storage/driver.accessor.js.map +0 -1
- package/src/lib/storage/driver.js +0 -12
- package/src/lib/storage/driver.js.map +0 -1
- package/src/lib/storage/index.js +0 -8
- package/src/lib/storage/index.js.map +0 -1
- package/src/lib/storage/storage.js +0 -20
- package/src/lib/storage/storage.js.map +0 -1
- package/src/lib/storage/storage.service.js +0 -26
- package/src/lib/storage/storage.service.js.map +0 -1
- package/test/src/index.js +0 -5
- package/test/src/index.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.auth.js +0 -260
- package/test/src/lib/firebase/firebase.admin.auth.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.collection.js +0 -108
- package/test/src/lib/firebase/firebase.admin.collection.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.function.js +0 -132
- package/test/src/lib/firebase/firebase.admin.function.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.js +0 -174
- package/test/src/lib/firebase/firebase.admin.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.nest.function.callable.context.js +0 -42
- package/test/src/lib/firebase/firebase.admin.nest.function.callable.context.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.nest.function.cloud.context.js +0 -40
- package/test/src/lib/firebase/firebase.admin.nest.function.cloud.context.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.nest.function.js +0 -64
- package/test/src/lib/firebase/firebase.admin.nest.function.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.nest.js +0 -107
- package/test/src/lib/firebase/firebase.admin.nest.js.map +0 -1
- package/test/src/lib/firebase/firebase.admin.test.server.js +0 -37
- package/test/src/lib/firebase/firebase.admin.test.server.js.map +0 -1
- package/test/src/lib/firebase/firebase.function.js +0 -58
- package/test/src/lib/firebase/firebase.function.js.map +0 -1
- package/test/src/lib/firebase/firebase.jest.d.ts +0 -21
- package/test/src/lib/firebase/firebase.jest.js +0 -45
- package/test/src/lib/firebase/firebase.jest.js.map +0 -1
- package/test/src/lib/firebase/firebase.js +0 -74
- package/test/src/lib/firebase/firebase.js.map +0 -1
- package/test/src/lib/firebase/index.js +0 -15
- package/test/src/lib/firebase/index.js.map +0 -1
- package/test/src/lib/firestore/firestore.admin.js +0 -21
- package/test/src/lib/firestore/firestore.admin.js.map +0 -1
- package/test/src/lib/firestore/firestore.js +0 -57
- package/test/src/lib/firestore/firestore.js.map +0 -1
- package/test/src/lib/firestore/index.js +0 -6
- package/test/src/lib/firestore/index.js.map +0 -1
- package/test/src/lib/index.js +0 -7
- package/test/src/lib/index.js.map +0 -1
- package/test/src/lib/storage/index.js +0 -6
- package/test/src/lib/storage/index.js.map +0 -1
- package/test/src/lib/storage/storage.admin.js +0 -21
- package/test/src/lib/storage/storage.admin.js.map +0 -1
- package/test/src/lib/storage/storage.js +0 -59
- package/test/src/lib/storage/storage.js.map +0 -1
- /package/{zoho/index.cjs.d.ts → index.d.ts} +0 -0
- /package/{zoho/index.esm.d.ts → mailgun/index.d.ts} +0 -0
package/index.cjs.js
ADDED
|
@@ -0,0 +1,2937 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var firebase = require('@dereekb/firebase');
|
|
4
|
+
var util = require('@dereekb/util');
|
|
5
|
+
var https = require('firebase-functions/https');
|
|
6
|
+
var date = require('@dereekb/date');
|
|
7
|
+
var makeError = require('make-error');
|
|
8
|
+
require('core-js/modules/es.iterator.constructor.js');
|
|
9
|
+
require('core-js/modules/es.iterator.for-each.js');
|
|
10
|
+
var rxjs = require('rxjs');
|
|
11
|
+
var firestore = require('@google-cloud/firestore');
|
|
12
|
+
var common = require('@nestjs/common');
|
|
13
|
+
var nestjs = require('@dereekb/nestjs');
|
|
14
|
+
var v2 = require('firebase-functions/v2');
|
|
15
|
+
var model = require('@dereekb/model');
|
|
16
|
+
var admin = require('firebase-admin');
|
|
17
|
+
require('core-js/modules/es.iterator.map.js');
|
|
18
|
+
var storage = require('@google-cloud/storage');
|
|
19
|
+
var dateFns = require('date-fns');
|
|
20
|
+
var types = require('util/types');
|
|
21
|
+
require('core-js/modules/es.map.get-or-insert.js');
|
|
22
|
+
require('core-js/modules/es.map.get-or-insert-computed.js');
|
|
23
|
+
var core = require('@nestjs/core');
|
|
24
|
+
var platformExpress = require('@nestjs/platform-express');
|
|
25
|
+
var express = require('express');
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated use DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE instead
|
|
29
|
+
*/
|
|
30
|
+
const NO_AUTH_ERROR_CODE = firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE;
|
|
31
|
+
function unauthenticatedContextHasNoAuthData() {
|
|
32
|
+
return unauthenticatedError({
|
|
33
|
+
message: 'expected auth',
|
|
34
|
+
code: NO_AUTH_ERROR_CODE
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* @deprecated use DBX_FIREBASE_SERVER_NO_UID_ERROR_CODE instead
|
|
39
|
+
*/
|
|
40
|
+
const NO_UID_ERROR_CODE = firebase.DBX_FIREBASE_SERVER_NO_AUTH_ERROR_CODE;
|
|
41
|
+
function unauthenticatedContextHasNoUidError() {
|
|
42
|
+
return unauthenticatedError({
|
|
43
|
+
message: 'no user uid',
|
|
44
|
+
code: NO_UID_ERROR_CODE
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
// MARK: General Errors
|
|
48
|
+
const UNAUTHENTICATED_ERROR_CODE = 'UNAUTHENTICATED';
|
|
49
|
+
function unauthenticatedError(messageOrError) {
|
|
50
|
+
const serverError = util.partialServerError(messageOrError);
|
|
51
|
+
return new https.HttpsError('unauthenticated', serverError?.message || 'unauthenticated', {
|
|
52
|
+
status: 401,
|
|
53
|
+
code: UNAUTHENTICATED_ERROR_CODE,
|
|
54
|
+
...serverError,
|
|
55
|
+
_error: undefined
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
const FORBIDDEN_ERROR_CODE = 'FORBIDDEN';
|
|
59
|
+
function forbiddenError(messageOrError) {
|
|
60
|
+
const serverError = util.partialServerError(messageOrError);
|
|
61
|
+
return new https.HttpsError('permission-denied', serverError?.message || 'forbidden', {
|
|
62
|
+
status: 403,
|
|
63
|
+
code: FORBIDDEN_ERROR_CODE,
|
|
64
|
+
...serverError,
|
|
65
|
+
_error: undefined
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
const PERMISSION_DENIED_ERROR_CODE = 'PERMISSION_DENIED';
|
|
69
|
+
function permissionDeniedError(messageOrError) {
|
|
70
|
+
const serverError = util.partialServerError(messageOrError);
|
|
71
|
+
return new https.HttpsError('permission-denied', serverError?.message || 'permission denied', {
|
|
72
|
+
status: 403,
|
|
73
|
+
code: PERMISSION_DENIED_ERROR_CODE,
|
|
74
|
+
...serverError,
|
|
75
|
+
_error: undefined
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const NOT_FOUND_ERROR_CODE = 'NOT_FOUND';
|
|
79
|
+
function notFoundError(messageOrError) {
|
|
80
|
+
const serverError = util.partialServerError(messageOrError);
|
|
81
|
+
return new https.HttpsError('not-found', serverError?.message || 'not found', {
|
|
82
|
+
status: 404,
|
|
83
|
+
code: NOT_FOUND_ERROR_CODE,
|
|
84
|
+
...serverError,
|
|
85
|
+
_error: undefined
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const MODEL_NOT_AVAILABLE_ERROR_CODE = 'MODEL_NOT_AVAILABLE';
|
|
89
|
+
function modelNotAvailableError(messageOrError) {
|
|
90
|
+
const serverError = util.partialServerError(messageOrError);
|
|
91
|
+
return new https.HttpsError('not-found', serverError?.message || 'model was not available', {
|
|
92
|
+
status: 404,
|
|
93
|
+
code: MODEL_NOT_AVAILABLE_ERROR_CODE,
|
|
94
|
+
...serverError,
|
|
95
|
+
_error: undefined
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const BAD_REQUEST_ERROR_CODE = 'BAD_REQUEST';
|
|
99
|
+
function badRequestError(messageOrError) {
|
|
100
|
+
const serverError = util.partialServerError(messageOrError);
|
|
101
|
+
return new https.HttpsError('invalid-argument', serverError?.message || 'bad request', {
|
|
102
|
+
status: 400,
|
|
103
|
+
code: BAD_REQUEST_ERROR_CODE,
|
|
104
|
+
...serverError,
|
|
105
|
+
_error: undefined
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
const CONFLICT_ERROR_CODE = 'CONFLICT';
|
|
109
|
+
function preconditionConflictError(messageOrError) {
|
|
110
|
+
const serverError = util.partialServerError(messageOrError);
|
|
111
|
+
return new https.HttpsError('failed-precondition', serverError?.message || 'conflict', {
|
|
112
|
+
status: 409,
|
|
113
|
+
code: CONFLICT_ERROR_CODE,
|
|
114
|
+
...serverError,
|
|
115
|
+
_error: undefined
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
const ALREADY_EXISTS_ERROR_CODE = 'ALREADY_EXISTS';
|
|
119
|
+
function alreadyExistsError(messageOrError) {
|
|
120
|
+
const serverError = util.partialServerError(messageOrError);
|
|
121
|
+
return new https.HttpsError('already-exists', serverError?.message || 'already exists', {
|
|
122
|
+
status: 409,
|
|
123
|
+
code: ALREADY_EXISTS_ERROR_CODE,
|
|
124
|
+
...serverError,
|
|
125
|
+
_error: undefined
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
const UNAVAILABLE_ERROR_CODE = 'UNAVAILABLE';
|
|
129
|
+
function unavailableError(messageOrError) {
|
|
130
|
+
const serverError = util.partialServerError(messageOrError);
|
|
131
|
+
return new https.HttpsError('unavailable', serverError?.message || 'service unavailable', {
|
|
132
|
+
status: 503,
|
|
133
|
+
code: UNAVAILABLE_ERROR_CODE,
|
|
134
|
+
...serverError,
|
|
135
|
+
_error: undefined
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE = 'UNAVAILABLE_OR_DEACTIVATED_FUNCTION';
|
|
139
|
+
function unavailableOrDeactivatedFunctionError(messageOrError) {
|
|
140
|
+
const serverError = util.partialServerError(messageOrError);
|
|
141
|
+
return new https.HttpsError('unimplemented', serverError?.message || 'the requested function is not available or has been deactivated for use', {
|
|
142
|
+
status: 501,
|
|
143
|
+
code: UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE,
|
|
144
|
+
...serverError,
|
|
145
|
+
_error: undefined
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const INTERNAL_SERVER_ERROR_CODE = 'INTERNAL_ERROR';
|
|
149
|
+
function internalServerError(messageOrError) {
|
|
150
|
+
const serverError = util.partialServerError(messageOrError);
|
|
151
|
+
return new https.HttpsError('internal', serverError?.message || 'internal error', {
|
|
152
|
+
status: 500,
|
|
153
|
+
code: INTERNAL_SERVER_ERROR_CODE,
|
|
154
|
+
...serverError,
|
|
155
|
+
_error: undefined
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
function isFirebaseHttpsError(input) {
|
|
159
|
+
return typeof input === 'object' && input.code != null && input.httpErrorCode != null && input.toJSON != null;
|
|
160
|
+
}
|
|
161
|
+
function isFirebaseError(input) {
|
|
162
|
+
return typeof input === 'object' && input.code != null && input.message != null && input.toJSON != null;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Creates a FirebaseServerErrorInfo from the input.
|
|
166
|
+
*
|
|
167
|
+
* @param e
|
|
168
|
+
* @returns
|
|
169
|
+
*/
|
|
170
|
+
function firebaseServerErrorInfo(e) {
|
|
171
|
+
let type = 'unknown';
|
|
172
|
+
let httpsError;
|
|
173
|
+
let firebaseError;
|
|
174
|
+
let firebaseErrorCode;
|
|
175
|
+
let httpsErrorDetailsServerError;
|
|
176
|
+
let serverErrorCode;
|
|
177
|
+
if (e != null) {
|
|
178
|
+
if (isFirebaseHttpsError(e)) {
|
|
179
|
+
type = 'httpsError';
|
|
180
|
+
httpsError = e;
|
|
181
|
+
firebaseErrorCode = httpsError.code;
|
|
182
|
+
if (httpsError.details && util.isServerError(httpsError.details)) {
|
|
183
|
+
httpsErrorDetailsServerError = httpsError.details;
|
|
184
|
+
serverErrorCode = httpsErrorDetailsServerError.code;
|
|
185
|
+
}
|
|
186
|
+
} else if (isFirebaseError(e)) {
|
|
187
|
+
type = 'firebaseError';
|
|
188
|
+
firebaseError = e;
|
|
189
|
+
firebaseErrorCode = firebaseError.code;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
httpsError,
|
|
194
|
+
firebaseError,
|
|
195
|
+
firebaseErrorCode,
|
|
196
|
+
httpsErrorDetailsServerError,
|
|
197
|
+
serverErrorCode,
|
|
198
|
+
type,
|
|
199
|
+
e
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function firebaseServerErrorInfoCodePair(e) {
|
|
203
|
+
const info = firebaseServerErrorInfo(e);
|
|
204
|
+
return [info.firebaseErrorCode, info];
|
|
205
|
+
}
|
|
206
|
+
function firebaseServerErrorInfoServerErrorPair(e) {
|
|
207
|
+
const info = firebaseServerErrorInfo(e);
|
|
208
|
+
return [info.httpsErrorDetailsServerError, info];
|
|
209
|
+
}
|
|
210
|
+
function firebaseServerErrorInfoServerErrorCodePair(e) {
|
|
211
|
+
const info = firebaseServerErrorInfo(e);
|
|
212
|
+
return [info.serverErrorCode, info];
|
|
213
|
+
}
|
|
214
|
+
function handleFirebaseError(e, handleFirebaseErrorFn) {
|
|
215
|
+
const firebaseError = e.code ? e : undefined;
|
|
216
|
+
if (firebaseError) {
|
|
217
|
+
handleFirebaseErrorFn(firebaseError);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function isContextWithAuthData(context) {
|
|
222
|
+
return Boolean(context.auth !== null && context.auth?.uid);
|
|
223
|
+
}
|
|
224
|
+
function assertIsContextWithAuthData(context) {
|
|
225
|
+
if (!isContextWithAuthData(context)) {
|
|
226
|
+
throw unauthenticatedContextHasNoAuthData();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function firebaseAuthTokenFromDecodedIdToken(token) {
|
|
231
|
+
return {
|
|
232
|
+
email: token.email,
|
|
233
|
+
emailVerified: token.email_verified,
|
|
234
|
+
phoneNumber: token.phone_number,
|
|
235
|
+
lastSignInTime: new Date(token.auth_time).toISOString(),
|
|
236
|
+
lastRefreshTime: new Date(token.iat).toISOString()
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Awaits the load result from the input promise. If it encounters a FIREBASE_AUTH_USER_NOT_FOUND_ERROR, then returns undefined. Throws the error otherwise.
|
|
242
|
+
* @param promise
|
|
243
|
+
* @returns
|
|
244
|
+
*/
|
|
245
|
+
async function getAuthUserOrUndefined(promise) {
|
|
246
|
+
try {
|
|
247
|
+
return await promise;
|
|
248
|
+
} catch (error) {
|
|
249
|
+
if (error?.code === firebase.FIREBASE_AUTH_USER_NOT_FOUND_ERROR) {
|
|
250
|
+
return undefined;
|
|
251
|
+
} else {
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Thrown by sendSetupDetails() if the user has no setup configuration available, meaning they probably already have accepted their invite or is in an invalid state.
|
|
259
|
+
*/
|
|
260
|
+
class FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError extends makeError.BaseError {
|
|
261
|
+
constructor() {
|
|
262
|
+
super(`This user has no setup configuration available.`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Thrown by sendSetupDetails() if the user was recently sent details.
|
|
267
|
+
*/
|
|
268
|
+
class FirebaseServerAuthNewUserSendSetupDetailsThrottleError extends makeError.BaseError {
|
|
269
|
+
constructor(lastSentAt) {
|
|
270
|
+
super(`This user was recently sent details. Try again later.`);
|
|
271
|
+
this.lastSentAt = void 0;
|
|
272
|
+
this.lastSentAt = lastSentAt;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Thrown by sendSetupDetails() if the user was recently sent details.
|
|
277
|
+
*/
|
|
278
|
+
class FirebaseServerAuthNewUserSendSetupDetailsSendOnceError extends makeError.BaseError {
|
|
279
|
+
constructor() {
|
|
280
|
+
super(`The user has been sent details before and the sendSetupDetailsOnce configuration was true.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR = util.randomNumberFactory({
|
|
285
|
+
min: 100000,
|
|
286
|
+
max: 1000000,
|
|
287
|
+
round: 'floor'
|
|
288
|
+
}); // 6 digits
|
|
289
|
+
class AbstractFirebaseServerAuthUserContext {
|
|
290
|
+
constructor(service, uid) {
|
|
291
|
+
this._service = void 0;
|
|
292
|
+
this._uid = void 0;
|
|
293
|
+
this._loadRecord = util.cachedGetter(() => this._service.auth.getUser(this._uid));
|
|
294
|
+
this._service = service;
|
|
295
|
+
this._uid = uid;
|
|
296
|
+
}
|
|
297
|
+
get service() {
|
|
298
|
+
return this._service;
|
|
299
|
+
}
|
|
300
|
+
get uid() {
|
|
301
|
+
return this._uid;
|
|
302
|
+
}
|
|
303
|
+
async exists() {
|
|
304
|
+
return getAuthUserOrUndefined(this._loadRecord()).then(x => Boolean(x));
|
|
305
|
+
}
|
|
306
|
+
loadRecord() {
|
|
307
|
+
return this._loadRecord();
|
|
308
|
+
}
|
|
309
|
+
loadDetails() {
|
|
310
|
+
return this.loadRecord().then(record => this.service.authDetailsForRecord(record));
|
|
311
|
+
}
|
|
312
|
+
_generateResetPasswordKey() {
|
|
313
|
+
return String(DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR());
|
|
314
|
+
}
|
|
315
|
+
async beginResetPassword() {
|
|
316
|
+
const password = this._generateResetPasswordKey();
|
|
317
|
+
const passwordClaimsData = {
|
|
318
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY]: password,
|
|
319
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY]: date.toISODateString(new Date())
|
|
320
|
+
};
|
|
321
|
+
// set the claims
|
|
322
|
+
await this.updateClaims(passwordClaimsData);
|
|
323
|
+
// update the user
|
|
324
|
+
await this.updateUser({
|
|
325
|
+
password
|
|
326
|
+
});
|
|
327
|
+
return passwordClaimsData;
|
|
328
|
+
}
|
|
329
|
+
async loadResetPasswordClaims() {
|
|
330
|
+
const claims = await this.loadClaims();
|
|
331
|
+
if (claims.resetPassword != null) {
|
|
332
|
+
return claims;
|
|
333
|
+
} else {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Sets the user's password.
|
|
339
|
+
*/
|
|
340
|
+
async setPassword(password) {
|
|
341
|
+
const record = await this.updateUser({
|
|
342
|
+
password
|
|
343
|
+
});
|
|
344
|
+
// clear password reset claims
|
|
345
|
+
await this.updateClaims({
|
|
346
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_PASSWORD_KEY]: null,
|
|
347
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_RESET_LAST_COM_DATE_KEY]: null
|
|
348
|
+
});
|
|
349
|
+
return record;
|
|
350
|
+
}
|
|
351
|
+
async updateUser(template) {
|
|
352
|
+
return this.service.auth.updateUser(this.uid, template);
|
|
353
|
+
}
|
|
354
|
+
async loadRoles() {
|
|
355
|
+
const claims = await this.loadClaims();
|
|
356
|
+
return this.service.readRoles(claims);
|
|
357
|
+
}
|
|
358
|
+
async addRoles(roles) {
|
|
359
|
+
const claims = this._claimsForRolesChange(roles);
|
|
360
|
+
return this.updateClaims(claims);
|
|
361
|
+
}
|
|
362
|
+
async removeRoles(roles) {
|
|
363
|
+
const baseClaims = this._claimsForRolesChange(roles);
|
|
364
|
+
const claims = {};
|
|
365
|
+
util.forEachKeyValue(baseClaims, {
|
|
366
|
+
forEach: ([key]) => {
|
|
367
|
+
claims[key] = null; // set null on every key
|
|
368
|
+
},
|
|
369
|
+
filter: util.KeyValueTypleValueFilter.NONE // don't skip any key/value
|
|
370
|
+
});
|
|
371
|
+
return this.updateClaims(claims);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Sets the claims using the input roles and roles set.
|
|
375
|
+
*
|
|
376
|
+
* All other claims are cleared.
|
|
377
|
+
*
|
|
378
|
+
* Use the claimsToRetain input to retain other claims that are outside of the roles.
|
|
379
|
+
*
|
|
380
|
+
* @param roles
|
|
381
|
+
* @param claimsToRetain
|
|
382
|
+
* @returns
|
|
383
|
+
*/
|
|
384
|
+
async setRoles(roles, claimsToRetain) {
|
|
385
|
+
const claims = {
|
|
386
|
+
...claimsToRetain,
|
|
387
|
+
...this._claimsForRolesChange(Array.from(roles))
|
|
388
|
+
};
|
|
389
|
+
return this.setClaims(claims);
|
|
390
|
+
}
|
|
391
|
+
_claimsForRolesChange(roles) {
|
|
392
|
+
// filter null/undefined since the claims will contain null values for claims that are not related.
|
|
393
|
+
return util.filterNullAndUndefinedValues(this.service.claimsForRoles(util.asSet(roles)));
|
|
394
|
+
}
|
|
395
|
+
loadClaims() {
|
|
396
|
+
return this.loadRecord().then(x => x.customClaims ?? {});
|
|
397
|
+
}
|
|
398
|
+
async updateClaims(claims) {
|
|
399
|
+
const currentClaims = await this.loadClaims();
|
|
400
|
+
let newClaims;
|
|
401
|
+
if (currentClaims) {
|
|
402
|
+
newClaims = {
|
|
403
|
+
...currentClaims,
|
|
404
|
+
...util.filterUndefinedValues(claims, false)
|
|
405
|
+
};
|
|
406
|
+
newClaims = util.filterNullAndUndefinedValues(newClaims);
|
|
407
|
+
} else {
|
|
408
|
+
newClaims = claims;
|
|
409
|
+
}
|
|
410
|
+
return this.setClaims(newClaims);
|
|
411
|
+
}
|
|
412
|
+
clearClaims() {
|
|
413
|
+
return this.setClaims(null);
|
|
414
|
+
}
|
|
415
|
+
setClaims(claims) {
|
|
416
|
+
return this.service.auth.setCustomUserClaims(this.uid, claims).then(() => {
|
|
417
|
+
this._loadRecord.reset(); // reset the cache
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
class AbstractFirebaseServerAuthContext {
|
|
422
|
+
constructor(service, context) {
|
|
423
|
+
this._service = void 0;
|
|
424
|
+
this._context = void 0;
|
|
425
|
+
this._authRoles = util.cachedGetter(() => this.service.readRoles(this.claims));
|
|
426
|
+
this._isAdmin = util.cachedGetter(() => this.service.isAdminInRoles(this._authRoles()));
|
|
427
|
+
this._hasSignedTos = util.cachedGetter(() => this.service.hasSignedTosInRoles(this._authRoles()));
|
|
428
|
+
this._userContext = util.cachedGetter(() => this.service.userContext(this.context.auth.uid));
|
|
429
|
+
this._service = service;
|
|
430
|
+
this._context = context;
|
|
431
|
+
}
|
|
432
|
+
get service() {
|
|
433
|
+
return this._service;
|
|
434
|
+
}
|
|
435
|
+
get context() {
|
|
436
|
+
return this._context;
|
|
437
|
+
}
|
|
438
|
+
get userContext() {
|
|
439
|
+
return this._userContext();
|
|
440
|
+
}
|
|
441
|
+
get isAdmin() {
|
|
442
|
+
return this._isAdmin();
|
|
443
|
+
}
|
|
444
|
+
get hasSignedTos() {
|
|
445
|
+
return this._hasSignedTos();
|
|
446
|
+
}
|
|
447
|
+
get authRoles() {
|
|
448
|
+
return this._authRoles();
|
|
449
|
+
}
|
|
450
|
+
get token() {
|
|
451
|
+
return this.context.auth.token;
|
|
452
|
+
}
|
|
453
|
+
get claims() {
|
|
454
|
+
return this.context.auth.token;
|
|
455
|
+
}
|
|
456
|
+
// MARK: FirebaseServerAuthUserContext
|
|
457
|
+
get uid() {
|
|
458
|
+
return this.userContext.uid;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* 1 hour
|
|
463
|
+
*/
|
|
464
|
+
const DEFAULT_SETUP_COM_THROTTLE_TIME = date.hoursToMs(1);
|
|
465
|
+
function userContextFromUid(authService, userContextOrUid) {
|
|
466
|
+
const userContext = typeof userContextOrUid === 'string' ? authService.userContext(userContextOrUid) : userContextOrUid;
|
|
467
|
+
return userContext;
|
|
468
|
+
}
|
|
469
|
+
class AbstractFirebaseServerNewUserService {
|
|
470
|
+
constructor(authService) {
|
|
471
|
+
this._authService = void 0;
|
|
472
|
+
this.setupThrottleTime = DEFAULT_SETUP_COM_THROTTLE_TIME;
|
|
473
|
+
this._authService = authService;
|
|
474
|
+
}
|
|
475
|
+
get authService() {
|
|
476
|
+
return this._authService;
|
|
477
|
+
}
|
|
478
|
+
async initializeNewUser(input) {
|
|
479
|
+
const {
|
|
480
|
+
uid,
|
|
481
|
+
email,
|
|
482
|
+
phone,
|
|
483
|
+
sendSetupContent,
|
|
484
|
+
sendSetupContentIfUserExists,
|
|
485
|
+
sendSetupDetailsOnce,
|
|
486
|
+
sendSetupIgnoreThrottle,
|
|
487
|
+
sendSetupThrowErrors,
|
|
488
|
+
data,
|
|
489
|
+
sendDetailsInTestEnvironment
|
|
490
|
+
} = input;
|
|
491
|
+
let userRecordPromise;
|
|
492
|
+
if (uid) {
|
|
493
|
+
userRecordPromise = this.authService.auth.getUser(uid);
|
|
494
|
+
} else if (email) {
|
|
495
|
+
userRecordPromise = this.authService.auth.getUserByEmail(email);
|
|
496
|
+
} else if (phone) {
|
|
497
|
+
userRecordPromise = this.authService.auth.getUserByPhoneNumber(phone);
|
|
498
|
+
} else {
|
|
499
|
+
throw new Error('email or phone is required to initialize a new user.');
|
|
500
|
+
}
|
|
501
|
+
let userRecord = await getAuthUserOrUndefined(userRecordPromise);
|
|
502
|
+
let userRecordId;
|
|
503
|
+
let createdUser = false;
|
|
504
|
+
if (!userRecord) {
|
|
505
|
+
const createResult = await this.createNewUser(input);
|
|
506
|
+
// add the setup password to the user's credentials
|
|
507
|
+
const userContext = this.authService.userContext(createResult.user.uid);
|
|
508
|
+
await this.addNewUserSetupClaims(userContext, createResult.password);
|
|
509
|
+
createdUser = true;
|
|
510
|
+
userRecordId = userContext.uid;
|
|
511
|
+
userRecord = await userContext.loadRecord();
|
|
512
|
+
} else {
|
|
513
|
+
userRecordId = userRecord.uid;
|
|
514
|
+
}
|
|
515
|
+
// send content if necessary
|
|
516
|
+
if (createdUser && sendSetupContent === true || sendSetupContentIfUserExists) {
|
|
517
|
+
const sentEmail = await this.sendSetupContent(userRecordId, {
|
|
518
|
+
data,
|
|
519
|
+
sendSetupDetailsOnce,
|
|
520
|
+
ignoreSendThrottleTime: sendSetupIgnoreThrottle,
|
|
521
|
+
throwErrors: sendSetupThrowErrors,
|
|
522
|
+
sendDetailsInTestEnvironment
|
|
523
|
+
});
|
|
524
|
+
// reload the user record
|
|
525
|
+
if (sentEmail) {
|
|
526
|
+
const userContext = this.authService.userContext(userRecordId);
|
|
527
|
+
userRecord = await userContext.loadRecord();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return userRecord;
|
|
531
|
+
}
|
|
532
|
+
async addNewUserSetupClaims(userContextOrUid, setupPassword) {
|
|
533
|
+
const password = setupPassword ?? this.generateRandomSetupPassword();
|
|
534
|
+
const userContext = userContextFromUid(this.authService, userContextOrUid);
|
|
535
|
+
await userContext.updateClaims({
|
|
536
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY]: password
|
|
537
|
+
});
|
|
538
|
+
return userContext;
|
|
539
|
+
}
|
|
540
|
+
async sendSetupContent(userContextOrUid, config) {
|
|
541
|
+
const setupDetails = await this.loadSetupDetails(userContextOrUid, config);
|
|
542
|
+
let sentContent = false;
|
|
543
|
+
if (setupDetails) {
|
|
544
|
+
const {
|
|
545
|
+
setupCommunicationAt
|
|
546
|
+
} = setupDetails.claims;
|
|
547
|
+
const hasSentCommunication = Boolean(setupCommunicationAt);
|
|
548
|
+
if (config?.sendSetupDetailsOnce && hasSentCommunication) {
|
|
549
|
+
// do not send.
|
|
550
|
+
if (config?.throwErrors) {
|
|
551
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsSendOnceError();
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
const lastSentAt = setupCommunicationAt ? new Date(setupCommunicationAt) : undefined;
|
|
555
|
+
const sendIsThrottled = hasSentCommunication && !config?.ignoreSendThrottleTime && util.isThrottled(this.setupThrottleTime, lastSentAt);
|
|
556
|
+
if (!sendIsThrottled) {
|
|
557
|
+
await this.sendSetupContentToUser(setupDetails);
|
|
558
|
+
await this.updateSetupContentSentTime(setupDetails);
|
|
559
|
+
sentContent = true;
|
|
560
|
+
} else if (config?.throwErrors) {
|
|
561
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsThrottleError(lastSentAt);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
} else if (config?.throwErrors) {
|
|
565
|
+
throw new FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError();
|
|
566
|
+
}
|
|
567
|
+
return sentContent;
|
|
568
|
+
}
|
|
569
|
+
async loadSetupDetails(userContextOrUid, config) {
|
|
570
|
+
const userContext = userContextFromUid(this.authService, userContextOrUid);
|
|
571
|
+
const userExists = await userContext.exists();
|
|
572
|
+
let details;
|
|
573
|
+
if (userExists) {
|
|
574
|
+
details = await this.loadSetupDetailsForUserContext(userContext, config);
|
|
575
|
+
}
|
|
576
|
+
return details;
|
|
577
|
+
}
|
|
578
|
+
async loadSetupDetailsForUserContext(userContext, config) {
|
|
579
|
+
let details;
|
|
580
|
+
const {
|
|
581
|
+
setupPassword,
|
|
582
|
+
setupCommunicationAt
|
|
583
|
+
} = await userContext.loadClaims();
|
|
584
|
+
if (setupPassword) {
|
|
585
|
+
details = {
|
|
586
|
+
userContext,
|
|
587
|
+
claims: {
|
|
588
|
+
setupPassword,
|
|
589
|
+
setupCommunicationAt
|
|
590
|
+
},
|
|
591
|
+
data: config?.data,
|
|
592
|
+
sendDetailsInTestEnvironment: config?.sendDetailsInTestEnvironment
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return details;
|
|
596
|
+
}
|
|
597
|
+
async updateSetupContentSentTime(details) {
|
|
598
|
+
const setupCommunicationAt = date.toISODateString(new Date());
|
|
599
|
+
await details.userContext.updateClaims({
|
|
600
|
+
setupCommunicationAt
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Update a user's claims to clear any setup-related content.
|
|
605
|
+
*
|
|
606
|
+
* Returns true if a user was updated.
|
|
607
|
+
*
|
|
608
|
+
* @param uid
|
|
609
|
+
*/
|
|
610
|
+
async markUserSetupAsComplete(uid) {
|
|
611
|
+
const userContext = this.authService.userContext(uid);
|
|
612
|
+
const userExists = await userContext.exists();
|
|
613
|
+
if (userExists) {
|
|
614
|
+
await this.updateClaimsToClearUser(userContext);
|
|
615
|
+
}
|
|
616
|
+
return userExists;
|
|
617
|
+
}
|
|
618
|
+
async createNewUser(input) {
|
|
619
|
+
const {
|
|
620
|
+
uid,
|
|
621
|
+
displayName,
|
|
622
|
+
email,
|
|
623
|
+
phone: phoneNumber,
|
|
624
|
+
setupPassword: inputPassword
|
|
625
|
+
} = input;
|
|
626
|
+
const password = inputPassword ?? this.generateRandomSetupPassword();
|
|
627
|
+
const user = await this.authService.auth.createUser({
|
|
628
|
+
uid,
|
|
629
|
+
displayName,
|
|
630
|
+
email,
|
|
631
|
+
phoneNumber,
|
|
632
|
+
password
|
|
633
|
+
});
|
|
634
|
+
return {
|
|
635
|
+
user,
|
|
636
|
+
password
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
generateRandomSetupPassword() {
|
|
640
|
+
return `${DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR()}`;
|
|
641
|
+
}
|
|
642
|
+
async updateClaimsToClearUser(userContext) {
|
|
643
|
+
await userContext.updateClaims({
|
|
644
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY]: null,
|
|
645
|
+
[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_LAST_COM_DATE_KEY]: null
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
class NoSetupContentFirebaseServerNewUserService extends AbstractFirebaseServerNewUserService {
|
|
650
|
+
async sendSetupContentToUser(user) {
|
|
651
|
+
// send nothing.
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* FirebaseServer auth service that provides accessors to auth-related components.
|
|
656
|
+
*/
|
|
657
|
+
class FirebaseServerAuthService {}
|
|
658
|
+
/**
|
|
659
|
+
* Abstract FirebaseServerAuthService implementation.
|
|
660
|
+
*/
|
|
661
|
+
class AbstractFirebaseServerAuthService {
|
|
662
|
+
constructor(auth) {
|
|
663
|
+
this._auth = void 0;
|
|
664
|
+
this._auth = auth;
|
|
665
|
+
}
|
|
666
|
+
get auth() {
|
|
667
|
+
return this._auth;
|
|
668
|
+
}
|
|
669
|
+
context(context) {
|
|
670
|
+
assertIsContextWithAuthData(context);
|
|
671
|
+
return this._context(context);
|
|
672
|
+
}
|
|
673
|
+
isAdmin(claims) {
|
|
674
|
+
return this.isAdminInRoles(this.readRoles(claims));
|
|
675
|
+
}
|
|
676
|
+
isAdminInRoles(roles) {
|
|
677
|
+
return roles.has(util.AUTH_ADMIN_ROLE);
|
|
678
|
+
}
|
|
679
|
+
hasSignedTos(claims) {
|
|
680
|
+
return this.hasSignedTosInRoles(this.readRoles(claims));
|
|
681
|
+
}
|
|
682
|
+
hasSignedTosInRoles(roles) {
|
|
683
|
+
return roles.has(util.AUTH_TOS_SIGNED_ROLE);
|
|
684
|
+
}
|
|
685
|
+
newUser() {
|
|
686
|
+
return new NoSetupContentFirebaseServerNewUserService(this);
|
|
687
|
+
}
|
|
688
|
+
authContextInfo(context) {
|
|
689
|
+
const {
|
|
690
|
+
auth
|
|
691
|
+
} = context;
|
|
692
|
+
let result;
|
|
693
|
+
if (auth) {
|
|
694
|
+
const _roles = util.cachedGetter(() => this.readRoles(auth.token));
|
|
695
|
+
const getClaims = () => auth.token;
|
|
696
|
+
result = {
|
|
697
|
+
uid: auth.uid,
|
|
698
|
+
isAdmin: () => this.isAdminInRoles(_roles()),
|
|
699
|
+
getClaims,
|
|
700
|
+
getAuthRoles: _roles,
|
|
701
|
+
token: firebaseAuthTokenFromDecodedIdToken(auth.token)
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
return result;
|
|
705
|
+
}
|
|
706
|
+
authDetailsForRecord(record) {
|
|
707
|
+
return {
|
|
708
|
+
uid: record.uid,
|
|
709
|
+
email: record.email,
|
|
710
|
+
emailVerified: record.emailVerified,
|
|
711
|
+
phoneNumber: record.phoneNumber,
|
|
712
|
+
disabled: record.disabled,
|
|
713
|
+
displayName: record.displayName,
|
|
714
|
+
photoURL: record.photoURL,
|
|
715
|
+
creationTime: record.metadata.creationTime ? new Date(record.metadata.creationTime).toISOString() : undefined,
|
|
716
|
+
lastSignInTime: record.metadata.lastSignInTime ? new Date(record.metadata.lastSignInTime).toISOString() : undefined,
|
|
717
|
+
lastRefreshTime: record.metadata.lastRefreshTime ? new Date(record.metadata.lastRefreshTime).toISOString() : undefined
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
class FirebaseServerEnvService {}
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Creates UpdateData corresponding to the input increment update.
|
|
726
|
+
*
|
|
727
|
+
* @param input
|
|
728
|
+
* @returns
|
|
729
|
+
*/
|
|
730
|
+
function firestoreServerIncrementUpdateToUpdateData(input) {
|
|
731
|
+
return util.mapObjectMap(input, incrementValue => {
|
|
732
|
+
return firestore.FieldValue.increment(incrementValue ?? 0);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Creates UpdateData corresponding to the input array update.
|
|
738
|
+
*
|
|
739
|
+
* @param input
|
|
740
|
+
* @returns
|
|
741
|
+
*/
|
|
742
|
+
function firestoreServerArrayUpdateToUpdateData(input) {
|
|
743
|
+
const union = input?.union;
|
|
744
|
+
const remove = input?.remove;
|
|
745
|
+
function createUpdatesWithArrayFunction(fieldUpdate, arrayUpdateFunction) {
|
|
746
|
+
let result;
|
|
747
|
+
if (fieldUpdate) {
|
|
748
|
+
result = util.mapObjectMap(fieldUpdate, arrayUpdate => {
|
|
749
|
+
let result;
|
|
750
|
+
if (arrayUpdate) {
|
|
751
|
+
result = arrayUpdateFunction(...arrayUpdate); // use spread operator to insert each value as an argument, as "nested arrays" are not allowed in the Firestore
|
|
752
|
+
}
|
|
753
|
+
return result;
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
return result;
|
|
757
|
+
}
|
|
758
|
+
const updateData = {
|
|
759
|
+
...createUpdatesWithArrayFunction(union, firestore.FieldValue.arrayUnion),
|
|
760
|
+
...createUpdatesWithArrayFunction(remove, firestore.FieldValue.arrayRemove)
|
|
761
|
+
};
|
|
762
|
+
return updateData;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
// MARK: Accessor
|
|
766
|
+
/**
|
|
767
|
+
* FirestoreDocumentDataAccessor implementation for a batch.
|
|
768
|
+
*/
|
|
769
|
+
class WriteBatchFirestoreDocumentDataAccessor {
|
|
770
|
+
constructor(batch, documentRef) {
|
|
771
|
+
this.documentRef = void 0;
|
|
772
|
+
this._batch = void 0;
|
|
773
|
+
this.documentRef = documentRef;
|
|
774
|
+
this._batch = batch;
|
|
775
|
+
}
|
|
776
|
+
get batch() {
|
|
777
|
+
return this._batch;
|
|
778
|
+
}
|
|
779
|
+
stream() {
|
|
780
|
+
return rxjs.from(this.get()); // todo
|
|
781
|
+
}
|
|
782
|
+
create(data) {
|
|
783
|
+
this.batch.create(this.documentRef, data);
|
|
784
|
+
return Promise.resolve();
|
|
785
|
+
}
|
|
786
|
+
exists() {
|
|
787
|
+
return this.get().then(x => x.exists);
|
|
788
|
+
}
|
|
789
|
+
get() {
|
|
790
|
+
return this.documentRef.get();
|
|
791
|
+
}
|
|
792
|
+
getWithConverter(converter) {
|
|
793
|
+
return this.documentRef.withConverter(converter).get();
|
|
794
|
+
}
|
|
795
|
+
delete(params) {
|
|
796
|
+
this.batch.delete(this.documentRef, params?.precondition);
|
|
797
|
+
return Promise.resolve();
|
|
798
|
+
}
|
|
799
|
+
set(data) {
|
|
800
|
+
this.batch.set(this.documentRef, data);
|
|
801
|
+
return Promise.resolve();
|
|
802
|
+
}
|
|
803
|
+
increment(data, params) {
|
|
804
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
805
|
+
}
|
|
806
|
+
arrayUpdate(data, params) {
|
|
807
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
808
|
+
}
|
|
809
|
+
update(data, params) {
|
|
810
|
+
if (params?.precondition != null) {
|
|
811
|
+
this.batch.update(this.documentRef, data, params?.precondition);
|
|
812
|
+
} else {
|
|
813
|
+
this.batch.update(this.documentRef, data);
|
|
814
|
+
}
|
|
815
|
+
return Promise.resolve();
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Creates a new FirestoreDocumentDataAccessorFactory for a Batch.
|
|
820
|
+
*
|
|
821
|
+
* @param batch
|
|
822
|
+
* @returns
|
|
823
|
+
*/
|
|
824
|
+
function writeBatchAccessorFactory(writeBatch) {
|
|
825
|
+
return {
|
|
826
|
+
accessorFor: ref => new WriteBatchFirestoreDocumentDataAccessor(writeBatch, ref)
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
// MARK: Context
|
|
830
|
+
class WriteBatchFirestoreDocumentContext {
|
|
831
|
+
constructor(batch) {
|
|
832
|
+
this._batch = void 0;
|
|
833
|
+
this.contextType = firebase.FirestoreDocumentContextType.BATCH;
|
|
834
|
+
this.accessorFactory = void 0;
|
|
835
|
+
this._batch = batch;
|
|
836
|
+
this.accessorFactory = writeBatchAccessorFactory(batch);
|
|
837
|
+
}
|
|
838
|
+
get batch() {
|
|
839
|
+
return this._batch;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
function writeBatchDocumentContext(batch) {
|
|
843
|
+
return new WriteBatchFirestoreDocumentContext(batch);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// MARK: Accessor
|
|
847
|
+
class DefaultFirestoreDocumentDataAccessor {
|
|
848
|
+
constructor(documentRef) {
|
|
849
|
+
this._documentRef = void 0;
|
|
850
|
+
this._documentRef = documentRef;
|
|
851
|
+
}
|
|
852
|
+
get documentRef() {
|
|
853
|
+
return this._documentRef;
|
|
854
|
+
}
|
|
855
|
+
stream() {
|
|
856
|
+
return firebase.streamFromOnSnapshot(({
|
|
857
|
+
next,
|
|
858
|
+
error
|
|
859
|
+
}) => this.documentRef.onSnapshot(next, error));
|
|
860
|
+
}
|
|
861
|
+
create(data) {
|
|
862
|
+
return this.documentRef.create(data);
|
|
863
|
+
}
|
|
864
|
+
exists() {
|
|
865
|
+
return this.get().then(x => x.exists);
|
|
866
|
+
}
|
|
867
|
+
get() {
|
|
868
|
+
return this.documentRef.get();
|
|
869
|
+
}
|
|
870
|
+
getWithConverter(converter) {
|
|
871
|
+
return this.documentRef.withConverter(converter).get();
|
|
872
|
+
}
|
|
873
|
+
delete(params) {
|
|
874
|
+
return this.documentRef.delete(params?.precondition);
|
|
875
|
+
}
|
|
876
|
+
set(data, options) {
|
|
877
|
+
return options ? this.documentRef.set(data, options) : this.documentRef.set(data);
|
|
878
|
+
}
|
|
879
|
+
increment(data, params) {
|
|
880
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
881
|
+
}
|
|
882
|
+
arrayUpdate(data, params) {
|
|
883
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
884
|
+
}
|
|
885
|
+
update(data, params) {
|
|
886
|
+
return params?.precondition ? this.documentRef.update(data, params.precondition) : this.documentRef.update(data);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
function defaultFirestoreAccessorFactory() {
|
|
890
|
+
return {
|
|
891
|
+
accessorFor: ref => new DefaultFirestoreDocumentDataAccessor(ref)
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
// MARK: Context
|
|
895
|
+
function defaultFirestoreDocumentContext() {
|
|
896
|
+
return {
|
|
897
|
+
contextType: firebase.FirestoreDocumentContextType.NONE,
|
|
898
|
+
accessorFactory: defaultFirestoreAccessorFactory()
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// MARK: Accessor
|
|
903
|
+
/**
|
|
904
|
+
* FirestoreDocumentDataAccessor implementation for a transaction.
|
|
905
|
+
*/
|
|
906
|
+
class TransactionFirestoreDocumentDataAccessor {
|
|
907
|
+
constructor(transaction, documentRef) {
|
|
908
|
+
this._transaction = void 0;
|
|
909
|
+
this._documentRef = void 0;
|
|
910
|
+
this._transaction = transaction;
|
|
911
|
+
this._documentRef = documentRef;
|
|
912
|
+
}
|
|
913
|
+
get transaction() {
|
|
914
|
+
return this._transaction;
|
|
915
|
+
}
|
|
916
|
+
get documentRef() {
|
|
917
|
+
return this._documentRef;
|
|
918
|
+
}
|
|
919
|
+
stream() {
|
|
920
|
+
return rxjs.from(this.get());
|
|
921
|
+
}
|
|
922
|
+
create(data) {
|
|
923
|
+
this.transaction.create(this.documentRef, data);
|
|
924
|
+
return Promise.resolve();
|
|
925
|
+
}
|
|
926
|
+
exists() {
|
|
927
|
+
return this.get().then(x => x.exists);
|
|
928
|
+
}
|
|
929
|
+
get() {
|
|
930
|
+
return this.transaction.get(this.documentRef);
|
|
931
|
+
}
|
|
932
|
+
getWithConverter(converter) {
|
|
933
|
+
return this.transaction.get(this.documentRef.withConverter(converter));
|
|
934
|
+
}
|
|
935
|
+
delete() {
|
|
936
|
+
this.transaction.delete(this.documentRef);
|
|
937
|
+
return Promise.resolve();
|
|
938
|
+
}
|
|
939
|
+
set(data, options) {
|
|
940
|
+
this.transaction.set(this.documentRef, data, options);
|
|
941
|
+
return Promise.resolve();
|
|
942
|
+
}
|
|
943
|
+
increment(data, params) {
|
|
944
|
+
return this.update(firestoreServerIncrementUpdateToUpdateData(data), params);
|
|
945
|
+
}
|
|
946
|
+
arrayUpdate(data, params) {
|
|
947
|
+
return this.update(firestoreServerArrayUpdateToUpdateData(data), params);
|
|
948
|
+
}
|
|
949
|
+
update(data, params) {
|
|
950
|
+
if (params?.precondition) {
|
|
951
|
+
this.transaction.update(this.documentRef, data, params?.precondition);
|
|
952
|
+
} else {
|
|
953
|
+
this.transaction.update(this.documentRef, data);
|
|
954
|
+
}
|
|
955
|
+
return Promise.resolve();
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Creates a new FirestoreDocumentDataAccessorFactory for a Transaction.
|
|
960
|
+
*
|
|
961
|
+
* @param transaction
|
|
962
|
+
* @returns
|
|
963
|
+
*/
|
|
964
|
+
function transactionAccessorFactory(transaction) {
|
|
965
|
+
return {
|
|
966
|
+
accessorFor: ref => new TransactionFirestoreDocumentDataAccessor(transaction, ref)
|
|
967
|
+
};
|
|
968
|
+
}
|
|
969
|
+
// MARK: Context
|
|
970
|
+
class TransactionFirestoreDocumentContext {
|
|
971
|
+
constructor(transaction) {
|
|
972
|
+
this._transaction = void 0;
|
|
973
|
+
this.contextType = firebase.FirestoreDocumentContextType.TRANSACTION;
|
|
974
|
+
this.accessorFactory = void 0;
|
|
975
|
+
this._transaction = transaction;
|
|
976
|
+
this.accessorFactory = transactionAccessorFactory(transaction);
|
|
977
|
+
}
|
|
978
|
+
get transaction() {
|
|
979
|
+
return this._transaction;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function transactionDocumentContext(transaction) {
|
|
983
|
+
return new TransactionFirestoreDocumentContext(transaction);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function collectionRefForPath(start, path, pathSegments) {
|
|
987
|
+
let ref = start.collection(path);
|
|
988
|
+
if (pathSegments?.length) {
|
|
989
|
+
if (pathSegments?.length % 2 !== 0) {
|
|
990
|
+
throw new Error(`Invalid number of path segments provided for collection. Path: "${path}" + "${pathSegments}"`);
|
|
991
|
+
}
|
|
992
|
+
const batches = util.batch(pathSegments, 2); // batch to tuple [string, string]
|
|
993
|
+
batches.forEach(x => {
|
|
994
|
+
const [first, second] = x;
|
|
995
|
+
ref = ref.doc(first).collection(second);
|
|
996
|
+
});
|
|
997
|
+
}
|
|
998
|
+
return ref;
|
|
999
|
+
}
|
|
1000
|
+
function docRefForPath(start, path, pathSegments) {
|
|
1001
|
+
let doc = path ? start.doc(path) : start.doc();
|
|
1002
|
+
if (pathSegments?.length) {
|
|
1003
|
+
const batches = util.batch(pathSegments, 2); // batch to tuple [string, string]
|
|
1004
|
+
batches.forEach(x => {
|
|
1005
|
+
const [first, second] = x;
|
|
1006
|
+
const collection = doc.collection(first);
|
|
1007
|
+
doc = second ? collection.doc(second) : collection.doc();
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
return doc;
|
|
1011
|
+
}
|
|
1012
|
+
function googleCloudFirestoreAccessorDriver() {
|
|
1013
|
+
return {
|
|
1014
|
+
doc: (collection, path, ...pathSegments) => docRefForPath(collection, path, pathSegments),
|
|
1015
|
+
docAtPath: (firestore, fullPath) => firestore.doc(fullPath),
|
|
1016
|
+
collectionGroup: (firestore, collectionId) => firestore.collectionGroup(collectionId),
|
|
1017
|
+
collection: (firestore, path, ...pathSegments) => collectionRefForPath(firestore, path, pathSegments),
|
|
1018
|
+
subcollection: (document, path, ...pathSegments) => collectionRefForPath(document, path, pathSegments),
|
|
1019
|
+
transactionFactoryForFirestore: firestore => async fn => await firestore.runTransaction(fn),
|
|
1020
|
+
writeBatchFactoryForFirestore: firestore => () => firestore.batch(),
|
|
1021
|
+
defaultContextFactory: defaultFirestoreDocumentContext,
|
|
1022
|
+
transactionContextFactory: transactionDocumentContext,
|
|
1023
|
+
writeBatchContextFactory: writeBatchDocumentContext
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
const FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING = {
|
|
1028
|
+
[firebase.FIRESTORE_LIMIT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.limit(data.limit),
|
|
1029
|
+
[firebase.FIRESTORE_LIMIT_TO_LAST_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.limitToLast(data.limit),
|
|
1030
|
+
[firebase.FIRESTORE_ORDER_BY_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.orderBy(data.fieldPath, data.directionStr),
|
|
1031
|
+
[firebase.FIRESTORE_ORDER_BY_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.orderBy(firestore.FieldPath.documentId(), data.directionStr),
|
|
1032
|
+
[firebase.FIRESTORE_WHERE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.where(data.fieldPath, data.opStr, data.value),
|
|
1033
|
+
[firebase.FIRESTORE_WHERE_DOCUMENT_ID_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.where(firestore.FieldPath.documentId(), data.opStr, data.value),
|
|
1034
|
+
[firebase.FIRESTORE_OFFSET_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.offset(data.offset),
|
|
1035
|
+
[firebase.FIRESTORE_START_AT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAt(data.snapshot),
|
|
1036
|
+
[firebase.FIRESTORE_START_AT_VALUE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAt(...data.fieldValues),
|
|
1037
|
+
[firebase.FIRESTORE_START_AFTER_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.startAfter(data.snapshot),
|
|
1038
|
+
[firebase.FIRESTORE_END_AT_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endAt(data.snapshot),
|
|
1039
|
+
[firebase.FIRESTORE_END_AT_VALUE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endAt(...data.fieldValues),
|
|
1040
|
+
[firebase.FIRESTORE_END_BEFORE_QUERY_CONSTRAINT_TYPE]: (builder, data) => builder.endBefore(data.snapshot)
|
|
1041
|
+
};
|
|
1042
|
+
function firestoreClientQueryConstraintFunctionsDriver() {
|
|
1043
|
+
return firebase.makeFirestoreQueryConstraintFunctionsDriver({
|
|
1044
|
+
mapping: FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING,
|
|
1045
|
+
init: query => query,
|
|
1046
|
+
build: query => query,
|
|
1047
|
+
documentIdFieldPath: () => firestore.FieldPath.documentId()
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
function googleCloudFirestoreQueryDriver() {
|
|
1051
|
+
return {
|
|
1052
|
+
...firestoreClientQueryConstraintFunctionsDriver(),
|
|
1053
|
+
countDocs(query) {
|
|
1054
|
+
return query.count().get().then(x => x.data().count);
|
|
1055
|
+
},
|
|
1056
|
+
getDocs(query, transaction) {
|
|
1057
|
+
let result;
|
|
1058
|
+
if (transaction) {
|
|
1059
|
+
result = transaction.get(query);
|
|
1060
|
+
} else {
|
|
1061
|
+
result = query.get();
|
|
1062
|
+
}
|
|
1063
|
+
return result;
|
|
1064
|
+
},
|
|
1065
|
+
streamDocs(query) {
|
|
1066
|
+
return firebase.streamFromOnSnapshot(({
|
|
1067
|
+
next,
|
|
1068
|
+
error
|
|
1069
|
+
}) => query.onSnapshot(next, error));
|
|
1070
|
+
}
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function googleCloudFirestoreDrivers() {
|
|
1075
|
+
return {
|
|
1076
|
+
firestoreDriverIdentifier: '@google-cloud/firestore',
|
|
1077
|
+
firestoreDriverType: 'production',
|
|
1078
|
+
firestoreAccessorDriver: googleCloudFirestoreAccessorDriver(),
|
|
1079
|
+
firestoreQueryDriver: googleCloudFirestoreQueryDriver()
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* Creates a FirestoreContextFactory that uses the @'@google-cloud/firestore package.
|
|
1085
|
+
*/
|
|
1086
|
+
const googleCloudFirestoreContextFactory = firebase.firestoreContextFactory(googleCloudFirestoreDrivers());
|
|
1087
|
+
|
|
1088
|
+
function assertContextHasAuth(context) {
|
|
1089
|
+
if (!isContextWithAuthData(context)) {
|
|
1090
|
+
throw unauthenticatedContextHasNoUidError();
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
/**
|
|
1094
|
+
* Attempts to load data from the document. A modelNotAvailableError is thrown if the snapshot data is null/undefined (the document does not exist).
|
|
1095
|
+
*
|
|
1096
|
+
* @param document
|
|
1097
|
+
* @param message
|
|
1098
|
+
* @returns
|
|
1099
|
+
*/
|
|
1100
|
+
async function assertSnapshotData(document, message) {
|
|
1101
|
+
const data = await document.snapshotData();
|
|
1102
|
+
if (data == null) {
|
|
1103
|
+
throw modelNotAvailableError({
|
|
1104
|
+
message: message ?? `The ${document.modelType} was unavailable.`
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1107
|
+
return data;
|
|
1108
|
+
}
|
|
1109
|
+
/**
|
|
1110
|
+
* Convenience function for assertSnapshotData that also attaches the id and key of the document to the data.
|
|
1111
|
+
*
|
|
1112
|
+
* @param document
|
|
1113
|
+
* @param message
|
|
1114
|
+
* @returns
|
|
1115
|
+
*/
|
|
1116
|
+
async function assertSnapshotDataWithKey(document, message) {
|
|
1117
|
+
const data = await assertSnapshotData(document, message);
|
|
1118
|
+
return firebase.setIdAndKeyFromKeyIdRefOnDocumentData(data, document);
|
|
1119
|
+
}
|
|
1120
|
+
/**
|
|
1121
|
+
* Asserts that the document exists. A modelNotAvailableError is thrown if the document does not exist.
|
|
1122
|
+
*
|
|
1123
|
+
* @param document
|
|
1124
|
+
* @param message
|
|
1125
|
+
* @returns
|
|
1126
|
+
*/
|
|
1127
|
+
async function assertDocumentExists(document, message) {
|
|
1128
|
+
const exists = await document.exists();
|
|
1129
|
+
if (!exists) {
|
|
1130
|
+
throw documentModelNotAvailableError(document, message);
|
|
1131
|
+
}
|
|
1132
|
+
}
|
|
1133
|
+
/**
|
|
1134
|
+
* Error thrown by assertDocumentExists().
|
|
1135
|
+
*
|
|
1136
|
+
* @param document
|
|
1137
|
+
* @param message
|
|
1138
|
+
* @returns
|
|
1139
|
+
*/
|
|
1140
|
+
function documentModelNotAvailableError(document, message) {
|
|
1141
|
+
return modelNotAvailableError({
|
|
1142
|
+
message: message ?? `The ${document.modelType} was unavailable.`
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
const PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE = 'PHONE_NUMBER_ALREADY_EXISTS';
|
|
1147
|
+
function phoneNumberAlreadyExistsError() {
|
|
1148
|
+
return preconditionConflictError({
|
|
1149
|
+
code: PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE,
|
|
1150
|
+
message: 'This phone number already exists in the system.'
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function handleFirebaseAuthError(e, handleUnknownCode) {
|
|
1154
|
+
handleFirebaseError(e, firebaseError => {
|
|
1155
|
+
switch (firebaseError.code) {
|
|
1156
|
+
case 'auth/phone-number-already-exists':
|
|
1157
|
+
throw phoneNumberAlreadyExistsError();
|
|
1158
|
+
default:
|
|
1159
|
+
handleUnknownCode?.(firebaseError);
|
|
1160
|
+
break;
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
/******************************************************************************
|
|
1166
|
+
Copyright (c) Microsoft Corporation.
|
|
1167
|
+
|
|
1168
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
1169
|
+
purpose with or without fee is hereby granted.
|
|
1170
|
+
|
|
1171
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
1172
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
1173
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
1174
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
1175
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
1176
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
1177
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
1178
|
+
***************************************************************************** */
|
|
1179
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
1180
|
+
|
|
1181
|
+
|
|
1182
|
+
function __decorate(decorators, target, key, desc) {
|
|
1183
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1184
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1185
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
1186
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function __param(paramIndex, decorator) {
|
|
1190
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
function __metadata(metadataKey, metadataValue) {
|
|
1194
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
1198
|
+
var e = new Error(message);
|
|
1199
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
// MARK: Tokens
|
|
1203
|
+
/**
|
|
1204
|
+
* Nest Injection Token to access the
|
|
1205
|
+
*/
|
|
1206
|
+
const FIREBASE_APP_TOKEN = 'FIREBASE_APP_TOKEN';
|
|
1207
|
+
// MARK: Firebase Admin Provider
|
|
1208
|
+
function firebaseServerAppTokenProvider(useFactory) {
|
|
1209
|
+
return {
|
|
1210
|
+
provide: FIREBASE_APP_TOKEN,
|
|
1211
|
+
useFactory
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// MARK: Tokens
|
|
1216
|
+
/**
|
|
1217
|
+
* Token to access the Auth for the app.
|
|
1218
|
+
*/
|
|
1219
|
+
const FIREBASE_AUTH_TOKEN = 'FIREBASE_AUTH_TOKEN';
|
|
1220
|
+
/**
|
|
1221
|
+
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
1222
|
+
*/
|
|
1223
|
+
exports.FirebaseServerAuthModule = class FirebaseServerAuthModule {};
|
|
1224
|
+
exports.FirebaseServerAuthModule = __decorate([common.Module({
|
|
1225
|
+
providers: [{
|
|
1226
|
+
provide: FIREBASE_AUTH_TOKEN,
|
|
1227
|
+
useFactory: app => app.auth(),
|
|
1228
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
1229
|
+
}],
|
|
1230
|
+
exports: [FIREBASE_AUTH_TOKEN]
|
|
1231
|
+
})], exports.FirebaseServerAuthModule);
|
|
1232
|
+
function provideFirebaseServerAuthService(provider) {
|
|
1233
|
+
return [{
|
|
1234
|
+
...provider,
|
|
1235
|
+
inject: provider.inject ?? [FIREBASE_AUTH_TOKEN]
|
|
1236
|
+
}, {
|
|
1237
|
+
provide: FirebaseServerAuthService,
|
|
1238
|
+
useExisting: provider.provide
|
|
1239
|
+
}];
|
|
1240
|
+
}
|
|
1241
|
+
/**
|
|
1242
|
+
* Convenience function used to generate ModuleMetadata for an app's Auth related modules and FirebaseServerAuthService provider.
|
|
1243
|
+
*
|
|
1244
|
+
* @param provide
|
|
1245
|
+
* @param useFactory
|
|
1246
|
+
* @returns
|
|
1247
|
+
*/
|
|
1248
|
+
function firebaseServerAuthModuleMetadata(config) {
|
|
1249
|
+
return nestjs.mergeModuleMetadata({
|
|
1250
|
+
imports: [exports.FirebaseServerAuthModule],
|
|
1251
|
+
exports: [exports.FirebaseServerAuthModule, config.serviceProvider.provide],
|
|
1252
|
+
providers: provideFirebaseServerAuthService(config.serviceProvider)
|
|
1253
|
+
}, config);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
function assertIsAdminInRequest(request) {
|
|
1257
|
+
if (!isAdminInRequest(request)) {
|
|
1258
|
+
throw forbiddenError();
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
function isAdminInRequest(request) {
|
|
1262
|
+
return request.nest.authService.context(request).isAdmin;
|
|
1263
|
+
}
|
|
1264
|
+
function assertIsAdminOrTargetUserInRequestData(request, requireUid) {
|
|
1265
|
+
if (!isAdminOrTargetUserInRequestData(request, requireUid)) {
|
|
1266
|
+
throw forbiddenError();
|
|
1267
|
+
}
|
|
1268
|
+
return request.data.uid ?? request.auth?.uid;
|
|
1269
|
+
}
|
|
1270
|
+
function isAdminOrTargetUserInRequestData(request, requireUid = false) {
|
|
1271
|
+
const uid = request.data.uid;
|
|
1272
|
+
const authUid = request.auth?.uid;
|
|
1273
|
+
let isAdminOrTargetUser = true;
|
|
1274
|
+
if (requireUid && uid == null || uid != null && uid !== authUid) {
|
|
1275
|
+
isAdminOrTargetUser = request.nest.authService.context(request).isAdmin;
|
|
1276
|
+
}
|
|
1277
|
+
return isAdminOrTargetUser;
|
|
1278
|
+
}
|
|
1279
|
+
function assertHasSignedTosInRequest(request) {
|
|
1280
|
+
if (!hasSignedTosInRequest(request)) {
|
|
1281
|
+
throw forbiddenError({
|
|
1282
|
+
message: 'ToS has not been signed.'
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
function hasSignedTosInRequest(request) {
|
|
1287
|
+
return request.nest.authService.context(request).hasSignedTos;
|
|
1288
|
+
}
|
|
1289
|
+
function assertHasRolesInRequest(request, authRoles) {
|
|
1290
|
+
if (!hasAuthRolesInRequest(request, authRoles)) {
|
|
1291
|
+
throw forbiddenError({
|
|
1292
|
+
message: 'Missing required auth roles.',
|
|
1293
|
+
data: {
|
|
1294
|
+
roles: util.asArray(authRoles)
|
|
1295
|
+
}
|
|
1296
|
+
});
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
function hasAuthRolesInRequest(request, authRoles) {
|
|
1300
|
+
return util.containsAllValues(request.nest.authService.context(request).authRoles, authRoles);
|
|
1301
|
+
}
|
|
1302
|
+
/**
|
|
1303
|
+
* Returns true if the claims have a FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY claims value, indicating they are a newly invited user.
|
|
1304
|
+
*
|
|
1305
|
+
* This may be used to filter out new users that were not invited from finishing their onboarding.
|
|
1306
|
+
*
|
|
1307
|
+
* @param request
|
|
1308
|
+
*/
|
|
1309
|
+
function hasNewUserSetupPasswordInRequest(request) {
|
|
1310
|
+
const claims = request.nest.authService.context(request).claims;
|
|
1311
|
+
return claims[firebase.FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY] != null;
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* Creates a OnCallWithAuthorizedNestContext function for creating a model.
|
|
1316
|
+
*
|
|
1317
|
+
* @param map
|
|
1318
|
+
* @returns
|
|
1319
|
+
*/
|
|
1320
|
+
function onCallDevelopmentFunction(map, config = {}) {
|
|
1321
|
+
const {
|
|
1322
|
+
preAssert = () => undefined
|
|
1323
|
+
} = config;
|
|
1324
|
+
return request => {
|
|
1325
|
+
const specifier = request.data.specifier;
|
|
1326
|
+
const devFn = map[specifier];
|
|
1327
|
+
if (devFn) {
|
|
1328
|
+
preAssert({
|
|
1329
|
+
request,
|
|
1330
|
+
specifier
|
|
1331
|
+
});
|
|
1332
|
+
return devFn({
|
|
1333
|
+
...request,
|
|
1334
|
+
specifier,
|
|
1335
|
+
data: request.data.data
|
|
1336
|
+
});
|
|
1337
|
+
} else {
|
|
1338
|
+
throw developmentUnknownSpecifierError(specifier);
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
}
|
|
1342
|
+
function developmentUnknownSpecifierError(specifier) {
|
|
1343
|
+
return badRequestError(util.serverError({
|
|
1344
|
+
status: 400,
|
|
1345
|
+
code: 'UNKNOWN_SPECIFIER_ERROR',
|
|
1346
|
+
message: `Invalid specifier "${specifier}" to run.`,
|
|
1347
|
+
data: {
|
|
1348
|
+
specifier
|
|
1349
|
+
}
|
|
1350
|
+
}));
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1353
|
+
const NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE = 'NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION';
|
|
1354
|
+
function noRunNameSpecifiedForScheduledFunctionDevelopmentFunction() {
|
|
1355
|
+
return badRequestError({
|
|
1356
|
+
code: NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE,
|
|
1357
|
+
message: `Must specify run parameter.`
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
const UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE = 'UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME';
|
|
1361
|
+
function unknownScheduledFunctionDevelopmentFunctionName(name) {
|
|
1362
|
+
return badRequestError({
|
|
1363
|
+
code: UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE,
|
|
1364
|
+
message: `Unknown function with name "${name}"`,
|
|
1365
|
+
data: {
|
|
1366
|
+
name
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
}
|
|
1370
|
+
const UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE = 'UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE';
|
|
1371
|
+
function unknownScheduledFunctionDevelopmentFunctionType(type) {
|
|
1372
|
+
return badRequestError({
|
|
1373
|
+
code: UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE,
|
|
1374
|
+
message: `Unknown type "${type}"`,
|
|
1375
|
+
data: {
|
|
1376
|
+
type
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
function makeScheduledFunctionDevelopmentFunction(config) {
|
|
1382
|
+
const {
|
|
1383
|
+
allScheduledFunctions
|
|
1384
|
+
} = config;
|
|
1385
|
+
const getListValues = util.cachedGetter(() => {
|
|
1386
|
+
const result = [];
|
|
1387
|
+
util.forEachKeyValue(allScheduledFunctions, {
|
|
1388
|
+
forEach: x => {
|
|
1389
|
+
const [functionName, config] = x;
|
|
1390
|
+
result.push({
|
|
1391
|
+
name: functionName.toString()
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
});
|
|
1395
|
+
return result;
|
|
1396
|
+
});
|
|
1397
|
+
return async request => {
|
|
1398
|
+
const {
|
|
1399
|
+
data
|
|
1400
|
+
} = request;
|
|
1401
|
+
const {
|
|
1402
|
+
type
|
|
1403
|
+
} = data;
|
|
1404
|
+
switch (type) {
|
|
1405
|
+
case 'run':
|
|
1406
|
+
const targetRunName = data.run;
|
|
1407
|
+
if (!targetRunName) {
|
|
1408
|
+
throw noRunNameSpecifiedForScheduledFunctionDevelopmentFunction();
|
|
1409
|
+
}
|
|
1410
|
+
const targetFunction = allScheduledFunctions[targetRunName];
|
|
1411
|
+
if (!targetFunction) {
|
|
1412
|
+
throw unknownScheduledFunctionDevelopmentFunctionName(targetRunName);
|
|
1413
|
+
}
|
|
1414
|
+
try {
|
|
1415
|
+
await targetFunction._runNow();
|
|
1416
|
+
} catch (e) {
|
|
1417
|
+
console.error(`Failed manually running task "${targetRunName}".`, e);
|
|
1418
|
+
throw e;
|
|
1419
|
+
}
|
|
1420
|
+
return {
|
|
1421
|
+
type: 'run',
|
|
1422
|
+
success: true
|
|
1423
|
+
};
|
|
1424
|
+
case 'list':
|
|
1425
|
+
return {
|
|
1426
|
+
type: 'list',
|
|
1427
|
+
list: getListValues()
|
|
1428
|
+
};
|
|
1429
|
+
default:
|
|
1430
|
+
throw unknownScheduledFunctionDevelopmentFunctionType(type);
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function setNestContextOnRequest(makeNestContext, request) {
|
|
1436
|
+
request.nest = makeNestContext(request.nestApplication);
|
|
1437
|
+
return request;
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Wraps the input OnCallWithNestContext function to flag it as optional to have auth data.
|
|
1441
|
+
*
|
|
1442
|
+
* @param fn
|
|
1443
|
+
* @returns
|
|
1444
|
+
*/
|
|
1445
|
+
function optionalAuthContext(fn) {
|
|
1446
|
+
const fnWithOptionalAuth = request => fn(request);
|
|
1447
|
+
fnWithOptionalAuth._requireAuth = false;
|
|
1448
|
+
return fnWithOptionalAuth;
|
|
1449
|
+
}
|
|
1450
|
+
/**
|
|
1451
|
+
* Asserts that the input request has auth data if the inputOnCallWithAuthAwareNestRequireAuthRef object is flagged to require auth.
|
|
1452
|
+
*
|
|
1453
|
+
* @param fn
|
|
1454
|
+
* @param request
|
|
1455
|
+
*/
|
|
1456
|
+
function assertRequestRequiresAuthForFunction(fn, request) {
|
|
1457
|
+
if (fn._requireAuth !== false) {
|
|
1458
|
+
assertIsContextWithAuthData(request);
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
/**
|
|
1462
|
+
* Creates an OnCallWithNestContext wrapper that validates the input CallableContext to assert the context has auth data before entering the function.
|
|
1463
|
+
*
|
|
1464
|
+
* @param fn
|
|
1465
|
+
* @returns
|
|
1466
|
+
*/
|
|
1467
|
+
function inAuthContext(fn) {
|
|
1468
|
+
return request => {
|
|
1469
|
+
assertIsContextWithAuthData(request);
|
|
1470
|
+
return fn(request);
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function firebaseServerDevFunctions(config) {
|
|
1475
|
+
const {
|
|
1476
|
+
enabled,
|
|
1477
|
+
secure,
|
|
1478
|
+
nest,
|
|
1479
|
+
developerFunctionsMap,
|
|
1480
|
+
onCallFactory,
|
|
1481
|
+
allScheduledFunctions,
|
|
1482
|
+
disableDevelopmentScheduleFunction
|
|
1483
|
+
} = config;
|
|
1484
|
+
let dev;
|
|
1485
|
+
if (enabled) {
|
|
1486
|
+
const fullFunctionsMap = {
|
|
1487
|
+
...developerFunctionsMap
|
|
1488
|
+
};
|
|
1489
|
+
if (allScheduledFunctions && disableDevelopmentScheduleFunction !== false) {
|
|
1490
|
+
fullFunctionsMap[firebase.SCHEDULED_FUNCTION_DEV_FUNCTION_SPECIFIER] = makeScheduledFunctionDevelopmentFunction({
|
|
1491
|
+
allScheduledFunctions
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
let onCallFunction = onCallDevelopmentFunction(fullFunctionsMap);
|
|
1495
|
+
if (secure != false) {
|
|
1496
|
+
onCallFunction = inAuthContext(onCallFunction);
|
|
1497
|
+
}
|
|
1498
|
+
dev = onCallFactory(onCallFunction)(nest);
|
|
1499
|
+
} else {
|
|
1500
|
+
dev = onCallFactory(async x => {
|
|
1501
|
+
throw unavailableError({
|
|
1502
|
+
message: 'developer tools service is not enabled.'
|
|
1503
|
+
});
|
|
1504
|
+
})(nest);
|
|
1505
|
+
}
|
|
1506
|
+
return {
|
|
1507
|
+
dev
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
exports.DefaultFirebaseServerEnvService = class DefaultFirebaseServerEnvService extends nestjs.ServerEnvironmentService {
|
|
1512
|
+
/**
|
|
1513
|
+
* Enabled when not in production and not in a testing environment.
|
|
1514
|
+
*/
|
|
1515
|
+
get developmentSchedulerEnabled() {
|
|
1516
|
+
return !this.isProduction && !this.isTestingEnv;
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
exports.DefaultFirebaseServerEnvService = __decorate([common.Injectable()], exports.DefaultFirebaseServerEnvService);
|
|
1520
|
+
|
|
1521
|
+
function nestAppIsProductionEnvironment(nest) {
|
|
1522
|
+
return () => nest().then(x => x.get(FirebaseServerEnvService).isProduction);
|
|
1523
|
+
}
|
|
1524
|
+
function nestAppHasDevelopmentSchedulerEnabled(nest) {
|
|
1525
|
+
return () => nest().then(x => x.get(FirebaseServerEnvService).developmentSchedulerEnabled);
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
// MARK: Tokens
|
|
1529
|
+
/**
|
|
1530
|
+
* Token to access the Firestore.
|
|
1531
|
+
*/
|
|
1532
|
+
const FIREBASE_FIRESTORE_TOKEN = 'FIREBASE_FIRESTORE_TOKEN';
|
|
1533
|
+
/**
|
|
1534
|
+
* Token to access the root FirestoreContext for a server.
|
|
1535
|
+
*/
|
|
1536
|
+
const FIREBASE_FIRESTORE_CONTEXT_TOKEN = 'FIREBASE_FIRESTORE_CONTEXT_TOKEN';
|
|
1537
|
+
/**
|
|
1538
|
+
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
1539
|
+
*/
|
|
1540
|
+
exports.FirebaseServerFirestoreModule = class FirebaseServerFirestoreModule {};
|
|
1541
|
+
exports.FirebaseServerFirestoreModule = __decorate([common.Module({
|
|
1542
|
+
providers: [{
|
|
1543
|
+
provide: FIREBASE_FIRESTORE_TOKEN,
|
|
1544
|
+
useFactory: app => app.firestore(),
|
|
1545
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
1546
|
+
}],
|
|
1547
|
+
exports: [FIREBASE_FIRESTORE_TOKEN]
|
|
1548
|
+
})], exports.FirebaseServerFirestoreModule);
|
|
1549
|
+
/**
|
|
1550
|
+
* Nest provider module for firebase that includes the FirebaseServerFirestoreModule and provides a value for FIRESTORE_CONTEXT_TOKEN using the googleCloudFirestoreContextFactory.
|
|
1551
|
+
*/
|
|
1552
|
+
exports.FirebaseServerFirestoreContextModule = class FirebaseServerFirestoreContextModule {};
|
|
1553
|
+
exports.FirebaseServerFirestoreContextModule = __decorate([common.Module({
|
|
1554
|
+
imports: [exports.FirebaseServerFirestoreModule],
|
|
1555
|
+
providers: [{
|
|
1556
|
+
provide: FIREBASE_FIRESTORE_CONTEXT_TOKEN,
|
|
1557
|
+
useFactory: googleCloudFirestoreContextFactory,
|
|
1558
|
+
inject: [FIREBASE_FIRESTORE_TOKEN]
|
|
1559
|
+
}],
|
|
1560
|
+
exports: [exports.FirebaseServerFirestoreModule, FIREBASE_FIRESTORE_CONTEXT_TOKEN]
|
|
1561
|
+
})], exports.FirebaseServerFirestoreContextModule);
|
|
1562
|
+
/**
|
|
1563
|
+
* Used to configure a Nestjs provider for a FirestoreCollections-type object that is initialized with a FirestoreContext.
|
|
1564
|
+
*
|
|
1565
|
+
* @param type
|
|
1566
|
+
* @param useFactory
|
|
1567
|
+
* @returns
|
|
1568
|
+
*/
|
|
1569
|
+
function provideAppFirestoreCollections({
|
|
1570
|
+
provide,
|
|
1571
|
+
useFactory
|
|
1572
|
+
}) {
|
|
1573
|
+
return [{
|
|
1574
|
+
provide,
|
|
1575
|
+
useFactory,
|
|
1576
|
+
inject: [FIREBASE_FIRESTORE_CONTEXT_TOKEN]
|
|
1577
|
+
}];
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Convenience function used to generate ModuleMetadata for an app's Firestore related modules and an appFirestoreCollection
|
|
1581
|
+
*
|
|
1582
|
+
* @param provide
|
|
1583
|
+
* @param useFactory
|
|
1584
|
+
* @returns
|
|
1585
|
+
*/
|
|
1586
|
+
function appFirestoreModuleMetadata(config) {
|
|
1587
|
+
return {
|
|
1588
|
+
imports: [exports.FirebaseServerFirestoreContextModule, ...(config.imports ?? [])],
|
|
1589
|
+
exports: [exports.FirebaseServerFirestoreContextModule, config.provide, ...(config.exports ?? [])],
|
|
1590
|
+
providers: [...provideAppFirestoreCollections(config), ...(config.providers ?? [])]
|
|
1591
|
+
};
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
/**
|
|
1595
|
+
* Creates a BlockingFunctionWithHandler from the input.
|
|
1596
|
+
*
|
|
1597
|
+
* @param blockingFunctionBuilder
|
|
1598
|
+
* @param handler
|
|
1599
|
+
* @returns
|
|
1600
|
+
*/
|
|
1601
|
+
function makeBlockingFunctionWithHandler(blockingFunctionBuilder, handler, opts) {
|
|
1602
|
+
const blockingFn = opts != null ? blockingFunctionBuilder(opts, handler) : blockingFunctionBuilder(handler);
|
|
1603
|
+
blockingFn.__handler = handler;
|
|
1604
|
+
return blockingFn;
|
|
1605
|
+
}
|
|
1606
|
+
/**
|
|
1607
|
+
* Creates a BlockingFunctionHandlerWithNestContextFactory.
|
|
1608
|
+
*
|
|
1609
|
+
* @param appFactory
|
|
1610
|
+
* @param makeNestContext
|
|
1611
|
+
* @returns
|
|
1612
|
+
*/
|
|
1613
|
+
function blockingFunctionHandlerWithNestContextFactory(makeNestContext) {
|
|
1614
|
+
return fn => {
|
|
1615
|
+
return nestAppPromiseGetter => {
|
|
1616
|
+
const handlerBuilder = handler => {
|
|
1617
|
+
const fnHandler = event => nestAppPromiseGetter().then(nestApplication => handler({
|
|
1618
|
+
...event,
|
|
1619
|
+
nest: makeNestContext(nestApplication)
|
|
1620
|
+
}));
|
|
1621
|
+
return fnHandler;
|
|
1622
|
+
};
|
|
1623
|
+
return fn(handlerBuilder);
|
|
1624
|
+
};
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
/**
|
|
1629
|
+
* Creates a factory for generating OnCallWithNestApplication (firebase-functions v2) functions.
|
|
1630
|
+
*
|
|
1631
|
+
* @param opts
|
|
1632
|
+
* @returns
|
|
1633
|
+
*/
|
|
1634
|
+
function onCallHandlerWithNestApplicationFactory(defaultOpts = {}) {
|
|
1635
|
+
return (fn, opts) => {
|
|
1636
|
+
return nestAppPromiseGetter => v2.https.onCall({
|
|
1637
|
+
...defaultOpts,
|
|
1638
|
+
...opts
|
|
1639
|
+
}, request => nestAppPromiseGetter().then(nestApplication => fn({
|
|
1640
|
+
...request,
|
|
1641
|
+
nestApplication
|
|
1642
|
+
})));
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
/**
|
|
1646
|
+
* Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function.
|
|
1647
|
+
*
|
|
1648
|
+
* @param appFactory
|
|
1649
|
+
* @param makeNestContext
|
|
1650
|
+
* @returns
|
|
1651
|
+
*/
|
|
1652
|
+
function onCallHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
1653
|
+
return (fn, opts) => appFactory(request => fn(setNestContextOnRequest(makeNestContext, request)), opts);
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/**
|
|
1657
|
+
* Creates a CloudEventHandlerWithNestContextFactory.
|
|
1658
|
+
*
|
|
1659
|
+
* @param appFactory
|
|
1660
|
+
* @param makeNestContext
|
|
1661
|
+
* @returns
|
|
1662
|
+
*/
|
|
1663
|
+
function cloudEventHandlerWithNestContextFactory(makeNestContext) {
|
|
1664
|
+
return fn => {
|
|
1665
|
+
return nestAppPromiseGetter => {
|
|
1666
|
+
const handlerBuilder = handler => {
|
|
1667
|
+
const fnHandler = event => nestAppPromiseGetter().then(nestApplication => handler({
|
|
1668
|
+
...event,
|
|
1669
|
+
nest: makeNestContext(nestApplication)
|
|
1670
|
+
}));
|
|
1671
|
+
return fnHandler;
|
|
1672
|
+
};
|
|
1673
|
+
return fn(handlerBuilder);
|
|
1674
|
+
};
|
|
1675
|
+
};
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
function setNestContextOnScheduleRequest(makeNestContext, request) {
|
|
1679
|
+
request.nest = makeNestContext(request.nestApplication);
|
|
1680
|
+
return request;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
function makeOnScheduleHandlerWithNestApplicationRequest(nestApplication, scheduleContext) {
|
|
1684
|
+
return {
|
|
1685
|
+
nestApplication,
|
|
1686
|
+
scheduleContext
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Creates a factory for generating OnCallWithNestApplication functions.
|
|
1691
|
+
*
|
|
1692
|
+
* @param nestAppPromiseGetter
|
|
1693
|
+
* @returns
|
|
1694
|
+
*/
|
|
1695
|
+
function onScheduleHandlerWithNestApplicationFactory(baseScheduleConfig) {
|
|
1696
|
+
return (inputSchedule, fn) => {
|
|
1697
|
+
const schedule = util.mergeObjects([baseScheduleConfig, inputSchedule]);
|
|
1698
|
+
if (!schedule.schedule) {
|
|
1699
|
+
if (schedule.cron) {
|
|
1700
|
+
if (typeof schedule.cron === 'number') {
|
|
1701
|
+
schedule.schedule = util.cronExpressionRepeatingEveryNMinutes(schedule.cron);
|
|
1702
|
+
} else {
|
|
1703
|
+
schedule.schedule = schedule.cron;
|
|
1704
|
+
}
|
|
1705
|
+
} else {
|
|
1706
|
+
throw new Error('Missing required "cron" or "schedule" variable for configuration.');
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return nestAppPromiseGetter => {
|
|
1710
|
+
const runNow = scheduleContext => nestAppPromiseGetter().then(x => fn(makeOnScheduleHandlerWithNestApplicationRequest(x, scheduleContext)));
|
|
1711
|
+
const fnn = v2.scheduler.onSchedule(schedule, runNow);
|
|
1712
|
+
fnn._schedule = schedule;
|
|
1713
|
+
fnn._runNow = runNow;
|
|
1714
|
+
return fnn;
|
|
1715
|
+
};
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Creates a factory for generating OnCallWithNestContext functions with a nest context object that is generated by the input function.
|
|
1720
|
+
*
|
|
1721
|
+
* @param appFactory
|
|
1722
|
+
* @param makeNestContext
|
|
1723
|
+
* @returns
|
|
1724
|
+
*/
|
|
1725
|
+
function onScheduleHandlerWithNestContextFactory(appFactory, makeNestContext) {
|
|
1726
|
+
return (schedule, fn) => appFactory(schedule, request => fn(setNestContextOnScheduleRequest(makeNestContext, request)));
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
/**
|
|
1730
|
+
* Creates a TaskQueueFunctionHandlerWithNestContextFactory.
|
|
1731
|
+
*
|
|
1732
|
+
* @param appFactory
|
|
1733
|
+
* @param makeNestContext
|
|
1734
|
+
* @returns
|
|
1735
|
+
*/
|
|
1736
|
+
function taskQueueFunctionHandlerWithNestContextFactory(makeNestContext) {
|
|
1737
|
+
return fn => {
|
|
1738
|
+
return nestAppPromiseGetter => {
|
|
1739
|
+
const handlerBuilder = handler => {
|
|
1740
|
+
const fnHandler = taskRequest => nestAppPromiseGetter().then(nestApplication => handler({
|
|
1741
|
+
...taskRequest,
|
|
1742
|
+
nest: makeNestContext(nestApplication)
|
|
1743
|
+
}));
|
|
1744
|
+
return fnHandler;
|
|
1745
|
+
};
|
|
1746
|
+
return fn(handlerBuilder);
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
}
|
|
1750
|
+
// TODO(FUTURE): Add factory that also adds onTaskDispatched usage, as the above is incomplete for full usage and only sets up a function for the request.
|
|
1751
|
+
|
|
1752
|
+
class AbstractFirebaseServerActionsContext {}
|
|
1753
|
+
function firebaseServerActionsContext(options) {
|
|
1754
|
+
return {
|
|
1755
|
+
...firebaseServerActionsTransformContext(options)
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
const defaultFirebaseServerActionsTransformFactoryLogErrorFunction = details => {
|
|
1759
|
+
console.log('firebaseServerActionsTransformFactory() encountered validation error: ', details);
|
|
1760
|
+
};
|
|
1761
|
+
function firebaseServerActionsTransformContext(options) {
|
|
1762
|
+
const firebaseServerActionTransformFactory = firebaseServerActionsTransformFactory(options);
|
|
1763
|
+
const firebaseServerActionTransformFunctionFactory = model.toTransformAndValidateFunctionResultFactory(firebaseServerActionTransformFactory);
|
|
1764
|
+
return {
|
|
1765
|
+
firebaseServerActionTransformFactory,
|
|
1766
|
+
firebaseServerActionTransformFunctionFactory
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
const FIREBASE_SERVER_VALIDATION_ERROR_CODE = 'VALIDATION_ERROR';
|
|
1770
|
+
/**
|
|
1771
|
+
*
|
|
1772
|
+
* @param validationError
|
|
1773
|
+
* @returns
|
|
1774
|
+
*/
|
|
1775
|
+
function firebaseServerValidationServerError(validationError) {
|
|
1776
|
+
const nestValidationExceptionFactory = new common.ValidationPipe({
|
|
1777
|
+
forbidUnknownValues: false
|
|
1778
|
+
}).createExceptionFactory();
|
|
1779
|
+
const nestError = nestValidationExceptionFactory(validationError);
|
|
1780
|
+
const data = nestError.getResponse();
|
|
1781
|
+
return {
|
|
1782
|
+
message: 'One or more data/form validation errors occurred.',
|
|
1783
|
+
code: FIREBASE_SERVER_VALIDATION_ERROR_CODE,
|
|
1784
|
+
data
|
|
1785
|
+
};
|
|
1786
|
+
}
|
|
1787
|
+
/**
|
|
1788
|
+
* Creates a new badRequestError with the validation error details as the response data.
|
|
1789
|
+
*
|
|
1790
|
+
* @param validationError
|
|
1791
|
+
* @returns
|
|
1792
|
+
*/
|
|
1793
|
+
function firebaseServerValidationError(validationError) {
|
|
1794
|
+
const serverError = firebaseServerValidationServerError(validationError);
|
|
1795
|
+
return badRequestError(serverError);
|
|
1796
|
+
}
|
|
1797
|
+
function firebaseServerActionsTransformFactory(options) {
|
|
1798
|
+
const {
|
|
1799
|
+
logError,
|
|
1800
|
+
defaultValidationOptions
|
|
1801
|
+
} = options ?? {};
|
|
1802
|
+
const logErrorFunction = logError !== false ? typeof logError === 'function' ? logError : defaultFirebaseServerActionsTransformFactoryLogErrorFunction : util.mapIdentityFunction;
|
|
1803
|
+
return model.transformAndValidateObjectFactory({
|
|
1804
|
+
handleValidationError: validationError => {
|
|
1805
|
+
const serverError = firebaseServerValidationServerError(validationError);
|
|
1806
|
+
const {
|
|
1807
|
+
data
|
|
1808
|
+
} = serverError;
|
|
1809
|
+
logErrorFunction(data);
|
|
1810
|
+
throw badRequestError(serverError);
|
|
1811
|
+
},
|
|
1812
|
+
defaultValidationOptions
|
|
1813
|
+
});
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
function injectNestIntoRequest(nest, request) {
|
|
1817
|
+
return {
|
|
1818
|
+
...request,
|
|
1819
|
+
nest
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
function injectNestApplicationContextIntoRequest(nestContext, request) {
|
|
1823
|
+
return {
|
|
1824
|
+
...request,
|
|
1825
|
+
nestApplication: nestContext
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
/**
|
|
1830
|
+
* Can be injected to retrieve information about the global prefix configured for the app.
|
|
1831
|
+
*/
|
|
1832
|
+
class GlobalRoutePrefixConfig {
|
|
1833
|
+
constructor() {
|
|
1834
|
+
this.globalApiRoutePrefix = void 0;
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
|
|
1838
|
+
/**
|
|
1839
|
+
* Middleware that verifies the X-Firebase-AppCheck header using admin.
|
|
1840
|
+
*
|
|
1841
|
+
* It ignores all webhook paths by default.
|
|
1842
|
+
*/
|
|
1843
|
+
exports.FirebaseAppCheckMiddleware = class FirebaseAppCheckMiddleware {
|
|
1844
|
+
constructor(globalRoutePrefixConfig) {
|
|
1845
|
+
this.globalRoutePrefixConfig = void 0;
|
|
1846
|
+
this.logger = new common.Logger('FirebaseAppCheckMiddleware');
|
|
1847
|
+
this._ignoredWebhookPath = void 0;
|
|
1848
|
+
this.globalRoutePrefixConfig = globalRoutePrefixConfig;
|
|
1849
|
+
this._ignoredWebhookPath = this.globalRoutePrefixConfig?.globalApiRoutePrefix ? `${this.globalRoutePrefixConfig.globalApiRoutePrefix}${nestjs.DEFAULT_BASE_WEBHOOK_PATH}` : nestjs.DEFAULT_BASE_WEBHOOK_PATH;
|
|
1850
|
+
}
|
|
1851
|
+
async use(req, res, next) {
|
|
1852
|
+
const isIgnoredRoute = this.isIgnoredRequest(req);
|
|
1853
|
+
let error;
|
|
1854
|
+
if (!isIgnoredRoute) {
|
|
1855
|
+
error = await verifyAppCheckInRequest(req);
|
|
1856
|
+
if (error) {
|
|
1857
|
+
this.logger.error('app check token failed verify');
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1860
|
+
next(error);
|
|
1861
|
+
}
|
|
1862
|
+
isIgnoredRequest(req) {
|
|
1863
|
+
const isIgnoredRoute = req.skipAppCheck || this.isIgnoredPath(req.baseUrl);
|
|
1864
|
+
return isIgnoredRoute;
|
|
1865
|
+
}
|
|
1866
|
+
isIgnoredPath(path) {
|
|
1867
|
+
return path.startsWith(this._ignoredWebhookPath);
|
|
1868
|
+
}
|
|
1869
|
+
};
|
|
1870
|
+
exports.FirebaseAppCheckMiddleware = __decorate([common.Injectable(), __param(0, common.Optional()), __param(0, common.Inject(GlobalRoutePrefixConfig)), __metadata("design:paramtypes", [Object])], exports.FirebaseAppCheckMiddleware);
|
|
1871
|
+
/**
|
|
1872
|
+
* Verifies the AppCheck parameter. If it fails, a value is returned.
|
|
1873
|
+
*
|
|
1874
|
+
* @param req
|
|
1875
|
+
* @param res
|
|
1876
|
+
* @param next
|
|
1877
|
+
* @returns
|
|
1878
|
+
*/
|
|
1879
|
+
async function verifyAppCheckInRequest(req) {
|
|
1880
|
+
const appCheckToken = req.header('X-Firebase-AppCheck');
|
|
1881
|
+
let error;
|
|
1882
|
+
if (!appCheckToken) {
|
|
1883
|
+
error = new common.ForbiddenException();
|
|
1884
|
+
} else {
|
|
1885
|
+
// verify the token
|
|
1886
|
+
try {
|
|
1887
|
+
await admin.appCheck().verifyToken(appCheckToken);
|
|
1888
|
+
} catch (e) {
|
|
1889
|
+
error = new common.ForbiddenException();
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return error;
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
/**
|
|
1896
|
+
* Convenience class that mirrors the ConfigureAppCheckMiddlewareModule class in @dereekb/nestjs, but for Firebase apps.
|
|
1897
|
+
*/
|
|
1898
|
+
exports.ConfigureFirebaseAppCheckMiddlewareModule = class ConfigureFirebaseAppCheckMiddlewareModule {
|
|
1899
|
+
constructor() {
|
|
1900
|
+
this.logger = new common.Logger('ConfigureFirebaseAppCheckMiddlewareModule');
|
|
1901
|
+
}
|
|
1902
|
+
configure(consumer) {
|
|
1903
|
+
consumer.apply(exports.FirebaseAppCheckMiddleware).forRoutes('*');
|
|
1904
|
+
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1905
|
+
}
|
|
1906
|
+
};
|
|
1907
|
+
exports.ConfigureFirebaseAppCheckMiddlewareModule = __decorate([common.Module({})], exports.ConfigureFirebaseAppCheckMiddlewareModule);
|
|
1908
|
+
|
|
1909
|
+
/**
|
|
1910
|
+
* nestjs decorator that will instruct FirebaseAppCheckMiddleware to skip AppCheck for related requests.
|
|
1911
|
+
*/
|
|
1912
|
+
const SkipAppCheck = common.createParamDecorator(async (_, context) => {
|
|
1913
|
+
const req = context.switchToHttp().getRequest();
|
|
1914
|
+
req.skipAppCheck = true;
|
|
1915
|
+
});
|
|
1916
|
+
|
|
1917
|
+
exports.FirebaseRawBodyMiddleware = class FirebaseRawBodyMiddleware {
|
|
1918
|
+
use(req, res, next) {
|
|
1919
|
+
req.body = req.rawBody;
|
|
1920
|
+
next();
|
|
1921
|
+
}
|
|
1922
|
+
};
|
|
1923
|
+
exports.FirebaseRawBodyMiddleware = __decorate([common.Injectable()], exports.FirebaseRawBodyMiddleware);
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* Convenience class that mirrors the ConfigureWebhookMiddlewareModule class in @dereekb/nestjs, but for Firebase apps.
|
|
1927
|
+
*
|
|
1928
|
+
* Requests to /webhook/* have their request.body value set to the rawBody.
|
|
1929
|
+
*/
|
|
1930
|
+
exports.ConfigureFirebaseWebhookMiddlewareModule = class ConfigureFirebaseWebhookMiddlewareModule {
|
|
1931
|
+
constructor() {
|
|
1932
|
+
this.logger = new common.Logger('ConfigureFirebaseWebhookMiddlewareModule');
|
|
1933
|
+
}
|
|
1934
|
+
configure(consumer) {
|
|
1935
|
+
consumer.apply(exports.FirebaseRawBodyMiddleware).forRoutes(nestjs.DEFAULT_WEBHOOK_MIDDLEWARE_ROUTE_INFO);
|
|
1936
|
+
this.logger.debug('Configured firebase webhook routes with proper middleware.');
|
|
1937
|
+
}
|
|
1938
|
+
};
|
|
1939
|
+
exports.ConfigureFirebaseWebhookMiddlewareModule = __decorate([common.Module({})], exports.ConfigureFirebaseWebhookMiddlewareModule);
|
|
1940
|
+
|
|
1941
|
+
const nestFirebaseDoesNotExistError = firebaseContextGrantedModelRoles => {
|
|
1942
|
+
return modelNotAvailableError({
|
|
1943
|
+
data: {
|
|
1944
|
+
id: firebaseContextGrantedModelRoles.data?.document.key,
|
|
1945
|
+
type: firebaseContextGrantedModelRoles.data?.document.modelType
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
};
|
|
1949
|
+
const nestFirebaseForbiddenPermissionError = (firebaseContextGrantedModelRoles, roles) => {
|
|
1950
|
+
return forbiddenError({
|
|
1951
|
+
data: {
|
|
1952
|
+
id: firebaseContextGrantedModelRoles.data?.document.key,
|
|
1953
|
+
type: firebaseContextGrantedModelRoles.data?.document.modelType,
|
|
1954
|
+
roles
|
|
1955
|
+
}
|
|
1956
|
+
});
|
|
1957
|
+
};
|
|
1958
|
+
|
|
1959
|
+
function onCallSpecifierHandler(config) {
|
|
1960
|
+
const map = util.objectToMap(config);
|
|
1961
|
+
const fn = request => {
|
|
1962
|
+
const {
|
|
1963
|
+
specifier = firebase.MODEL_FUNCTION_FIREBASE_CRUD_FUNCTION_SPECIFIER_DEFAULT
|
|
1964
|
+
} = request;
|
|
1965
|
+
const handler = map.get(specifier);
|
|
1966
|
+
if (handler != null) {
|
|
1967
|
+
assertRequestRequiresAuthForFunction(handler, request);
|
|
1968
|
+
return handler(request);
|
|
1969
|
+
} else {
|
|
1970
|
+
throw unknownModelCrudFunctionSpecifierError(specifier);
|
|
1971
|
+
}
|
|
1972
|
+
};
|
|
1973
|
+
fn._requireAuth = false;
|
|
1974
|
+
return fn;
|
|
1975
|
+
}
|
|
1976
|
+
function unknownModelCrudFunctionSpecifierError(specifier) {
|
|
1977
|
+
return badRequestError(util.serverError({
|
|
1978
|
+
status: 400,
|
|
1979
|
+
code: 'UNKNOWN_SPECIFIER_ERROR',
|
|
1980
|
+
message: 'Invalid/unknown specifier for this function.',
|
|
1981
|
+
data: {
|
|
1982
|
+
specifier
|
|
1983
|
+
}
|
|
1984
|
+
}));
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
/**
|
|
1988
|
+
* Creates a OnCallWithAuthorizedNestContext function for creating a model.
|
|
1989
|
+
*
|
|
1990
|
+
* @param map
|
|
1991
|
+
* @returns
|
|
1992
|
+
*/
|
|
1993
|
+
function onCallModel(map, config = {}) {
|
|
1994
|
+
const {
|
|
1995
|
+
preAssert = () => undefined
|
|
1996
|
+
} = config;
|
|
1997
|
+
return request => {
|
|
1998
|
+
const call = request.data?.call;
|
|
1999
|
+
if (call) {
|
|
2000
|
+
const callFn = map[call];
|
|
2001
|
+
if (callFn) {
|
|
2002
|
+
const {
|
|
2003
|
+
specifier,
|
|
2004
|
+
modelType
|
|
2005
|
+
} = request.data;
|
|
2006
|
+
preAssert({
|
|
2007
|
+
call,
|
|
2008
|
+
request,
|
|
2009
|
+
modelType,
|
|
2010
|
+
specifier
|
|
2011
|
+
});
|
|
2012
|
+
return callFn(request);
|
|
2013
|
+
} else {
|
|
2014
|
+
throw onCallModelUnknownCallTypeError(call);
|
|
2015
|
+
}
|
|
2016
|
+
} else {
|
|
2017
|
+
throw onCallModelMissingCallTypeError();
|
|
2018
|
+
}
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
function onCallModelMissingCallTypeError() {
|
|
2022
|
+
return badRequestError(util.serverError({
|
|
2023
|
+
status: 400,
|
|
2024
|
+
code: 'CALL_TYPE_MISSING_ERROR',
|
|
2025
|
+
message: `The call type was missing from the request.`
|
|
2026
|
+
}));
|
|
2027
|
+
}
|
|
2028
|
+
function onCallModelUnknownCallTypeError(call) {
|
|
2029
|
+
return badRequestError(util.serverError({
|
|
2030
|
+
status: 400,
|
|
2031
|
+
code: 'UNKNOWN_CALL_TYPE_ERROR',
|
|
2032
|
+
message: `Unknown call type "${call}".`,
|
|
2033
|
+
data: {
|
|
2034
|
+
call
|
|
2035
|
+
}
|
|
2036
|
+
}));
|
|
2037
|
+
}
|
|
2038
|
+
function _onCallWithCallTypeFunction(map, config) {
|
|
2039
|
+
const {
|
|
2040
|
+
callType,
|
|
2041
|
+
crudType,
|
|
2042
|
+
preAssert = () => undefined,
|
|
2043
|
+
throwOnUnknownModelType
|
|
2044
|
+
} = config;
|
|
2045
|
+
return request => {
|
|
2046
|
+
const modelType = request.data?.modelType;
|
|
2047
|
+
const crudFn = map[modelType];
|
|
2048
|
+
if (crudFn) {
|
|
2049
|
+
const specifier = request.data.specifier;
|
|
2050
|
+
assertRequestRequiresAuthForFunction(crudFn, request);
|
|
2051
|
+
preAssert({
|
|
2052
|
+
call: callType,
|
|
2053
|
+
request,
|
|
2054
|
+
modelType,
|
|
2055
|
+
specifier
|
|
2056
|
+
});
|
|
2057
|
+
return crudFn({
|
|
2058
|
+
...request,
|
|
2059
|
+
specifier,
|
|
2060
|
+
data: request.data.data
|
|
2061
|
+
});
|
|
2062
|
+
} else {
|
|
2063
|
+
throw throwOnUnknownModelType(modelType);
|
|
2064
|
+
}
|
|
2065
|
+
};
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
function onCallCreateModel(map, config = {}) {
|
|
2069
|
+
const {
|
|
2070
|
+
preAssert
|
|
2071
|
+
} = config;
|
|
2072
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2073
|
+
callType: 'create',
|
|
2074
|
+
crudType: 'create',
|
|
2075
|
+
preAssert,
|
|
2076
|
+
throwOnUnknownModelType: createModelUnknownModelTypeError
|
|
2077
|
+
});
|
|
2078
|
+
}
|
|
2079
|
+
function createModelUnknownModelTypeError(modelType) {
|
|
2080
|
+
return badRequestError(util.serverError({
|
|
2081
|
+
status: 400,
|
|
2082
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2083
|
+
message: `Invalid type "${modelType}" to create.`,
|
|
2084
|
+
data: {
|
|
2085
|
+
modelType
|
|
2086
|
+
}
|
|
2087
|
+
}));
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
function onCallReadModel(map, config = {}) {
|
|
2091
|
+
const {
|
|
2092
|
+
preAssert
|
|
2093
|
+
} = config;
|
|
2094
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2095
|
+
callType: 'read',
|
|
2096
|
+
crudType: 'read',
|
|
2097
|
+
preAssert,
|
|
2098
|
+
throwOnUnknownModelType: readModelUnknownModelTypeError
|
|
2099
|
+
});
|
|
2100
|
+
}
|
|
2101
|
+
function readModelUnknownModelTypeError(modelType) {
|
|
2102
|
+
return badRequestError(util.serverError({
|
|
2103
|
+
status: 400,
|
|
2104
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2105
|
+
message: 'Invalid type to read.',
|
|
2106
|
+
data: {
|
|
2107
|
+
modelType
|
|
2108
|
+
}
|
|
2109
|
+
}));
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
function onCallUpdateModel(map, config = {}) {
|
|
2113
|
+
const {
|
|
2114
|
+
preAssert
|
|
2115
|
+
} = config;
|
|
2116
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2117
|
+
callType: 'update',
|
|
2118
|
+
crudType: 'update',
|
|
2119
|
+
preAssert,
|
|
2120
|
+
throwOnUnknownModelType: updateModelUnknownModelTypeError
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
function updateModelUnknownModelTypeError(modelType) {
|
|
2124
|
+
return badRequestError(util.serverError({
|
|
2125
|
+
status: 400,
|
|
2126
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2127
|
+
message: 'Invalid type to update.',
|
|
2128
|
+
data: {
|
|
2129
|
+
modelType
|
|
2130
|
+
}
|
|
2131
|
+
}));
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
function onCallDeleteModel(map, config = {}) {
|
|
2135
|
+
const {
|
|
2136
|
+
preAssert
|
|
2137
|
+
} = config;
|
|
2138
|
+
return _onCallWithCallTypeFunction(map, {
|
|
2139
|
+
callType: 'delete',
|
|
2140
|
+
crudType: 'delete',
|
|
2141
|
+
preAssert,
|
|
2142
|
+
throwOnUnknownModelType: deleteModelUnknownModelTypeError
|
|
2143
|
+
});
|
|
2144
|
+
}
|
|
2145
|
+
function deleteModelUnknownModelTypeError(modelType) {
|
|
2146
|
+
return badRequestError(util.serverError({
|
|
2147
|
+
status: 400,
|
|
2148
|
+
code: 'UNKNOWN_TYPE_ERROR',
|
|
2149
|
+
message: 'Invalid type to delete.',
|
|
2150
|
+
data: {
|
|
2151
|
+
modelType
|
|
2152
|
+
}
|
|
2153
|
+
}));
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
function googleCloudStorageBucketForStorageFilePath(storage, path) {
|
|
2157
|
+
return storage.bucket(path.bucketId);
|
|
2158
|
+
}
|
|
2159
|
+
function googleCloudStorageFileForStorageFilePath(storage, path) {
|
|
2160
|
+
return googleCloudStorageBucketForStorageFilePath(storage, path).file(path.pathString);
|
|
2161
|
+
}
|
|
2162
|
+
function googleCloudFileMetadataToStorageMetadata(file, metadata) {
|
|
2163
|
+
const fullPath = file.name;
|
|
2164
|
+
const generation = String(metadata.generation ?? file.generation);
|
|
2165
|
+
const metageneration = String(metadata.metageneration);
|
|
2166
|
+
const size = Number(metadata.size);
|
|
2167
|
+
const customMetadata = metadata.metadata;
|
|
2168
|
+
return {
|
|
2169
|
+
bucket: file.bucket.name,
|
|
2170
|
+
fullPath,
|
|
2171
|
+
generation,
|
|
2172
|
+
metageneration,
|
|
2173
|
+
name: file.name,
|
|
2174
|
+
size,
|
|
2175
|
+
timeCreated: metadata.timeCreated,
|
|
2176
|
+
updated: metadata.updated,
|
|
2177
|
+
md5Hash: metadata.md5Hash,
|
|
2178
|
+
cacheControl: metadata.cacheControl,
|
|
2179
|
+
contentDisposition: metadata.contentDisposition,
|
|
2180
|
+
contentEncoding: metadata.contentEncoding,
|
|
2181
|
+
contentLanguage: metadata.contentLanguage,
|
|
2182
|
+
contentType: metadata.contentType,
|
|
2183
|
+
customMetadata
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
2186
|
+
function googleCloudStorageAccessorFile(storage$1, storagePath) {
|
|
2187
|
+
const file = googleCloudStorageFileForStorageFilePath(storage$1, storagePath);
|
|
2188
|
+
function makeDownloadOptions(maxDownloadSizeBytes) {
|
|
2189
|
+
return {
|
|
2190
|
+
...(maxDownloadSizeBytes ? {
|
|
2191
|
+
// end is inclusive
|
|
2192
|
+
end: maxDownloadSizeBytes - 1
|
|
2193
|
+
} : undefined)
|
|
2194
|
+
};
|
|
2195
|
+
}
|
|
2196
|
+
function _configureMetadata(options) {
|
|
2197
|
+
const customMetadata = util.filterUndefinedValues({
|
|
2198
|
+
...options.metadata?.customMetadata,
|
|
2199
|
+
...options?.customMetadata
|
|
2200
|
+
});
|
|
2201
|
+
return util.filterUndefinedValues({
|
|
2202
|
+
cacheControl: options.metadata?.cacheControl,
|
|
2203
|
+
contentDisposition: options.metadata?.contentDisposition,
|
|
2204
|
+
contentEncoding: options.metadata?.contentEncoding,
|
|
2205
|
+
contentLanguage: options.metadata?.contentLanguage,
|
|
2206
|
+
contentType: options.metadata?.contentType,
|
|
2207
|
+
metadata: !util.objectHasNoKeys(customMetadata) ? customMetadata : undefined
|
|
2208
|
+
});
|
|
2209
|
+
}
|
|
2210
|
+
function makeUploadOptions(options) {
|
|
2211
|
+
let metadata;
|
|
2212
|
+
if (options != null) {
|
|
2213
|
+
metadata = _configureMetadata({
|
|
2214
|
+
metadata: {
|
|
2215
|
+
...options.metadata,
|
|
2216
|
+
contentType: options.contentType ?? options.metadata?.contentType
|
|
2217
|
+
},
|
|
2218
|
+
customMetadata: options.customMetadata
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
return {
|
|
2222
|
+
// non-resumable
|
|
2223
|
+
resumable: false,
|
|
2224
|
+
// add content type and other custom metadata
|
|
2225
|
+
...(metadata ? {
|
|
2226
|
+
metadata
|
|
2227
|
+
} : undefined)
|
|
2228
|
+
};
|
|
2229
|
+
}
|
|
2230
|
+
function asFileMetadata(metadata) {
|
|
2231
|
+
return _configureMetadata({
|
|
2232
|
+
metadata
|
|
2233
|
+
});
|
|
2234
|
+
}
|
|
2235
|
+
function makeStoragePathForPath(newPath) {
|
|
2236
|
+
let path;
|
|
2237
|
+
if (typeof newPath === 'string') {
|
|
2238
|
+
path = {
|
|
2239
|
+
bucketId: file.bucket.name,
|
|
2240
|
+
pathString: newPath
|
|
2241
|
+
};
|
|
2242
|
+
} else {
|
|
2243
|
+
path = newPath;
|
|
2244
|
+
}
|
|
2245
|
+
return path;
|
|
2246
|
+
}
|
|
2247
|
+
async function copy(newPath, options) {
|
|
2248
|
+
const newStoragePath = makeStoragePathForPath(newPath);
|
|
2249
|
+
const newFile = googleCloudStorageAccessorFile(storage$1, newStoragePath);
|
|
2250
|
+
return _copyWithFile(newFile, options);
|
|
2251
|
+
}
|
|
2252
|
+
async function _copyWithFile(newFile, options) {
|
|
2253
|
+
const copyOptions = {
|
|
2254
|
+
...options
|
|
2255
|
+
};
|
|
2256
|
+
await file.copy(newFile.reference, copyOptions);
|
|
2257
|
+
return newFile;
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Configuration for the public ACL.
|
|
2261
|
+
*/
|
|
2262
|
+
const PUBLIC_ACL = {
|
|
2263
|
+
entity: 'allUsers',
|
|
2264
|
+
role: 'READER'
|
|
2265
|
+
};
|
|
2266
|
+
const accessorFile = {
|
|
2267
|
+
reference: file,
|
|
2268
|
+
storagePath,
|
|
2269
|
+
exists: () => file.exists().then(x => x[0]),
|
|
2270
|
+
getDownloadUrl: () => file.getMetadata().then(() => file.publicUrl()),
|
|
2271
|
+
getSignedUrl: async input => {
|
|
2272
|
+
const expires = input?.expiresAt ?? (input?.expiresIn != null ? dateFns.addMilliseconds(new Date(), input.expiresIn) // use expiresIn if provided
|
|
2273
|
+
: dateFns.addHours(new Date(), 1)); // default expiration in 1 hour
|
|
2274
|
+
const config = {
|
|
2275
|
+
...input,
|
|
2276
|
+
action: input?.action ?? 'read',
|
|
2277
|
+
expires,
|
|
2278
|
+
expiresIn: undefined,
|
|
2279
|
+
// clear from input
|
|
2280
|
+
expiresAt: undefined
|
|
2281
|
+
};
|
|
2282
|
+
return file.getSignedUrl(config).then(x => x[0]).catch(e => {
|
|
2283
|
+
let publicUrlBackup;
|
|
2284
|
+
if (e && e.name === 'SigningError' && (nestjs.isTestNodeEnv() || process.env.FIREBASE_STORAGE_EMULATOR_HOST)) {
|
|
2285
|
+
// NOTE: Signing does not behave properly in the emulator as it is not supported.
|
|
2286
|
+
// https://github.com/firebase/firebase-tools/issues/3400
|
|
2287
|
+
// we can return the public url instead.
|
|
2288
|
+
// This is fine, as in production this file url is protected by ACLs anyways.
|
|
2289
|
+
publicUrlBackup = file.publicUrl();
|
|
2290
|
+
} else {
|
|
2291
|
+
throw e;
|
|
2292
|
+
}
|
|
2293
|
+
return publicUrlBackup;
|
|
2294
|
+
});
|
|
2295
|
+
},
|
|
2296
|
+
getMetadata: () => file.getMetadata().then(x => googleCloudFileMetadataToStorageMetadata(file, x[0])),
|
|
2297
|
+
setMetadata: metadata => file.setMetadata(asFileMetadata(metadata)).then(x => googleCloudFileMetadataToStorageMetadata(file, x[0])),
|
|
2298
|
+
getBytes: maxDownloadSizeBytes => file.download(makeDownloadOptions(maxDownloadSizeBytes)).then(x => x[0]),
|
|
2299
|
+
getStream: maxDownloadSizeBytes => file.createReadStream(makeDownloadOptions(maxDownloadSizeBytes)),
|
|
2300
|
+
upload: async (input, options) => {
|
|
2301
|
+
let dataToUpload;
|
|
2302
|
+
if (typeof input === 'string') {
|
|
2303
|
+
const parsedStringFormat = firebase.assertStorageUploadOptionsStringFormat(options);
|
|
2304
|
+
const stringFormat = parsedStringFormat === 'raw' ? 'utf-8' : parsedStringFormat;
|
|
2305
|
+
if (stringFormat === 'data_url') {
|
|
2306
|
+
// TODO(FUTURE): support this later if necessary. Server should really never see this type.
|
|
2307
|
+
throw new Error('"data_url" is unsupported.');
|
|
2308
|
+
}
|
|
2309
|
+
dataToUpload = Buffer.from(input, stringFormat);
|
|
2310
|
+
} else {
|
|
2311
|
+
if (Buffer.isBuffer(input)) {
|
|
2312
|
+
dataToUpload = input;
|
|
2313
|
+
} else if (types.isUint8Array(input)) {
|
|
2314
|
+
dataToUpload = Buffer.from(input);
|
|
2315
|
+
} else {
|
|
2316
|
+
// NOTE: these values shouldn't ever be encountered in the NodeJS environment. May remove later.
|
|
2317
|
+
if (types.isArrayBuffer(input)) {
|
|
2318
|
+
dataToUpload = Buffer.from(input);
|
|
2319
|
+
} else {
|
|
2320
|
+
dataToUpload = input.arrayBuffer().then(x => Buffer.from(x));
|
|
2321
|
+
}
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
const data = await dataToUpload;
|
|
2325
|
+
return file.save(data, makeUploadOptions(options));
|
|
2326
|
+
},
|
|
2327
|
+
uploadStream: options => file.createWriteStream(makeUploadOptions(options)),
|
|
2328
|
+
move: async (newPath, options) => {
|
|
2329
|
+
const newStoragePath = makeStoragePathForPath(newPath);
|
|
2330
|
+
const newFile = googleCloudStorageAccessorFile(storage$1, newStoragePath);
|
|
2331
|
+
const moveOptions = {
|
|
2332
|
+
...options
|
|
2333
|
+
};
|
|
2334
|
+
await file.moveFileAtomic(newFile.reference, moveOptions).catch(async e => {
|
|
2335
|
+
if (e instanceof storage.ApiError && e.response?.statusMessage === 'Not Implemented') {
|
|
2336
|
+
// NOTE: This is not implemented in storage emulator, so it will fail with this error in testing.
|
|
2337
|
+
// https://github.com/firebase/firebase-tools/issues/3751
|
|
2338
|
+
// we can perform the same task using copy and then deleting this file.
|
|
2339
|
+
await copy(newPath, moveOptions);
|
|
2340
|
+
await accessorFile.delete();
|
|
2341
|
+
} else {
|
|
2342
|
+
throw e;
|
|
2343
|
+
}
|
|
2344
|
+
});
|
|
2345
|
+
return newFile;
|
|
2346
|
+
},
|
|
2347
|
+
copy,
|
|
2348
|
+
delete: options => file.delete(options).then(x => undefined),
|
|
2349
|
+
isPublic: () => file.isPublic().then(x => x[0]),
|
|
2350
|
+
makePublic: setPublic => (setPublic !== false ? file.acl.add(PUBLIC_ACL) : file.acl.delete({
|
|
2351
|
+
entity: PUBLIC_ACL.entity
|
|
2352
|
+
})).then(() => undefined),
|
|
2353
|
+
makePrivate: options => file.makePrivate(options).then(() => undefined),
|
|
2354
|
+
getAcls: options => file.acl.get(options).then(x => ({
|
|
2355
|
+
acls: x[0],
|
|
2356
|
+
metadata: x[1]
|
|
2357
|
+
}))
|
|
2358
|
+
};
|
|
2359
|
+
return accessorFile;
|
|
2360
|
+
}
|
|
2361
|
+
const googleCloudStorageListFilesResultFactory = firebase.storageListFilesResultFactory({
|
|
2362
|
+
hasItems(result) {
|
|
2363
|
+
return Boolean(result.apiResponse.items || result.apiResponse.prefixes);
|
|
2364
|
+
},
|
|
2365
|
+
hasNext: result => {
|
|
2366
|
+
return result.nextQuery != null;
|
|
2367
|
+
},
|
|
2368
|
+
nextPageTokenFromResult(result) {
|
|
2369
|
+
return result.nextQuery?.pageToken;
|
|
2370
|
+
},
|
|
2371
|
+
next(storage, options, folder, result) {
|
|
2372
|
+
return folder.list({
|
|
2373
|
+
...options,
|
|
2374
|
+
...result.nextQuery
|
|
2375
|
+
});
|
|
2376
|
+
},
|
|
2377
|
+
file(storage, fileResult) {
|
|
2378
|
+
return googleCloudStorageAccessorFile(storage, fileResult.storagePath);
|
|
2379
|
+
},
|
|
2380
|
+
folder(storage, folderResult) {
|
|
2381
|
+
return googleCloudStorageAccessorFolder(storage, folderResult.storagePath);
|
|
2382
|
+
},
|
|
2383
|
+
filesFromResult(result) {
|
|
2384
|
+
const items = result.apiResponse?.items ?? [];
|
|
2385
|
+
return items.map(x => ({
|
|
2386
|
+
raw: x,
|
|
2387
|
+
name: util.slashPathName(x.name),
|
|
2388
|
+
storagePath: {
|
|
2389
|
+
bucketId: x.bucket,
|
|
2390
|
+
pathString: x.name
|
|
2391
|
+
}
|
|
2392
|
+
}));
|
|
2393
|
+
},
|
|
2394
|
+
foldersFromResult(result, folder) {
|
|
2395
|
+
const items = result.apiResponse?.prefixes ?? [];
|
|
2396
|
+
return items.map(prefix => ({
|
|
2397
|
+
raw: prefix,
|
|
2398
|
+
name: util.slashPathName(prefix),
|
|
2399
|
+
storagePath: {
|
|
2400
|
+
bucketId: folder.storagePath.bucketId,
|
|
2401
|
+
pathString: prefix
|
|
2402
|
+
}
|
|
2403
|
+
}));
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2406
|
+
function googleCloudStorageAccessorFolder(storage, storagePath) {
|
|
2407
|
+
const bucket = googleCloudStorageBucketForStorageFilePath(storage, storagePath);
|
|
2408
|
+
const file = bucket.file(storagePath.pathString);
|
|
2409
|
+
const folder = {
|
|
2410
|
+
reference: file,
|
|
2411
|
+
storagePath,
|
|
2412
|
+
exists: async () => folder.list({
|
|
2413
|
+
maxResults: 1
|
|
2414
|
+
}).then(x => x.hasItems()),
|
|
2415
|
+
list: options => {
|
|
2416
|
+
const {
|
|
2417
|
+
maxResults,
|
|
2418
|
+
pageToken,
|
|
2419
|
+
includeNestedResults: listAll
|
|
2420
|
+
} = options ?? {};
|
|
2421
|
+
const listOptions = {
|
|
2422
|
+
maxResults,
|
|
2423
|
+
pageToken,
|
|
2424
|
+
autoPaginate: false,
|
|
2425
|
+
versions: false,
|
|
2426
|
+
...(listAll ? {
|
|
2427
|
+
prefix: util.toRelativeSlashPathStartType(util.fixMultiSlashesInSlashPath(storagePath.pathString + '/'))
|
|
2428
|
+
} : {
|
|
2429
|
+
// includeTrailingDelimiter: true,
|
|
2430
|
+
delimiter: util.SLASH_PATH_SEPARATOR,
|
|
2431
|
+
prefix: util.toRelativeSlashPathStartType(util.fixMultiSlashesInSlashPath(storagePath.pathString + '/')) // make sure the folder always ends with a slash
|
|
2432
|
+
})
|
|
2433
|
+
};
|
|
2434
|
+
return bucket.getFiles(listOptions).then(x => {
|
|
2435
|
+
const files = x[0];
|
|
2436
|
+
const nextQuery = x[1];
|
|
2437
|
+
const apiResponse = x[2];
|
|
2438
|
+
const result = {
|
|
2439
|
+
files: files,
|
|
2440
|
+
nextQuery,
|
|
2441
|
+
apiResponse: apiResponse
|
|
2442
|
+
};
|
|
2443
|
+
return googleCloudStorageListFilesResultFactory(storage, folder, options, result);
|
|
2444
|
+
});
|
|
2445
|
+
}
|
|
2446
|
+
};
|
|
2447
|
+
return folder;
|
|
2448
|
+
}
|
|
2449
|
+
function googleCloudStorageFirebaseStorageAccessorDriver() {
|
|
2450
|
+
return {
|
|
2451
|
+
type: 'server',
|
|
2452
|
+
file: (storage, path) => googleCloudStorageAccessorFile(storage, path),
|
|
2453
|
+
folder: (storage, path) => googleCloudStorageAccessorFolder(storage, path)
|
|
2454
|
+
};
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
function googleCloudFirebaseStorageDrivers() {
|
|
2458
|
+
return {
|
|
2459
|
+
storageDriverIdentifier: '@google-cloud/storage',
|
|
2460
|
+
storageDriverType: 'production',
|
|
2461
|
+
storageAccessorDriver: googleCloudStorageFirebaseStorageAccessorDriver()
|
|
2462
|
+
};
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
/**
|
|
2466
|
+
* Basic service that implements FirebaseStorageAccessor and provides a FirebaseStorageContext.
|
|
2467
|
+
*/
|
|
2468
|
+
class FirebaseServerStorageService {
|
|
2469
|
+
constructor(storageContext) {
|
|
2470
|
+
this._storageContext = void 0;
|
|
2471
|
+
this._storageContext = storageContext;
|
|
2472
|
+
}
|
|
2473
|
+
get storageContext() {
|
|
2474
|
+
return this._storageContext;
|
|
2475
|
+
}
|
|
2476
|
+
defaultBucket() {
|
|
2477
|
+
return this.storageContext.defaultBucket();
|
|
2478
|
+
}
|
|
2479
|
+
file(path) {
|
|
2480
|
+
return this.storageContext.file(path);
|
|
2481
|
+
}
|
|
2482
|
+
folder(path) {
|
|
2483
|
+
return this.storageContext.folder(path);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
|
|
2487
|
+
/**
|
|
2488
|
+
* Creates a FirestoreContextFactory that uses the @google-cloud/storage package.
|
|
2489
|
+
*/
|
|
2490
|
+
const googleCloudFirebaseStorageContextFactory = firebase.firebaseStorageContextFactory(googleCloudFirebaseStorageDrivers());
|
|
2491
|
+
/**
|
|
2492
|
+
* Retrieves the GoogleCloudStorage object from the input FirebaseAdmin Storage type.
|
|
2493
|
+
*
|
|
2494
|
+
* @param storage
|
|
2495
|
+
* @returns
|
|
2496
|
+
*/
|
|
2497
|
+
function googleCloudStorageFromFirebaseAdminStorage(storage) {
|
|
2498
|
+
return storage.storageClient;
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
// MARK: Tokens
|
|
2502
|
+
/**
|
|
2503
|
+
* Token to access the Storage.
|
|
2504
|
+
*/
|
|
2505
|
+
const FIREBASE_STORAGE_TOKEN = 'FIREBASE_STORAGE_TOKEN';
|
|
2506
|
+
/**
|
|
2507
|
+
* Token to access the root StorageContext for a server.
|
|
2508
|
+
*/
|
|
2509
|
+
const FIREBASE_STORAGE_CONTEXT_TOKEN = 'FIREBASE_STORAGE_CONTEXT_TOKEN';
|
|
2510
|
+
/**
|
|
2511
|
+
* Token to the default bucket id string
|
|
2512
|
+
*/
|
|
2513
|
+
const FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN = 'FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN';
|
|
2514
|
+
/**
|
|
2515
|
+
* Nest provider module for Firebase that provides a firestore, etc. from the firestore token.
|
|
2516
|
+
*/
|
|
2517
|
+
exports.FirebaseServerStorageModule = class FirebaseServerStorageModule {};
|
|
2518
|
+
exports.FirebaseServerStorageModule = __decorate([common.Module({
|
|
2519
|
+
providers: [{
|
|
2520
|
+
provide: FIREBASE_STORAGE_TOKEN,
|
|
2521
|
+
useFactory: app => googleCloudStorageFromFirebaseAdminStorage(app.storage()),
|
|
2522
|
+
inject: [FIREBASE_APP_TOKEN]
|
|
2523
|
+
}],
|
|
2524
|
+
exports: [FIREBASE_STORAGE_TOKEN]
|
|
2525
|
+
})], exports.FirebaseServerStorageModule);
|
|
2526
|
+
/**
|
|
2527
|
+
* Nest provider module for firebase that includes the FirebaseServerStorageModule and provides a value for STORAGE_CONTEXT_TOKEN using the googleCloudStorageContextFactory.
|
|
2528
|
+
*/
|
|
2529
|
+
exports.FirebaseServerStorageContextModule = class FirebaseServerStorageContextModule {};
|
|
2530
|
+
exports.FirebaseServerStorageContextModule = __decorate([common.Module({
|
|
2531
|
+
imports: [exports.FirebaseServerStorageModule],
|
|
2532
|
+
providers: [{
|
|
2533
|
+
provide: FIREBASE_STORAGE_CONTEXT_TOKEN,
|
|
2534
|
+
useFactory: googleCloudFirebaseStorageContextFactory,
|
|
2535
|
+
inject: [FIREBASE_STORAGE_TOKEN, FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN]
|
|
2536
|
+
}],
|
|
2537
|
+
exports: [exports.FirebaseServerStorageModule, FIREBASE_STORAGE_CONTEXT_TOKEN]
|
|
2538
|
+
})], exports.FirebaseServerStorageContextModule);
|
|
2539
|
+
// MARK: Token Configuration
|
|
2540
|
+
function firebaseServerStorageDefaultBucketIdTokenProvider(input) {
|
|
2541
|
+
const config = typeof input === 'string' ? {
|
|
2542
|
+
defaultBucketId: input
|
|
2543
|
+
} : input;
|
|
2544
|
+
if (!config.defaultBucketId) {
|
|
2545
|
+
throw new Error('Non-empty defaultBucketId is required.');
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
provide: FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN,
|
|
2549
|
+
useValue: config
|
|
2550
|
+
};
|
|
2551
|
+
}
|
|
2552
|
+
function defaultProvideFirebaseServerStorageServiceSimple() {
|
|
2553
|
+
return {
|
|
2554
|
+
provide: FirebaseServerStorageService,
|
|
2555
|
+
useFactory: context => new FirebaseServerStorageService(context)
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
function provideFirebaseServerStorageService(provider) {
|
|
2559
|
+
const providers = [{
|
|
2560
|
+
...provider,
|
|
2561
|
+
inject: provider.inject ?? [FIREBASE_STORAGE_CONTEXT_TOKEN]
|
|
2562
|
+
}];
|
|
2563
|
+
if (provider.provide !== FirebaseServerStorageService) {
|
|
2564
|
+
providers.push({
|
|
2565
|
+
provide: FirebaseServerStorageService,
|
|
2566
|
+
useExisting: provider.provide
|
|
2567
|
+
});
|
|
2568
|
+
}
|
|
2569
|
+
return providers;
|
|
2570
|
+
}
|
|
2571
|
+
/**
|
|
2572
|
+
* Convenience function used to generate ModuleMetadata for an app's Auth related modules and FirebaseServerStorageService provider.
|
|
2573
|
+
*
|
|
2574
|
+
* @param provide
|
|
2575
|
+
* @param useFactory
|
|
2576
|
+
* @returns
|
|
2577
|
+
*/
|
|
2578
|
+
function firebaseServerStorageModuleMetadata(config) {
|
|
2579
|
+
const serviceProvider = config && config.serviceProvider ? config.serviceProvider : defaultProvideFirebaseServerStorageServiceSimple();
|
|
2580
|
+
const providers = provideFirebaseServerStorageService(serviceProvider);
|
|
2581
|
+
const tokensToExport = nestjs.injectionTokensFromProviders(providers);
|
|
2582
|
+
return nestjs.mergeModuleMetadata({
|
|
2583
|
+
imports: [exports.FirebaseServerStorageContextModule],
|
|
2584
|
+
exports: [exports.FirebaseServerStorageContextModule, ...tokensToExport],
|
|
2585
|
+
providers
|
|
2586
|
+
}, config);
|
|
2587
|
+
}
|
|
2588
|
+
|
|
2589
|
+
class FirebaseNestServerRootModule {}
|
|
2590
|
+
function nestServerInstance(config) {
|
|
2591
|
+
const {
|
|
2592
|
+
moduleClass,
|
|
2593
|
+
providers: additionalProviders,
|
|
2594
|
+
defaultStorageBucket: inputDefaultStorageBucket,
|
|
2595
|
+
forceStorageBucket,
|
|
2596
|
+
globalApiRoutePrefix,
|
|
2597
|
+
configureNestServerInstance
|
|
2598
|
+
} = config;
|
|
2599
|
+
const serversCache = new Map();
|
|
2600
|
+
const initNestServer = (firebaseApp, env) => {
|
|
2601
|
+
const appName = firebaseApp.name;
|
|
2602
|
+
const defaultStorageBucket = inputDefaultStorageBucket ?? firebaseApp.options.storageBucket;
|
|
2603
|
+
let nestServer = serversCache.get(appName);
|
|
2604
|
+
if (!nestServer) {
|
|
2605
|
+
const server = express();
|
|
2606
|
+
const createNestServer = async expressInstance => {
|
|
2607
|
+
const providers = [firebaseServerAppTokenProvider(util.asGetter(firebaseApp))];
|
|
2608
|
+
// configure environment providers
|
|
2609
|
+
if (env?.environment != null) {
|
|
2610
|
+
providers.push(nestjs.serverEnvTokenProvider(env.environment));
|
|
2611
|
+
if (config.configureEnvService !== false) {
|
|
2612
|
+
providers.push({
|
|
2613
|
+
provide: FirebaseServerEnvService,
|
|
2614
|
+
useClass: exports.DefaultFirebaseServerEnvService
|
|
2615
|
+
}, {
|
|
2616
|
+
provide: nestjs.ServerEnvironmentService,
|
|
2617
|
+
useExisting: FirebaseServerEnvService
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
if (additionalProviders) {
|
|
2622
|
+
util.pushItemOrArrayItemsIntoArray(providers, additionalProviders);
|
|
2623
|
+
}
|
|
2624
|
+
const imports = [moduleClass];
|
|
2625
|
+
// NOTE: https://cloud.google.com/functions/docs/writing/http#parsing_http_requests
|
|
2626
|
+
const options = {
|
|
2627
|
+
bodyParser: false
|
|
2628
|
+
}; // firebase already parses the requests
|
|
2629
|
+
if (config.configureWebhooks) {
|
|
2630
|
+
imports.push(exports.ConfigureFirebaseWebhookMiddlewareModule);
|
|
2631
|
+
}
|
|
2632
|
+
if (config.appCheckEnabled != false) {
|
|
2633
|
+
imports.push(exports.ConfigureFirebaseAppCheckMiddlewareModule);
|
|
2634
|
+
}
|
|
2635
|
+
if (defaultStorageBucket) {
|
|
2636
|
+
providers.push(firebaseServerStorageDefaultBucketIdTokenProvider({
|
|
2637
|
+
defaultBucketId: defaultStorageBucket,
|
|
2638
|
+
forceBucket: forceStorageBucket
|
|
2639
|
+
}));
|
|
2640
|
+
}
|
|
2641
|
+
// provide the global prefix config to the app
|
|
2642
|
+
providers.push({
|
|
2643
|
+
provide: GlobalRoutePrefixConfig,
|
|
2644
|
+
useValue: {
|
|
2645
|
+
globalApiRoutePrefix
|
|
2646
|
+
}
|
|
2647
|
+
});
|
|
2648
|
+
const providersModule = {
|
|
2649
|
+
module: FirebaseNestServerRootModule,
|
|
2650
|
+
imports,
|
|
2651
|
+
providers,
|
|
2652
|
+
exports: providers,
|
|
2653
|
+
global: true
|
|
2654
|
+
};
|
|
2655
|
+
let nestApp = await core.NestFactory.create(providersModule, new platformExpress.ExpressAdapter(expressInstance), options);
|
|
2656
|
+
if (globalApiRoutePrefix) {
|
|
2657
|
+
nestApp = nestApp.setGlobalPrefix(globalApiRoutePrefix);
|
|
2658
|
+
}
|
|
2659
|
+
if (configureNestServerInstance) {
|
|
2660
|
+
nestApp = configureNestServerInstance(nestApp) || nestApp;
|
|
2661
|
+
}
|
|
2662
|
+
return nestApp.init();
|
|
2663
|
+
};
|
|
2664
|
+
const nest = createNestServer(server).catch(err => {
|
|
2665
|
+
console.error('Nest failed startup.', err);
|
|
2666
|
+
throw err;
|
|
2667
|
+
});
|
|
2668
|
+
nestServer = {
|
|
2669
|
+
server,
|
|
2670
|
+
nest: util.makeGetter(nest)
|
|
2671
|
+
};
|
|
2672
|
+
serversCache.set(appName, nestServer);
|
|
2673
|
+
}
|
|
2674
|
+
return nestServer;
|
|
2675
|
+
};
|
|
2676
|
+
const removeNestServer = async firebaseApp => {
|
|
2677
|
+
const appName = firebaseApp.name;
|
|
2678
|
+
const nestServer = serversCache.get(appName);
|
|
2679
|
+
let removed;
|
|
2680
|
+
if (nestServer) {
|
|
2681
|
+
removed = nestServer.nest().then(x => {
|
|
2682
|
+
serversCache.delete(appName);
|
|
2683
|
+
return x.close().then(() => true);
|
|
2684
|
+
});
|
|
2685
|
+
} else {
|
|
2686
|
+
removed = Promise.resolve(false);
|
|
2687
|
+
}
|
|
2688
|
+
return removed;
|
|
2689
|
+
};
|
|
2690
|
+
return {
|
|
2691
|
+
moduleClass,
|
|
2692
|
+
initNestServer,
|
|
2693
|
+
removeNestServer
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
/**
|
|
2698
|
+
* Abstract class that wraps an INestApplicationContext value.
|
|
2699
|
+
*/
|
|
2700
|
+
class AbstractNestContext {
|
|
2701
|
+
constructor(nest) {
|
|
2702
|
+
this._nest = void 0;
|
|
2703
|
+
this._nest = nest;
|
|
2704
|
+
}
|
|
2705
|
+
get nest() {
|
|
2706
|
+
return this._nest;
|
|
2707
|
+
}
|
|
2708
|
+
}
|
|
2709
|
+
class AbstractFirebaseNestContext extends AbstractNestContext {
|
|
2710
|
+
constructor(...args) {
|
|
2711
|
+
super(...args);
|
|
2712
|
+
/**
|
|
2713
|
+
* FirebasePermissionErrorContextErrorFunction to use with makeModelContext().
|
|
2714
|
+
*
|
|
2715
|
+
* Defaults to nestFirebaseForbiddenPermissionError().
|
|
2716
|
+
*/
|
|
2717
|
+
this.makePermissionError = nestFirebaseForbiddenPermissionError;
|
|
2718
|
+
/**
|
|
2719
|
+
* FirebaseDoesNotExistErrorContextErrorFunction to use with makeModelContext().
|
|
2720
|
+
*
|
|
2721
|
+
* Defaults to nestFirebaseDoesNotExistError().
|
|
2722
|
+
*
|
|
2723
|
+
* Some configurations may prefer to use nestFirebaseForbiddenPermissionError instead, which returns a forbidden error instead.
|
|
2724
|
+
* This prevents the leaking of information about the existence of an object.
|
|
2725
|
+
*/
|
|
2726
|
+
this.makeDoesNotExistError = nestFirebaseDoesNotExistError;
|
|
2727
|
+
}
|
|
2728
|
+
get envService() {
|
|
2729
|
+
return this.nest.get(FirebaseServerEnvService);
|
|
2730
|
+
}
|
|
2731
|
+
get storageService() {
|
|
2732
|
+
return this.nest.get(FirebaseServerStorageService);
|
|
2733
|
+
}
|
|
2734
|
+
/**
|
|
2735
|
+
* Creates a FirebaseAppModelContext instance.
|
|
2736
|
+
*
|
|
2737
|
+
* @param auth
|
|
2738
|
+
* @param buildFn
|
|
2739
|
+
* @returns
|
|
2740
|
+
*/
|
|
2741
|
+
makeModelContext(auth, buildFn) {
|
|
2742
|
+
const base = {
|
|
2743
|
+
auth: this.authService.authContextInfo(auth),
|
|
2744
|
+
app: this.app,
|
|
2745
|
+
makePermissionError: this.makePermissionError,
|
|
2746
|
+
makeDoesNotExistError: this.makeDoesNotExistError
|
|
2747
|
+
};
|
|
2748
|
+
return buildFn ? util.build({
|
|
2749
|
+
base,
|
|
2750
|
+
build: buildFn
|
|
2751
|
+
}) : base;
|
|
2752
|
+
}
|
|
2753
|
+
/**
|
|
2754
|
+
* Creates a InContextFirebaseModelsService given the input context and parameters.
|
|
2755
|
+
*
|
|
2756
|
+
* @param context
|
|
2757
|
+
* @param buildFn
|
|
2758
|
+
* @returns
|
|
2759
|
+
*/
|
|
2760
|
+
model(context, buildFn) {
|
|
2761
|
+
const firebaseModelContext = this.makeModelContext(context, buildFn);
|
|
2762
|
+
return firebase.inContextFirebaseModelsServiceFactory(this.firebaseModelsService)(firebaseModelContext);
|
|
2763
|
+
}
|
|
2764
|
+
async useModel(type, select) {
|
|
2765
|
+
const context = this.makeModelContext(select.request, select.buildFn);
|
|
2766
|
+
const usePromise = firebase.useFirebaseModelsService(this.firebaseModelsService, type, {
|
|
2767
|
+
context,
|
|
2768
|
+
key: select.key,
|
|
2769
|
+
roles: select.roles,
|
|
2770
|
+
rolesSetIncludes: select.rolesSetIncludes
|
|
2771
|
+
});
|
|
2772
|
+
const use = select.use ?? (x => x);
|
|
2773
|
+
return usePromise(use);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
|
|
2777
|
+
exports.ALREADY_EXISTS_ERROR_CODE = ALREADY_EXISTS_ERROR_CODE;
|
|
2778
|
+
exports.AbstractFirebaseNestContext = AbstractFirebaseNestContext;
|
|
2779
|
+
exports.AbstractFirebaseServerActionsContext = AbstractFirebaseServerActionsContext;
|
|
2780
|
+
exports.AbstractFirebaseServerAuthContext = AbstractFirebaseServerAuthContext;
|
|
2781
|
+
exports.AbstractFirebaseServerAuthService = AbstractFirebaseServerAuthService;
|
|
2782
|
+
exports.AbstractFirebaseServerAuthUserContext = AbstractFirebaseServerAuthUserContext;
|
|
2783
|
+
exports.AbstractFirebaseServerNewUserService = AbstractFirebaseServerNewUserService;
|
|
2784
|
+
exports.AbstractNestContext = AbstractNestContext;
|
|
2785
|
+
exports.BAD_REQUEST_ERROR_CODE = BAD_REQUEST_ERROR_CODE;
|
|
2786
|
+
exports.CONFLICT_ERROR_CODE = CONFLICT_ERROR_CODE;
|
|
2787
|
+
exports.DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR = DEFAULT_FIREBASE_PASSWORD_NUMBER_GENERATOR;
|
|
2788
|
+
exports.DEFAULT_SETUP_COM_THROTTLE_TIME = DEFAULT_SETUP_COM_THROTTLE_TIME;
|
|
2789
|
+
exports.FIREBASE_APP_TOKEN = FIREBASE_APP_TOKEN;
|
|
2790
|
+
exports.FIREBASE_AUTH_TOKEN = FIREBASE_AUTH_TOKEN;
|
|
2791
|
+
exports.FIREBASE_FIRESTORE_CONTEXT_TOKEN = FIREBASE_FIRESTORE_CONTEXT_TOKEN;
|
|
2792
|
+
exports.FIREBASE_FIRESTORE_TOKEN = FIREBASE_FIRESTORE_TOKEN;
|
|
2793
|
+
exports.FIREBASE_SERVER_VALIDATION_ERROR_CODE = FIREBASE_SERVER_VALIDATION_ERROR_CODE;
|
|
2794
|
+
exports.FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN = FIREBASE_STORAGE_CONTEXT_FACTORY_CONFIG_TOKEN;
|
|
2795
|
+
exports.FIREBASE_STORAGE_CONTEXT_TOKEN = FIREBASE_STORAGE_CONTEXT_TOKEN;
|
|
2796
|
+
exports.FIREBASE_STORAGE_TOKEN = FIREBASE_STORAGE_TOKEN;
|
|
2797
|
+
exports.FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING = FIRESTORE_CLIENT_QUERY_CONSTRAINT_HANDLER_MAPPING;
|
|
2798
|
+
exports.FORBIDDEN_ERROR_CODE = FORBIDDEN_ERROR_CODE;
|
|
2799
|
+
exports.FirebaseNestServerRootModule = FirebaseNestServerRootModule;
|
|
2800
|
+
exports.FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError = FirebaseServerAuthNewUserSendSetupDetailsNoSetupConfigError;
|
|
2801
|
+
exports.FirebaseServerAuthNewUserSendSetupDetailsSendOnceError = FirebaseServerAuthNewUserSendSetupDetailsSendOnceError;
|
|
2802
|
+
exports.FirebaseServerAuthNewUserSendSetupDetailsThrottleError = FirebaseServerAuthNewUserSendSetupDetailsThrottleError;
|
|
2803
|
+
exports.FirebaseServerAuthService = FirebaseServerAuthService;
|
|
2804
|
+
exports.FirebaseServerEnvService = FirebaseServerEnvService;
|
|
2805
|
+
exports.FirebaseServerStorageService = FirebaseServerStorageService;
|
|
2806
|
+
exports.INTERNAL_SERVER_ERROR_CODE = INTERNAL_SERVER_ERROR_CODE;
|
|
2807
|
+
exports.MODEL_NOT_AVAILABLE_ERROR_CODE = MODEL_NOT_AVAILABLE_ERROR_CODE;
|
|
2808
|
+
exports.NOT_FOUND_ERROR_CODE = NOT_FOUND_ERROR_CODE;
|
|
2809
|
+
exports.NO_AUTH_ERROR_CODE = NO_AUTH_ERROR_CODE;
|
|
2810
|
+
exports.NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE = NO_RUN_NAME_SPECIFIED_FOR_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_CODE;
|
|
2811
|
+
exports.NO_UID_ERROR_CODE = NO_UID_ERROR_CODE;
|
|
2812
|
+
exports.NoSetupContentFirebaseServerNewUserService = NoSetupContentFirebaseServerNewUserService;
|
|
2813
|
+
exports.PERMISSION_DENIED_ERROR_CODE = PERMISSION_DENIED_ERROR_CODE;
|
|
2814
|
+
exports.PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE = PHONE_NUMBER_ALREADY_EXISTS_ERROR_CODE;
|
|
2815
|
+
exports.SkipAppCheck = SkipAppCheck;
|
|
2816
|
+
exports.UNAUTHENTICATED_ERROR_CODE = UNAUTHENTICATED_ERROR_CODE;
|
|
2817
|
+
exports.UNAVAILABLE_ERROR_CODE = UNAVAILABLE_ERROR_CODE;
|
|
2818
|
+
exports.UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE = UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE;
|
|
2819
|
+
exports.UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE = UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_NAME_CODE;
|
|
2820
|
+
exports.UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE = UNKNOWN_SCHEDULED_FUNCTION_DEVELOPMENT_FUNCTION_TYPE_CODE;
|
|
2821
|
+
exports._onCallWithCallTypeFunction = _onCallWithCallTypeFunction;
|
|
2822
|
+
exports.alreadyExistsError = alreadyExistsError;
|
|
2823
|
+
exports.appFirestoreModuleMetadata = appFirestoreModuleMetadata;
|
|
2824
|
+
exports.assertContextHasAuth = assertContextHasAuth;
|
|
2825
|
+
exports.assertDocumentExists = assertDocumentExists;
|
|
2826
|
+
exports.assertHasRolesInRequest = assertHasRolesInRequest;
|
|
2827
|
+
exports.assertHasSignedTosInRequest = assertHasSignedTosInRequest;
|
|
2828
|
+
exports.assertIsAdminInRequest = assertIsAdminInRequest;
|
|
2829
|
+
exports.assertIsAdminOrTargetUserInRequestData = assertIsAdminOrTargetUserInRequestData;
|
|
2830
|
+
exports.assertIsContextWithAuthData = assertIsContextWithAuthData;
|
|
2831
|
+
exports.assertRequestRequiresAuthForFunction = assertRequestRequiresAuthForFunction;
|
|
2832
|
+
exports.assertSnapshotData = assertSnapshotData;
|
|
2833
|
+
exports.assertSnapshotDataWithKey = assertSnapshotDataWithKey;
|
|
2834
|
+
exports.badRequestError = badRequestError;
|
|
2835
|
+
exports.blockingFunctionHandlerWithNestContextFactory = blockingFunctionHandlerWithNestContextFactory;
|
|
2836
|
+
exports.cloudEventHandlerWithNestContextFactory = cloudEventHandlerWithNestContextFactory;
|
|
2837
|
+
exports.collectionRefForPath = collectionRefForPath;
|
|
2838
|
+
exports.createModelUnknownModelTypeError = createModelUnknownModelTypeError;
|
|
2839
|
+
exports.defaultFirebaseServerActionsTransformFactoryLogErrorFunction = defaultFirebaseServerActionsTransformFactoryLogErrorFunction;
|
|
2840
|
+
exports.defaultProvideFirebaseServerStorageServiceSimple = defaultProvideFirebaseServerStorageServiceSimple;
|
|
2841
|
+
exports.deleteModelUnknownModelTypeError = deleteModelUnknownModelTypeError;
|
|
2842
|
+
exports.developmentUnknownSpecifierError = developmentUnknownSpecifierError;
|
|
2843
|
+
exports.docRefForPath = docRefForPath;
|
|
2844
|
+
exports.documentModelNotAvailableError = documentModelNotAvailableError;
|
|
2845
|
+
exports.firebaseAuthTokenFromDecodedIdToken = firebaseAuthTokenFromDecodedIdToken;
|
|
2846
|
+
exports.firebaseServerActionsContext = firebaseServerActionsContext;
|
|
2847
|
+
exports.firebaseServerActionsTransformContext = firebaseServerActionsTransformContext;
|
|
2848
|
+
exports.firebaseServerActionsTransformFactory = firebaseServerActionsTransformFactory;
|
|
2849
|
+
exports.firebaseServerAppTokenProvider = firebaseServerAppTokenProvider;
|
|
2850
|
+
exports.firebaseServerAuthModuleMetadata = firebaseServerAuthModuleMetadata;
|
|
2851
|
+
exports.firebaseServerDevFunctions = firebaseServerDevFunctions;
|
|
2852
|
+
exports.firebaseServerErrorInfo = firebaseServerErrorInfo;
|
|
2853
|
+
exports.firebaseServerErrorInfoCodePair = firebaseServerErrorInfoCodePair;
|
|
2854
|
+
exports.firebaseServerErrorInfoServerErrorCodePair = firebaseServerErrorInfoServerErrorCodePair;
|
|
2855
|
+
exports.firebaseServerErrorInfoServerErrorPair = firebaseServerErrorInfoServerErrorPair;
|
|
2856
|
+
exports.firebaseServerStorageDefaultBucketIdTokenProvider = firebaseServerStorageDefaultBucketIdTokenProvider;
|
|
2857
|
+
exports.firebaseServerStorageModuleMetadata = firebaseServerStorageModuleMetadata;
|
|
2858
|
+
exports.firebaseServerValidationError = firebaseServerValidationError;
|
|
2859
|
+
exports.firebaseServerValidationServerError = firebaseServerValidationServerError;
|
|
2860
|
+
exports.firestoreClientQueryConstraintFunctionsDriver = firestoreClientQueryConstraintFunctionsDriver;
|
|
2861
|
+
exports.firestoreServerIncrementUpdateToUpdateData = firestoreServerIncrementUpdateToUpdateData;
|
|
2862
|
+
exports.forbiddenError = forbiddenError;
|
|
2863
|
+
exports.getAuthUserOrUndefined = getAuthUserOrUndefined;
|
|
2864
|
+
exports.googleCloudFileMetadataToStorageMetadata = googleCloudFileMetadataToStorageMetadata;
|
|
2865
|
+
exports.googleCloudFirebaseStorageContextFactory = googleCloudFirebaseStorageContextFactory;
|
|
2866
|
+
exports.googleCloudFirebaseStorageDrivers = googleCloudFirebaseStorageDrivers;
|
|
2867
|
+
exports.googleCloudFirestoreAccessorDriver = googleCloudFirestoreAccessorDriver;
|
|
2868
|
+
exports.googleCloudFirestoreContextFactory = googleCloudFirestoreContextFactory;
|
|
2869
|
+
exports.googleCloudFirestoreDrivers = googleCloudFirestoreDrivers;
|
|
2870
|
+
exports.googleCloudFirestoreQueryDriver = googleCloudFirestoreQueryDriver;
|
|
2871
|
+
exports.googleCloudStorageAccessorFile = googleCloudStorageAccessorFile;
|
|
2872
|
+
exports.googleCloudStorageAccessorFolder = googleCloudStorageAccessorFolder;
|
|
2873
|
+
exports.googleCloudStorageBucketForStorageFilePath = googleCloudStorageBucketForStorageFilePath;
|
|
2874
|
+
exports.googleCloudStorageFileForStorageFilePath = googleCloudStorageFileForStorageFilePath;
|
|
2875
|
+
exports.googleCloudStorageFirebaseStorageAccessorDriver = googleCloudStorageFirebaseStorageAccessorDriver;
|
|
2876
|
+
exports.googleCloudStorageFromFirebaseAdminStorage = googleCloudStorageFromFirebaseAdminStorage;
|
|
2877
|
+
exports.googleCloudStorageListFilesResultFactory = googleCloudStorageListFilesResultFactory;
|
|
2878
|
+
exports.handleFirebaseAuthError = handleFirebaseAuthError;
|
|
2879
|
+
exports.handleFirebaseError = handleFirebaseError;
|
|
2880
|
+
exports.hasAuthRolesInRequest = hasAuthRolesInRequest;
|
|
2881
|
+
exports.hasNewUserSetupPasswordInRequest = hasNewUserSetupPasswordInRequest;
|
|
2882
|
+
exports.hasSignedTosInRequest = hasSignedTosInRequest;
|
|
2883
|
+
exports.inAuthContext = inAuthContext;
|
|
2884
|
+
exports.injectNestApplicationContextIntoRequest = injectNestApplicationContextIntoRequest;
|
|
2885
|
+
exports.injectNestIntoRequest = injectNestIntoRequest;
|
|
2886
|
+
exports.internalServerError = internalServerError;
|
|
2887
|
+
exports.isAdminInRequest = isAdminInRequest;
|
|
2888
|
+
exports.isAdminOrTargetUserInRequestData = isAdminOrTargetUserInRequestData;
|
|
2889
|
+
exports.isContextWithAuthData = isContextWithAuthData;
|
|
2890
|
+
exports.isFirebaseError = isFirebaseError;
|
|
2891
|
+
exports.isFirebaseHttpsError = isFirebaseHttpsError;
|
|
2892
|
+
exports.makeBlockingFunctionWithHandler = makeBlockingFunctionWithHandler;
|
|
2893
|
+
exports.makeOnScheduleHandlerWithNestApplicationRequest = makeOnScheduleHandlerWithNestApplicationRequest;
|
|
2894
|
+
exports.makeScheduledFunctionDevelopmentFunction = makeScheduledFunctionDevelopmentFunction;
|
|
2895
|
+
exports.modelNotAvailableError = modelNotAvailableError;
|
|
2896
|
+
exports.nestAppHasDevelopmentSchedulerEnabled = nestAppHasDevelopmentSchedulerEnabled;
|
|
2897
|
+
exports.nestAppIsProductionEnvironment = nestAppIsProductionEnvironment;
|
|
2898
|
+
exports.nestFirebaseDoesNotExistError = nestFirebaseDoesNotExistError;
|
|
2899
|
+
exports.nestFirebaseForbiddenPermissionError = nestFirebaseForbiddenPermissionError;
|
|
2900
|
+
exports.nestServerInstance = nestServerInstance;
|
|
2901
|
+
exports.noRunNameSpecifiedForScheduledFunctionDevelopmentFunction = noRunNameSpecifiedForScheduledFunctionDevelopmentFunction;
|
|
2902
|
+
exports.notFoundError = notFoundError;
|
|
2903
|
+
exports.onCallCreateModel = onCallCreateModel;
|
|
2904
|
+
exports.onCallDeleteModel = onCallDeleteModel;
|
|
2905
|
+
exports.onCallDevelopmentFunction = onCallDevelopmentFunction;
|
|
2906
|
+
exports.onCallHandlerWithNestApplicationFactory = onCallHandlerWithNestApplicationFactory;
|
|
2907
|
+
exports.onCallHandlerWithNestContextFactory = onCallHandlerWithNestContextFactory;
|
|
2908
|
+
exports.onCallModel = onCallModel;
|
|
2909
|
+
exports.onCallModelMissingCallTypeError = onCallModelMissingCallTypeError;
|
|
2910
|
+
exports.onCallModelUnknownCallTypeError = onCallModelUnknownCallTypeError;
|
|
2911
|
+
exports.onCallReadModel = onCallReadModel;
|
|
2912
|
+
exports.onCallSpecifierHandler = onCallSpecifierHandler;
|
|
2913
|
+
exports.onCallUpdateModel = onCallUpdateModel;
|
|
2914
|
+
exports.onScheduleHandlerWithNestApplicationFactory = onScheduleHandlerWithNestApplicationFactory;
|
|
2915
|
+
exports.onScheduleHandlerWithNestContextFactory = onScheduleHandlerWithNestContextFactory;
|
|
2916
|
+
exports.optionalAuthContext = optionalAuthContext;
|
|
2917
|
+
exports.permissionDeniedError = permissionDeniedError;
|
|
2918
|
+
exports.phoneNumberAlreadyExistsError = phoneNumberAlreadyExistsError;
|
|
2919
|
+
exports.preconditionConflictError = preconditionConflictError;
|
|
2920
|
+
exports.provideAppFirestoreCollections = provideAppFirestoreCollections;
|
|
2921
|
+
exports.provideFirebaseServerAuthService = provideFirebaseServerAuthService;
|
|
2922
|
+
exports.provideFirebaseServerStorageService = provideFirebaseServerStorageService;
|
|
2923
|
+
exports.readModelUnknownModelTypeError = readModelUnknownModelTypeError;
|
|
2924
|
+
exports.setNestContextOnRequest = setNestContextOnRequest;
|
|
2925
|
+
exports.setNestContextOnScheduleRequest = setNestContextOnScheduleRequest;
|
|
2926
|
+
exports.taskQueueFunctionHandlerWithNestContextFactory = taskQueueFunctionHandlerWithNestContextFactory;
|
|
2927
|
+
exports.unauthenticatedContextHasNoAuthData = unauthenticatedContextHasNoAuthData;
|
|
2928
|
+
exports.unauthenticatedContextHasNoUidError = unauthenticatedContextHasNoUidError;
|
|
2929
|
+
exports.unauthenticatedError = unauthenticatedError;
|
|
2930
|
+
exports.unavailableError = unavailableError;
|
|
2931
|
+
exports.unavailableOrDeactivatedFunctionError = unavailableOrDeactivatedFunctionError;
|
|
2932
|
+
exports.unknownModelCrudFunctionSpecifierError = unknownModelCrudFunctionSpecifierError;
|
|
2933
|
+
exports.unknownScheduledFunctionDevelopmentFunctionName = unknownScheduledFunctionDevelopmentFunctionName;
|
|
2934
|
+
exports.unknownScheduledFunctionDevelopmentFunctionType = unknownScheduledFunctionDevelopmentFunctionType;
|
|
2935
|
+
exports.updateModelUnknownModelTypeError = updateModelUnknownModelTypeError;
|
|
2936
|
+
exports.userContextFromUid = userContextFromUid;
|
|
2937
|
+
exports.verifyAppCheckInRequest = verifyAppCheckInRequest;
|