123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- "use strict";
- Object.defineProperty(exports, "__esModule", { value: true });
- exports.DatabaseService = void 0;
- const url_1 = require("url");
- const path = require("path");
- const error_1 = require("../utils/error");
- const validator = require("../utils/validator");
- const api_request_1 = require("../utils/api-request");
- const index_1 = require("../utils/index");
- const TOKEN_REFRESH_THRESHOLD_MILLIS = 5 * 60 * 1000;
- class DatabaseService {
- constructor(app) {
- this.databases = {};
- if (!validator.isNonNullObject(app) || !('options' in app)) {
- throw new error_1.FirebaseDatabaseError({
- code: 'invalid-argument',
- message: 'First argument passed to admin.database() must be a valid Firebase app instance.',
- });
- }
- this.appInternal = app;
- }
- get firebaseApp() {
- return this.app;
- }
-
- delete() {
- if (this.tokenListener) {
- this.firebaseApp.INTERNAL.removeAuthTokenListener(this.tokenListener);
- clearTimeout(this.tokenRefreshTimeout);
- }
- const promises = [];
- for (const dbUrl of Object.keys(this.databases)) {
- const db = this.databases[dbUrl];
- promises.push(db.INTERNAL.delete());
- }
- return Promise.all(promises).then(() => {
- this.databases = {};
- });
- }
-
- get app() {
- return this.appInternal;
- }
- getDatabase(url) {
- const dbUrl = this.ensureUrl(url);
- if (!validator.isNonEmptyString(dbUrl)) {
- throw new error_1.FirebaseDatabaseError({
- code: 'invalid-argument',
- message: 'Database URL must be a valid, non-empty URL string.',
- });
- }
- let db = this.databases[dbUrl];
- if (typeof db === 'undefined') {
-
- const rtdb = require('@firebase/database-compat/standalone');
- db = rtdb.initStandalone(this.appInternal, dbUrl, (0, index_1.getSdkVersion)()).instance;
- const rulesClient = new DatabaseRulesClient(this.app, dbUrl);
- db.getRules = () => {
- return rulesClient.getRules();
- };
- db.getRulesJSON = () => {
- return rulesClient.getRulesJSON();
- };
- db.setRules = (source) => {
- return rulesClient.setRules(source);
- };
- this.databases[dbUrl] = db;
- }
- if (!this.tokenListener) {
- this.tokenListener = this.onTokenChange.bind(this);
- this.firebaseApp.INTERNAL.addAuthTokenListener(this.tokenListener);
- }
- return db;
- }
-
- onTokenChange(_) {
- const token = this.firebaseApp.INTERNAL.getCachedToken();
- if (token) {
- const delayMillis = token.expirationTime - TOKEN_REFRESH_THRESHOLD_MILLIS - Date.now();
-
-
- if (delayMillis > 0) {
- this.scheduleTokenRefresh(delayMillis);
- }
- }
- }
- scheduleTokenRefresh(delayMillis) {
- clearTimeout(this.tokenRefreshTimeout);
- this.tokenRefreshTimeout = setTimeout(() => {
- this.firebaseApp.INTERNAL.getToken( true)
- .catch(() => {
-
-
-
- });
- }, delayMillis);
- }
- ensureUrl(url) {
- if (typeof url !== 'undefined') {
- return url;
- }
- else if (typeof this.appInternal.options.databaseURL !== 'undefined') {
- return this.appInternal.options.databaseURL;
- }
- throw new error_1.FirebaseDatabaseError({
- code: 'invalid-argument',
- message: 'Can\'t determine Firebase Database URL.',
- });
- }
- }
- exports.DatabaseService = DatabaseService;
- const RULES_URL_PATH = '.settings/rules.json';
- class DatabaseRulesClient {
- constructor(app, dbUrl) {
- let parsedUrl = new url_1.URL(dbUrl);
- const emulatorHost = process.env.FIREBASE_DATABASE_EMULATOR_HOST;
- if (emulatorHost) {
- const namespace = extractNamespace(parsedUrl);
- parsedUrl = new url_1.URL(`http://${emulatorHost}?ns=${namespace}`);
- }
- parsedUrl.pathname = path.join(parsedUrl.pathname, RULES_URL_PATH);
- this.dbUrl = parsedUrl.toString();
- this.httpClient = new api_request_1.AuthorizedHttpClient(app);
- }
-
- getRules() {
- const req = {
- method: 'GET',
- url: this.dbUrl,
- };
- return this.httpClient.send(req)
- .then((resp) => {
- if (!resp.text) {
- throw new error_1.FirebaseAppError(error_1.AppErrorCodes.INTERNAL_ERROR, 'HTTP response missing data.');
- }
- return resp.text;
- })
- .catch((err) => {
- throw this.handleError(err);
- });
- }
-
- getRulesJSON() {
- const req = {
- method: 'GET',
- url: this.dbUrl,
- data: { format: 'strict' },
- };
- return this.httpClient.send(req)
- .then((resp) => {
- return resp.data;
- })
- .catch((err) => {
- throw this.handleError(err);
- });
- }
-
- setRules(source) {
- if (!validator.isNonEmptyString(source) &&
- !validator.isBuffer(source) &&
- !validator.isNonNullObject(source)) {
- const error = new error_1.FirebaseDatabaseError({
- code: 'invalid-argument',
- message: 'Source must be a non-empty string, Buffer or an object.',
- });
- return Promise.reject(error);
- }
- const req = {
- method: 'PUT',
- url: this.dbUrl,
- data: source,
- headers: {
- 'content-type': 'application/json; charset=utf-8',
- },
- };
- return this.httpClient.send(req)
- .then(() => {
- return;
- })
- .catch((err) => {
- throw this.handleError(err);
- });
- }
- handleError(err) {
- if (err instanceof api_request_1.HttpError) {
- return new error_1.FirebaseDatabaseError({
- code: error_1.AppErrorCodes.INTERNAL_ERROR,
- message: this.getErrorMessage(err),
- });
- }
- return err;
- }
- getErrorMessage(err) {
- const intro = 'Error while accessing security rules';
- try {
- const body = err.response.data;
- if (body && body.error) {
- return `${intro}: ${body.error.trim()}`;
- }
- }
- catch {
-
- }
- return `${intro}: ${err.response.text}`;
- }
- }
- function extractNamespace(parsedUrl) {
- const ns = parsedUrl.searchParams.get('ns');
- if (ns) {
- return ns;
- }
- const hostname = parsedUrl.hostname;
- const dotIndex = hostname.indexOf('.');
- return hostname.substring(0, dotIndex).toLowerCase();
- }
|