node-red-contrib-smartnora
Version:
Google Smart Home integration via Smart Nora https://smart-nora.eu/
100 lines (99 loc) • 4.78 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FirebaseConnection = void 0;
const app_1 = require("firebase/app");
const auth_1 = require("firebase/auth");
const rxjs_1 = require("rxjs");
const operators_1 = require("rxjs/operators");
const __1 = require("..");
const config_1 = require("../config");
const fetch_1 = require("./fetch");
const async_commands_registry_1 = require("./async-commands.registry");
const local_execution_1 = require("./local-execution");
const sync_1 = require("./sync");
class FirebaseConnection {
static withLogger(logger) {
var _a;
(_a = this.logger) !== null && _a !== void 0 ? _a : (this.logger = logger);
local_execution_1.LocalExecution.withLogger(logger);
async_commands_registry_1.AsyncCommandsRegistry.withLogger(logger);
return this;
}
static fromConfig(config, ctx) {
const key = (0, __1.getHash)(`${config.email}:${config.group}`);
let cached = this.configs[key];
if (!cached) {
cached = this.configs[key] = this.getAppFromConfig(config)
.pipe((0, operators_1.map)(app => new sync_1.FirebaseSync(app, config.group, this.logger)), (0, operators_1.retry)({
delay: err => {
var _a, _b;
const seconds = Math.round(Math.random() * 120) / 2 + 30;
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`nora: ${err}`);
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.warn(`nora: trying again in ${seconds} sec`);
return (0, rxjs_1.timer)(seconds * 1000);
}
}), (0, operators_1.finalize)(() => delete this.configs[key]), (0, __1.publishReplayRefCountWithDelay)(5000));
}
return cached.pipe((0, operators_1.switchMap)(connection => (0, rxjs_1.merge)(connection.connected$.pipe((0, operators_1.tap)(ctx.connected$), (0, operators_1.ignoreElements)()), (0, rxjs_1.of)(connection))));
}
static getAppFromConfig(config) {
const key = (0, __1.getHash)(`${config.email}`);
let cached = this.apps[key];
if (!cached) {
cached = this.apps[key] = this.createFirebaseApp().pipe((0, operators_1.switchMap)(async (app) => {
var _a, _b;
const result = await this.authenticate(app, config);
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.info(`nora: authenticated, uid: ${(_b = result.user) === null || _b === void 0 ? void 0 : _b.uid}`);
return app;
}), (0, operators_1.finalize)(() => delete this.apps[key]), (0, __1.publishReplayRefCountWithDelay)(5000));
}
return cached;
}
static async authenticate(app, config) {
var _a, _b, _c;
const auth = (0, auth_1.getAuth)(app);
if ((_a = config.password) === null || _a === void 0 ? void 0 : _a.length) {
return await (0, auth_1.signInWithEmailAndPassword)(auth, config.email, config.password);
}
else if ((_b = config.sso) === null || _b === void 0 ? void 0 : _b.length) {
const customToken = await this.exchangeToken(config.sso);
return await (0, auth_1.signInWithCustomToken)(auth, customToken);
}
else {
(_c = this.logger) === null || _c === void 0 ? void 0 : _c.error('nora: invalid auth config; not retrying');
return (0, rxjs_1.firstValueFrom)(rxjs_1.NEVER);
}
}
static async exchangeToken(ssoToken) {
var _a;
const url = `${config_1.API_ENDPOINT}/sso/exchange`;
const response = await (0, fetch_1.fetch)(url, {
method: 'POST',
headers: {
'user-agent': config_1.USER_AGENT,
},
body: {
token: ssoToken
},
});
if (response.status === 200) {
const { token } = await response.json();
return token;
}
if (response.status === 400) {
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`nora: invalid sso token; not retrying - ${await response.text()}`);
return (0, rxjs_1.firstValueFrom)(rxjs_1.NEVER);
}
throw new __1.HttpError(response.status, await response.text());
}
static createFirebaseApp() {
return new rxjs_1.Observable(observer => {
const app = (0, app_1.initializeApp)(config_1.FIREBASE_CONFIG, `app-${new Date().getTime()}`);
observer.next(app);
return () => (0, app_1.deleteApp)(app);
});
}
}
exports.FirebaseConnection = FirebaseConnection;
FirebaseConnection.configs = {};
FirebaseConnection.apps = {};