@cocalc/hub
Version:
CoCalc: Backend webserver component
166 lines • 6.31 kB
JavaScript
;
/*
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
* License: AGPLv3 s.t. "Commons Clause" – see LICENSE.md for details
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.WebappConfiguration = exports.clear_cache = void 0;
const software_envs_1 = require("@cocalc/server/software-envs");
const async_utils_1 = require("@cocalc/util/async-utils");
const site_settings_extras_1 = require("@cocalc/util/db-schema/site-settings-extras");
const schema_1 = require("@cocalc/util/schema");
const awaiting_1 = require("awaiting");
const debug_1 = __importDefault(require("debug"));
const parse_domain_1 = require("parse-domain");
const auth_1 = require("./auth");
const server_settings_1 = __importDefault(require("./servers/server-settings"));
const utils_1 = require("./utils");
const L = (0, debug_1.default)("hub:webapp-config");
const lru_cache_1 = __importDefault(require("lru-cache"));
const CACHE = new lru_cache_1.default({ max: 1000, maxAge: 60 * 1000 }); // 1 minutes
function clear_cache() {
CACHE.reset();
}
exports.clear_cache = clear_cache;
async function get_passport_manager_async() {
// the only issue here is, that the http server already starts up before the
// passport manager is configured – but, the passport manager depends on the http server
// we just retry during that initial period of uncertainty…
let ms = 100;
while (true) {
const pp_manager = (0, auth_1.get_passport_manager)();
if (pp_manager != null) {
return pp_manager;
}
else {
L(`WARNING: Passport Manager not available yet -- trying again in ${ms}ms`);
await (0, awaiting_1.delay)(ms);
ms = Math.min(10000, 1.3 * ms);
}
}
}
class WebappConfiguration {
constructor({ db }) {
this.db = db;
this.init();
}
async init() {
// this.data.pub updates automatically – do not modify it!
this.data = await (0, server_settings_1.default)();
this.passport_manager = await get_passport_manager_async();
}
// server settings with whitelabeling settings
// TODO post-process all values
async settings(vID) {
const res = await (0, async_utils_1.callback2)(this.db._query, {
query: "SELECT id, settings FROM whitelabeling",
cache: true,
where: { "id = $::TEXT": vID },
});
if (this.data == null) {
// settings not yet initialized
return {};
}
const data = res.rows[0];
if (data != null) {
return { ...this.data.all, ...data.settings };
}
else {
return this.data.all;
}
}
// derive the vanity ID from the host string
get_vanity_id(host) {
const host_parsed = (0, parse_domain_1.parseDomain)(host);
if (host_parsed.type === parse_domain_1.ParseResultType.Listed) {
// vanity for vanity.cocalc.com or foo.p for foo.p.cocalc.com
return host_parsed.subDomains.join(".");
}
return undefined;
}
async theme(vID) {
const res = await (0, async_utils_1.callback2)(this.db._query, {
query: "SELECT id, theme FROM whitelabeling",
cache: true,
where: { "id = $::TEXT": vID },
});
const data = res.rows[0];
if (data != null) {
// post-process data, but do not set default values…
const theme = {};
for (const [key, value] of Object.entries(data.theme)) {
const config = schema_1.site_settings_conf[key] ?? site_settings_extras_1.EXTRAS[key];
if (typeof config?.to_val == "function") {
theme[key] = config.to_val(value, data.theme);
}
else {
if (typeof value == "string" || typeof value == "boolean") {
theme[key] = value;
}
}
}
L(`vanity theme=${JSON.stringify(theme)}`);
return theme;
}
else {
L(`theme id=${vID} not found`);
return {};
}
}
async get_vanity(vID) {
if (vID != null && vID !== "") {
L(`vanity ID = "${vID}"`);
return await this.theme(vID);
}
else {
return {};
}
}
// returns the global configuration + eventually vanity specific site config settings
async get_configuration({ host, country }) {
if (this.data == null) {
// settings not yet initialized
return {};
}
const vID = this.get_vanity_id(host);
const config = this.data.pub;
const vanity = this.get_vanity(vID);
return { ...config, ...vanity, ...{ country, dns: host } };
}
get_strategies() {
const key = "strategies";
let strategies = CACHE.get(key);
if (strategies == null) {
strategies = this.passport_manager.get_strategies_v2();
CACHE.set(key, strategies);
}
return strategies;
}
async get_config({ country, host }) {
const [configuration, registration, software] = await Promise.all([
this.get_configuration({ host, country }),
(0, utils_1.have_active_registration_tokens)(this.db),
(0, software_envs_1.getSoftwareEnvironments)("webapp"),
]);
const strategies = this.get_strategies();
return { configuration, registration, strategies, software };
}
// it returns a shallow copy, hence you can modify/add keys in the returned map!
async get({ country, host }) {
const key = `config::${country}::${host}`;
let config = CACHE.get(key);
if (config == null) {
config = await this.get_config({ country, host });
CACHE.set(key, config);
}
else {
L(`cache hit -- '${key}'`);
}
return config;
}
}
exports.WebappConfiguration = WebappConfiguration;
//# sourceMappingURL=webapp-configuration.js.map