1 |
|
2 | "use strict";
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 | Object.defineProperty(exports, "__esModule", { value: true });
|
19 | exports.DatabaseService = void 0;
|
20 | var url_1 = require("url");
|
21 | var path = require("path");
|
22 | var error_1 = require("../utils/error");
|
23 | var validator = require("../utils/validator");
|
24 | var api_request_1 = require("../utils/api-request");
|
25 | var index_1 = require("../utils/index");
|
26 | var TOKEN_REFRESH_THRESHOLD_MILLIS = 5 * 60 * 1000;
|
27 | var DatabaseService = (function () {
|
28 | function DatabaseService(app) {
|
29 | this.databases = {};
|
30 | if (!validator.isNonNullObject(app) || !('options' in app)) {
|
31 | throw new error_1.FirebaseDatabaseError({
|
32 | code: 'invalid-argument',
|
33 | message: 'First argument passed to admin.database() must be a valid Firebase app instance.',
|
34 | });
|
35 | }
|
36 | this.appInternal = app;
|
37 | }
|
38 | Object.defineProperty(DatabaseService.prototype, "firebaseApp", {
|
39 | get: function () {
|
40 | return this.app;
|
41 | },
|
42 | enumerable: false,
|
43 | configurable: true
|
44 | });
|
45 | |
46 |
|
47 |
|
48 | DatabaseService.prototype.delete = function () {
|
49 | var _this = this;
|
50 | if (this.tokenListener) {
|
51 | this.firebaseApp.INTERNAL.removeAuthTokenListener(this.tokenListener);
|
52 | clearTimeout(this.tokenRefreshTimeout);
|
53 | }
|
54 | var promises = [];
|
55 | for (var _i = 0, _a = Object.keys(this.databases); _i < _a.length; _i++) {
|
56 | var dbUrl = _a[_i];
|
57 | var db = this.databases[dbUrl];
|
58 | promises.push(db.INTERNAL.delete());
|
59 | }
|
60 | return Promise.all(promises).then(function () {
|
61 | _this.databases = {};
|
62 | });
|
63 | };
|
64 | Object.defineProperty(DatabaseService.prototype, "app", {
|
65 | |
66 |
|
67 |
|
68 |
|
69 |
|
70 | get: function () {
|
71 | return this.appInternal;
|
72 | },
|
73 | enumerable: false,
|
74 | configurable: true
|
75 | });
|
76 | DatabaseService.prototype.getDatabase = function (url) {
|
77 | var dbUrl = this.ensureUrl(url);
|
78 | if (!validator.isNonEmptyString(dbUrl)) {
|
79 | throw new error_1.FirebaseDatabaseError({
|
80 | code: 'invalid-argument',
|
81 | message: 'Database URL must be a valid, non-empty URL string.',
|
82 | });
|
83 | }
|
84 | var db = this.databases[dbUrl];
|
85 | if (typeof db === 'undefined') {
|
86 |
|
87 | var rtdb = require('@firebase/database-compat/standalone');
|
88 | db = rtdb.initStandalone(this.appInternal, dbUrl, index_1.getSdkVersion()).instance;
|
89 | var rulesClient_1 = new DatabaseRulesClient(this.app, dbUrl);
|
90 | db.getRules = function () {
|
91 | return rulesClient_1.getRules();
|
92 | };
|
93 | db.getRulesJSON = function () {
|
94 | return rulesClient_1.getRulesJSON();
|
95 | };
|
96 | db.setRules = function (source) {
|
97 | return rulesClient_1.setRules(source);
|
98 | };
|
99 | this.databases[dbUrl] = db;
|
100 | }
|
101 | if (!this.tokenListener) {
|
102 | this.tokenListener = this.onTokenChange.bind(this);
|
103 | this.firebaseApp.INTERNAL.addAuthTokenListener(this.tokenListener);
|
104 | }
|
105 | return db;
|
106 | };
|
107 |
|
108 | DatabaseService.prototype.onTokenChange = function (_) {
|
109 | var token = this.firebaseApp.INTERNAL.getCachedToken();
|
110 | if (token) {
|
111 | var delayMillis = token.expirationTime - TOKEN_REFRESH_THRESHOLD_MILLIS - Date.now();
|
112 |
|
113 |
|
114 | if (delayMillis > 0) {
|
115 | this.scheduleTokenRefresh(delayMillis);
|
116 | }
|
117 | }
|
118 | };
|
119 | DatabaseService.prototype.scheduleTokenRefresh = function (delayMillis) {
|
120 | var _this = this;
|
121 | clearTimeout(this.tokenRefreshTimeout);
|
122 | this.tokenRefreshTimeout = setTimeout(function () {
|
123 | _this.firebaseApp.INTERNAL.getToken( true)
|
124 | .catch(function () {
|
125 |
|
126 |
|
127 |
|
128 | });
|
129 | }, delayMillis);
|
130 | };
|
131 | DatabaseService.prototype.ensureUrl = function (url) {
|
132 | if (typeof url !== 'undefined') {
|
133 | return url;
|
134 | }
|
135 | else if (typeof this.appInternal.options.databaseURL !== 'undefined') {
|
136 | return this.appInternal.options.databaseURL;
|
137 | }
|
138 | throw new error_1.FirebaseDatabaseError({
|
139 | code: 'invalid-argument',
|
140 | message: 'Can\'t determine Firebase Database URL.',
|
141 | });
|
142 | };
|
143 | return DatabaseService;
|
144 | }());
|
145 | exports.DatabaseService = DatabaseService;
|
146 | var RULES_URL_PATH = '.settings/rules.json';
|
147 |
|
148 |
|
149 |
|
150 | var DatabaseRulesClient = (function () {
|
151 | function DatabaseRulesClient(app, dbUrl) {
|
152 | var parsedUrl = new url_1.URL(dbUrl);
|
153 | var emulatorHost = process.env.FIREBASE_DATABASE_EMULATOR_HOST;
|
154 | if (emulatorHost) {
|
155 | var namespace = extractNamespace(parsedUrl);
|
156 | parsedUrl = new url_1.URL("http://" + emulatorHost + "?ns=" + namespace);
|
157 | }
|
158 | parsedUrl.pathname = path.join(parsedUrl.pathname, RULES_URL_PATH);
|
159 | this.dbUrl = parsedUrl.toString();
|
160 | this.httpClient = new api_request_1.AuthorizedHttpClient(app);
|
161 | }
|
162 | |
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 | DatabaseRulesClient.prototype.getRules = function () {
|
169 | var _this = this;
|
170 | var req = {
|
171 | method: 'GET',
|
172 | url: this.dbUrl,
|
173 | };
|
174 | return this.httpClient.send(req)
|
175 | .then(function (resp) {
|
176 | if (!resp.text) {
|
177 | throw new error_1.FirebaseAppError(error_1.AppErrorCodes.INTERNAL_ERROR, 'HTTP response missing data.');
|
178 | }
|
179 | return resp.text;
|
180 | })
|
181 | .catch(function (err) {
|
182 | throw _this.handleError(err);
|
183 | });
|
184 | };
|
185 | |
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 | DatabaseRulesClient.prototype.getRulesJSON = function () {
|
192 | var _this = this;
|
193 | var req = {
|
194 | method: 'GET',
|
195 | url: this.dbUrl,
|
196 | data: { format: 'strict' },
|
197 | };
|
198 | return this.httpClient.send(req)
|
199 | .then(function (resp) {
|
200 | return resp.data;
|
201 | })
|
202 | .catch(function (err) {
|
203 | throw _this.handleError(err);
|
204 | });
|
205 | };
|
206 | |
207 |
|
208 |
|
209 |
|
210 |
|
211 |
|
212 |
|
213 |
|
214 | DatabaseRulesClient.prototype.setRules = function (source) {
|
215 | var _this = this;
|
216 | if (!validator.isNonEmptyString(source) &&
|
217 | !validator.isBuffer(source) &&
|
218 | !validator.isNonNullObject(source)) {
|
219 | var error = new error_1.FirebaseDatabaseError({
|
220 | code: 'invalid-argument',
|
221 | message: 'Source must be a non-empty string, Buffer or an object.',
|
222 | });
|
223 | return Promise.reject(error);
|
224 | }
|
225 | var req = {
|
226 | method: 'PUT',
|
227 | url: this.dbUrl,
|
228 | data: source,
|
229 | headers: {
|
230 | 'content-type': 'application/json; charset=utf-8',
|
231 | },
|
232 | };
|
233 | return this.httpClient.send(req)
|
234 | .then(function () {
|
235 | return;
|
236 | })
|
237 | .catch(function (err) {
|
238 | throw _this.handleError(err);
|
239 | });
|
240 | };
|
241 | DatabaseRulesClient.prototype.handleError = function (err) {
|
242 | if (err instanceof api_request_1.HttpError) {
|
243 | return new error_1.FirebaseDatabaseError({
|
244 | code: error_1.AppErrorCodes.INTERNAL_ERROR,
|
245 | message: this.getErrorMessage(err),
|
246 | });
|
247 | }
|
248 | return err;
|
249 | };
|
250 | DatabaseRulesClient.prototype.getErrorMessage = function (err) {
|
251 | var intro = 'Error while accessing security rules';
|
252 | try {
|
253 | var body = err.response.data;
|
254 | if (body && body.error) {
|
255 | return intro + ": " + body.error.trim();
|
256 | }
|
257 | }
|
258 | catch (_a) {
|
259 |
|
260 | }
|
261 | return intro + ": " + err.response.text;
|
262 | };
|
263 | return DatabaseRulesClient;
|
264 | }());
|
265 | function extractNamespace(parsedUrl) {
|
266 | var ns = parsedUrl.searchParams.get('ns');
|
267 | if (ns) {
|
268 | return ns;
|
269 | }
|
270 | var hostname = parsedUrl.hostname;
|
271 | var dotIndex = hostname.indexOf('.');
|
272 | return hostname.substring(0, dotIndex).toLowerCase();
|
273 | }
|