@hawtio/react
Version:
A Hawtio reimplementation based on TypeScript + React.
1,234 lines (1,177 loc) • 46.3 kB
JavaScript
;Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5;var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
// src/core/logging.ts
var logging_exports = {};
__export(logging_exports, {
Logger: () => Logger,
STORAGE_KEY_CHILD_LOGGERS: () => STORAGE_KEY_CHILD_LOGGERS,
STORAGE_KEY_LOG_LEVEL: () => STORAGE_KEY_LOG_LEVEL
});
// src/util/strings.ts
function isBlank(str) {
if (str === void 0 || str === null) {
return true;
}
if (typeof str !== "string") {
return false;
}
return str.trim().length === 0;
}
function toString(obj) {
if (!obj) {
return "{}";
}
const strs = Object.entries(obj).map(([key, value]) => {
let obscured = value;
if (key.toLowerCase() === "password") {
obscured = obfuscate(value);
} else if (typeof value === "object") {
obscured = toString(obscured);
}
return `${key}: ${obscured}`;
});
return `{ ${strs.join(", ")} }`;
}
function obfuscate(str) {
if (typeof str !== "string") {
return "";
}
return str.split("").map((_) => "*").join("");
}
function trimStart(text, chars) {
return text.replace(new RegExp(`^[${chars}]+`, "g"), "");
}
function trimEnd(text, chars) {
return text.replace(new RegExp(`[${chars}]+$`, "g"), "");
}
function trimQuotes(text) {
if (text && text.length > 0) {
const headTrimmed = trimStart(text, `'"`);
if (headTrimmed.length < text.length) {
return trimEnd(headTrimmed, `'"`);
}
}
return text;
}
function stringSorter(a, b, sortDesc) {
let res = a.localeCompare(b);
if (sortDesc) {
res *= -1;
}
return res;
}
function parseBoolean(value) {
if (!value) return false;
return /^true$/i.test(value) || parseInt(value) === 1;
}
function humanizeLabels(str) {
return str.split("-").filter((str2) => !isBlank(str2)).map((str2) => str2.replace(/^./, (str3) => str3.toUpperCase())).join(" ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b([A-Z]+)([A-Z])([a-z])/, "$1 $2$3").replace("M Bean", "MBean").replace("Mbean", "MBean").replace(/^./, (str2) => str2.toUpperCase()).replace(/ +/, " ").trim();
}
function matchWithWildcard(value, pattern) {
if (!pattern.includes("*")) {
return value === pattern;
}
const rule = pattern.split("*").map((s) => s.replace(/([.*+?^=!:${}()|[\]/\\])/g, "\\$1")).join(".*");
const regexp = new RegExp(`^${rule}$`, "i");
return value.match(regexp) !== null;
}
// src/core/logging.ts
__reExport(logging_exports, js_logger_star);
var _jslogger = require('js-logger'); var js_logger_star = _interopRequireWildcard(_jslogger);
var _superstruct = require('superstruct');
var STORAGE_KEY_LOG_LEVEL = "core.logging.logLevel";
var STORAGE_KEY_CHILD_LOGGERS = "core.logging.childLoggers";
var LocalStorageHawtioLogger = (_class = class {
__init() {this.TRACE = js_logger_star.default.TRACE}
__init2() {this.DEBUG = js_logger_star.default.DEBUG}
__init3() {this.INFO = js_logger_star.default.INFO}
__init4() {this.TIME = js_logger_star.default.TIME}
__init5() {this.WARN = js_logger_star.default.WARN}
__init6() {this.ERROR = js_logger_star.default.ERROR}
__init7() {this.OFF = js_logger_star.default.OFF}
__init8() {this.trace = js_logger_star.default.trace}
__init9() {this.debug = js_logger_star.default.debug}
__init10() {this.info = js_logger_star.default.info}
__init11() {this.log = js_logger_star.default.log}
__init12() {this.warn = js_logger_star.default.warn}
__init13() {this.error = js_logger_star.default.error}
__init14() {this.time = js_logger_star.default.time}
__init15() {this.timeEnd = js_logger_star.default.timeEnd}
__init16() {this.getLevel = js_logger_star.default.getLevel}
__init17() {this.enabledFor = js_logger_star.default.enabledFor}
__init18() {this.useDefaults = js_logger_star.default.useDefaults}
__init19() {this.setHandler = js_logger_star.default.setHandler}
// 'typeof jsLogger.createDefaultHandler' is a hack as otherwise tsc complains TS4029 error
__init20() {this.createDefaultHandler = js_logger_star.default.createDefaultHandler}
__init21() {this.LOG_LEVEL_MAP = {
TRACE: this.TRACE,
DEBUG: this.DEBUG,
INFO: this.INFO,
TIME: this.TIME,
WARN: this.WARN,
ERROR: this.ERROR,
OFF: this.OFF
}}
get(name) {
let logger = this.loggers[name];
if (logger) {
return logger;
}
logger = js_logger_star.default.get(name);
this.loggers[name] = logger;
return logger;
}
setLevel(level) {
const logLevel = this.toLogLevel(level);
js_logger_star.default.setLevel(logLevel);
this.saveLogLevel(logLevel);
}
__init22() {this.loggers = {}}
constructor() {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this);_class.prototype.__init6.call(this);_class.prototype.__init7.call(this);_class.prototype.__init8.call(this);_class.prototype.__init9.call(this);_class.prototype.__init10.call(this);_class.prototype.__init11.call(this);_class.prototype.__init12.call(this);_class.prototype.__init13.call(this);_class.prototype.__init14.call(this);_class.prototype.__init15.call(this);_class.prototype.__init16.call(this);_class.prototype.__init17.call(this);_class.prototype.__init18.call(this);_class.prototype.__init19.call(this);_class.prototype.__init20.call(this);_class.prototype.__init21.call(this);_class.prototype.__init22.call(this);
try {
const logLevel = this.loadLogLevel();
js_logger_star.default.setLevel(logLevel);
} catch (e) {
console.error("Failed to load log level from local storage:", e);
js_logger_star.default.setLevel(this.INFO);
}
try {
const childLoggers = this.loadChildLoggers();
childLoggers.forEach((logger) => this.get(logger.name).setLevel(logger.filterLevel));
} catch (e) {
console.error("Failed to load child loggers from local storage:", e);
}
this.setHandler(this.createDefaultHandler());
}
toLogLevel(level) {
if (typeof level !== "string") {
return level;
}
const logLevel = this.LOG_LEVEL_MAP[level];
if (!logLevel) {
console.error("Unknown log level:", level);
return this.INFO;
}
return logLevel;
}
loadLogLevel() {
const logLevel = localStorage.getItem(STORAGE_KEY_LOG_LEVEL);
return logLevel ? JSON.parse(logLevel) : this.INFO;
}
saveLogLevel(level) {
localStorage.setItem(STORAGE_KEY_LOG_LEVEL, JSON.stringify(level));
}
loadChildLoggers() {
const childLoggers = localStorage.getItem(STORAGE_KEY_CHILD_LOGGERS);
return childLoggers ? JSON.parse(childLoggers) : [];
}
saveChildLoggers(loggers) {
localStorage.setItem(STORAGE_KEY_CHILD_LOGGERS, JSON.stringify(loggers));
}
getChildLoggers() {
const childLoggers = this.loadChildLoggers();
childLoggers.sort((a, b) => stringSorter(a.name, b.name));
return childLoggers;
}
getAvailableChildLoggers() {
const allLoggers = [];
Object.values(this.loggers).forEach((logger) => {
if (_superstruct.is.call(void 0, logger, _superstruct.type.call(void 0, { context: _superstruct.object.call(void 0, ) }))) {
allLoggers.push(logger.context);
} else {
console.error("Logger does not have context:", logger);
}
});
const childLoggers = this.getChildLoggers();
const availableLoggers = allLoggers.filter((logger) => !childLoggers.some((l) => l.name === logger.name));
availableLoggers.sort((a, b) => stringSorter(a.name, b.name));
return availableLoggers;
}
addChildLogger(logger) {
const childLoggers = this.getChildLoggers();
childLoggers.push(logger);
this.saveChildLoggers(childLoggers);
this.get(logger.name).setLevel(logger.filterLevel);
}
updateChildLogger(name, level) {
const logLevel = this.toLogLevel(level);
const updated = this.getChildLoggers().map((logger) => {
if (logger.name === name) {
logger.filterLevel = logLevel;
}
return logger;
});
this.saveChildLoggers(updated);
this.get(name).setLevel(logLevel);
}
removeChildLogger(logger) {
const removed = this.getChildLoggers().filter((l) => l.name !== logger.name);
this.saveChildLoggers(removed);
this.get(logger.name).setLevel(this.getLevel());
}
}, _class);
var Logger = new LocalStorageHawtioLogger();
// src/auth/globals.ts
var moduleName = "hawtio-auth";
var log = Logger.get(moduleName);
var PUBLIC_USER = "public";
var PATH_USER = "user";
var PATH_LOGIN = "auth/login";
var PATH_LOGOUT = "auth/logout";
// src/core/config-manager.ts
var log2 = Logger.get("hawtio-core-config");
var DEFAULT_APP_NAME = "Hawtio Management Console";
var DEFAULT_LOGIN_TITLE = "Log in to your account";
var HAWTCONFIG_JSON = "hawtconfig.json";
var TaskState = /* @__PURE__ */ ((TaskState2) => {
TaskState2[TaskState2["started"] = 0] = "started";
TaskState2[TaskState2["skipped"] = 1] = "skipped";
TaskState2[TaskState2["finished"] = 2] = "finished";
TaskState2[TaskState2["error"] = 3] = "error";
return TaskState2;
})(TaskState || {});
var AuthenticationResult = /* @__PURE__ */ ((AuthenticationResult2) => {
AuthenticationResult2[AuthenticationResult2["ok"] = 0] = "ok";
AuthenticationResult2[AuthenticationResult2["configuration_error"] = 1] = "configuration_error";
AuthenticationResult2[AuthenticationResult2["connect_error"] = 2] = "connect_error";
AuthenticationResult2[AuthenticationResult2["security_context_error"] = 3] = "security_context_error";
return AuthenticationResult2;
})(AuthenticationResult || {});
var ConfigManager = (_class2 = class {constructor() { _class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this); }
/** Configuration object read from `hawtconfig.json` and/or built programmatically */
/** List of initialization tasks to be presented in `<HawtioInitialization>` component */
__init23() {this.initTasks = {}}
/** Listeners notified about initialization task state changes */
__init24() {this.initListeners = []}
/** Configuration of available authentication methods, used by login-related components */
__init25() {this.authenticationConfig = []}
/** Resolution method for `authenticationConfigPromise` */
__init26() {this.authenticationConfigReady = () => true}
__init27() {this.authenticationConfigEndpointSupported = false}
/** Promise resolved when authentication config is already read from external source */
__init28() {this.authenticationConfigPromise = new Promise((resolve) => {
this.authenticationConfigReady = resolve;
})}
// --- External Public API (IConfigManager)
async addProductInfo(name, value) {
const config = await this.getHawtconfig();
if (!config.about) {
config.about = {};
}
if (!config.about.productInfo) {
config.about.productInfo = [];
}
config.about.productInfo.push({ name, value });
}
initItem(item, ready, group) {
this.initTasks[item] = { ready, group };
setTimeout(() => {
for (const l of this.initListeners) {
l(this.initTasks);
}
}, 0);
}
globalLogLevel() {
return Logger.getLevel().value;
}
// --- Public API
/**
* This method is called by `hawtio.bootstrap()`, so we have a single point where global (not plugin-specific)
* configuration is loaded
*/
async initialize() {
this.initItem("Checking authentication providers", 0 /* started */, "config");
const defaultConfiguration = {
method: "form",
name: "Form Authentication",
url: PATH_LOGIN,
logoutUrl: PATH_LOGOUT,
type: "json",
userField: "username",
passwordField: "password"
};
this.authenticationConfig = await fetch("auth/config/login").then((response) => {
if (response.ok) {
this.authenticationConfigEndpointSupported = true;
return response.json();
} else {
if (response.status === 404) {
this.authenticationConfigEndpointSupported = false;
} else {
this.authenticationConfigEndpointSupported = true;
}
return [];
}
}).then((json) => {
if (Array.isArray(json)) {
return json.length == 0 ? [defaultConfiguration] : json;
} else {
return [defaultConfiguration];
}
}).catch((e) => {
log2.debug("Problem fetching authentication providers", e);
return [defaultConfiguration];
});
const indexMap = /* @__PURE__ */ new Map();
this.authenticationConfig.forEach((m) => {
if (!m.position) {
if (!indexMap.has(m.method)) {
indexMap.set(m.method, 0);
}
const idx = indexMap.get(m.method);
m.position = idx;
indexMap.set(m.method, idx + 1);
}
});
this.authenticationConfigReady(true);
this.initItem("Checking authentication providers", 2 /* finished */, "config");
return true;
}
/**
* Called by plugins to augment generic authentication method config.
* Plugins may provide additional details, like location of OIDC provider.
* @param config
*/
async configureAuthenticationMethod(config) {
let found = false;
return this.authenticationConfigPromise.then(() => {
for (const idx in this.authenticationConfig) {
if (_optionalChain([this, 'access', _2 => _2.authenticationConfig, 'access', _3 => _3[idx], 'optionalAccess', _4 => _4.method]) === config.method && _optionalChain([this, 'access', _5 => _5.authenticationConfig, 'access', _6 => _6[idx], 'optionalAccess', _7 => _7.position]) === config.position) {
const name = this.authenticationConfig[idx].name;
this.authenticationConfig[idx] = {
...config,
name
};
found = true;
break;
}
}
if (!found) {
let name = _nullishCoalesce(config["name"], () => ( config.method));
if (name == "oidc") {
name = "OpenID Connect";
}
if (name == "keycloak") {
name = "Keycloak";
}
if (this.authenticationConfigEndpointSupported) {
this.authenticationConfig.push({ ...config, name });
} else {
this.authenticationConfig = [{ ...config, name }];
}
}
});
}
/**
* Get configured authentication methods - possibly augmented by plugins. This method should
* be called from hooks and React components, so can be done only after hawtio.bootstrap() promise is
* resolved. This ensures that plugins already finished their configuration.
*/
getAuthenticationConfig() {
return this.authenticationConfig;
}
/**
* Returns selected authentication method by name
* @param method
* @param idx
*/
getAuthenticationMethod(method, idx) {
return this.authenticationConfig.find((am) => am.method === method && am.position === idx);
}
/**
* Set new `Hawtconfig` object as the configuration
* @param config
*/
setHawtconfig(config) {
this.config = Promise.resolve(config);
}
/**
* Returns currently configured `Hawtconfig` object
*/
async getHawtconfig() {
if (this.config) {
return this.config;
}
this.config = this.loadConfig();
return this.config;
}
/**
* Resets current `Hawtconfig` object to undefined state
*/
reset() {
this.config = void 0;
}
/**
* Loads configuration from `hawtconfig.json`.
* @private
*/
async loadConfig() {
log2.info("Loading", HAWTCONFIG_JSON);
this.initItem("Loading " + HAWTCONFIG_JSON, 0 /* started */, "config");
try {
const res = await fetch(HAWTCONFIG_JSON);
if (!res.ok) {
log2.error("Failed to fetch", HAWTCONFIG_JSON, "-", res.status, res.statusText);
this.initItem("Loading " + HAWTCONFIG_JSON, 1 /* skipped */, "config");
return {};
}
const config = await res.json();
log2.debug(HAWTCONFIG_JSON, "=", config);
log2.info("Loaded", HAWTCONFIG_JSON);
this.initItem("Loading " + HAWTCONFIG_JSON, 2 /* finished */, "config");
return config;
} catch (err) {
log2.error("Error fetching", HAWTCONFIG_JSON, "-", err);
this.initItem("Loading " + HAWTCONFIG_JSON, 1 /* skipped */, "config");
return {};
}
}
/**
* Plugins may use this method to change parts, or entire `hawtconfig.json` configuration.
* @param configurer
*/
async configure(configurer) {
const config = await this.getHawtconfig();
configurer(config);
}
/**
* Apply loaded configuration to application (titles, links, icons). Should be called during Hawtio bootstrap.
*/
async applyBranding() {
this.initItem("Applying branding", 0 /* started */, "config");
const { branding } = await this.getHawtconfig();
if (!branding) {
this.initItem("Applying branding", 1 /* skipped */, "config");
return false;
}
log2.info("Apply branding", branding);
let applied = false;
if (branding.appName) {
log2.info("Updating title -", branding.appName);
document.title = branding.appName;
applied = true;
}
if (branding.css) {
this.updateHref("#branding", branding.css, true);
applied = true;
}
if (branding.favicon) {
this.updateHref("#favicon", branding.favicon);
applied = true;
}
this.initItem("Applying branding", applied ? 2 /* finished */ : 1 /* skipped */, "config");
return applied;
}
/**
* Alters `href` attribute of selected DOM element (for icons, styles)
* @param id
* @param path
* @param moveToLast
* @private
*/
updateHref(id, path, moveToLast = false) {
log2.info("Updating href for", id, "-", path, moveToLast);
const elm = document.querySelector(id);
if (!elm) {
return;
}
if ("disabled" in elm) {
elm.disabled = true;
}
elm.setAttribute("href", path);
if (moveToLast) {
elm.remove();
_optionalChain([document, 'access', _8 => _8.querySelector, 'call', _9 => _9("head"), 'optionalAccess', _10 => _10.append, 'call', _11 => _11(elm)]);
}
if ("disabled" in elm) {
elm.disabled = false;
}
}
async isRouteEnabled(path) {
const { disabledRoutes } = await this.getHawtconfig();
return !disabledRoutes || !disabledRoutes.includes(path);
}
/**
* Filter loaded plugins, so only plugins which are not explicitly disabled in `hawtconfig.json` are used.
* @param plugins
*/
async filterEnabledPlugins(plugins) {
const { disabledRoutes } = await this.getHawtconfig();
const enabledPlugins = [];
for (const plugin of plugins) {
if (plugin.path == null && await plugin.isActive() || !disabledRoutes || !disabledRoutes.includes(plugin.path)) {
enabledPlugins.push(plugin);
} else {
log2.debug(`Plugin "${plugin.id}" disabled by hawtconfig.json`);
}
}
return enabledPlugins;
}
/**
* Returns current state of initialization tasks
*/
getInitializationTasks() {
return this.initTasks;
}
/**
* Register a listener to be notified about state changes of initialization tasks
* @param listener
*/
addInitListener(listener) {
this.initListeners.push(listener);
}
/**
* Unregister previously registered listener for state changes of initialization tasks
* @param listener
*/
removeInitListener(listener) {
this.initListeners.splice(this.initListeners.indexOf(listener), 1);
}
/**
* Are all the initialization items completed? The returned promise will be asynchronously resolved when
* initialization is finished.
*
* When the configuration is _ready_, Hawtio may proceed to rendering UI.
*/
async ready() {
return new Promise((resolve, _reject) => {
const h = setInterval(() => {
const result = Object.values(this.initTasks).find((v) => v.ready == 0 /* started */ || v.ready == 3 /* error */) == void 0;
if (result) {
resolve(true);
clearInterval(h);
}
}, 100);
});
}
}, _class2);
var configManager = new ConfigManager();
// src/core/index.ts
var core_exports = {};
__export(core_exports, {
AuthenticationResult: () => AuthenticationResult,
ConfigManager: () => ConfigManager,
DEFAULT_APP_NAME: () => DEFAULT_APP_NAME,
DEFAULT_LOGIN_TITLE: () => DEFAULT_LOGIN_TITLE,
EVENT_LOGIN: () => EVENT_LOGIN,
EVENT_LOGOUT: () => EVENT_LOGOUT,
EVENT_NOTIFY: () => EVENT_NOTIFY,
EVENT_PLUGINS_UPDATED: () => EVENT_PLUGINS_UPDATED,
EVENT_REFRESH: () => EVENT_REFRESH,
HAWTCONFIG_JSON: () => HAWTCONFIG_JSON,
HawtioCore: () => HawtioCore,
Logger: () => Logger,
STORAGE_KEY_CHILD_LOGGERS: () => STORAGE_KEY_CHILD_LOGGERS,
STORAGE_KEY_LOG_LEVEL: () => STORAGE_KEY_LOG_LEVEL,
TaskState: () => TaskState,
configManager: () => configManager,
eventService: () => eventService,
hawtio: () => hawtio,
isUniversalHeaderItem: () => isUniversalHeaderItem,
useHawtconfig: () => useHawtconfig,
usePlugins: () => usePlugins
});
// src/auth/hooks.ts
var _react = require('react');
function useUser() {
const [username, setUsername] = _react.useState.call(void 0, "");
const [isLogin, setIsLogin] = _react.useState.call(void 0, false);
const [isLoginError, setIsLoginError] = _react.useState.call(void 0, false);
const [loginError, setLoginError] = _react.useState.call(void 0, "");
const [userLoaded, setUserLoaded] = _react.useState.call(void 0, false);
const [loginMethod, setLoginMethod] = _react.useState.call(void 0, "");
_react.useEffect.call(void 0, () => {
let proceed = true;
const isProceed = () => proceed;
const fetchUser = async () => {
await userService.fetchUser(false, () => isProceed());
const loginMethod2 = await userService.getLoginMethod();
const isLoginError2 = await userService.isLoginError();
const loginError2 = isLoginError2 ? await userService.loginError() : "";
const username2 = isLoginError2 ? "" : await userService.getUsername();
const isLogin2 = isLoginError2 ? false : await userService.isLogin();
if (isProceed()) {
setUsername(_nullishCoalesce(username2, () => ( "")));
setIsLogin(isLogin2);
setIsLoginError(isLoginError2);
setLoginMethod(loginMethod2);
setLoginError(loginError2);
setUserLoaded(true);
}
};
fetchUser();
return () => {
proceed = false;
};
}, []);
return { username, isLogin, userLoaded, loginMethod, isLoginError, loginError };
}
// src/core/core.ts
var _utilities = require('@module-federation/utilities');
// src/core/event-service.ts
var _eventemitter3 = require('eventemitter3'); var _eventemitter32 = _interopRequireDefault(_eventemitter3);
var log3 = Logger.get("hawtio-core-event");
var EVENT_NOTIFY = "notify";
var EVENT_LOGIN = "login";
var EVENT_LOGOUT = "logout";
var EVENT_REFRESH = "refresh";
var EVENT_PLUGINS_UPDATED = "pluginsUpdated";
var DEFAULT_DURATION = 8e3;
var EventService = (_class3 = class {constructor() { _class3.prototype.__init29.call(this); }
__init29() {this.eventEmitter = new (0, _eventemitter32.default)()}
notify(notification) {
if (!notification.duration) {
notification.duration = DEFAULT_DURATION;
}
this.eventEmitter.emit(EVENT_NOTIFY, notification);
}
onNotify(listener) {
this.eventEmitter.on(EVENT_NOTIFY, listener);
log3.debug("Number of listeners on", EVENT_NOTIFY, "=", this.eventEmitter.listenerCount(EVENT_NOTIFY));
}
login() {
this.eventEmitter.emit(EVENT_LOGIN);
}
onLogin(listener) {
this.eventEmitter.on(EVENT_LOGIN, listener);
}
logout() {
this.eventEmitter.emit(EVENT_LOGOUT);
}
onLogout(listener) {
this.eventEmitter.on(EVENT_LOGOUT, listener);
}
refresh() {
this.eventEmitter.emit(EVENT_REFRESH);
}
onRefresh(listener) {
this.eventEmitter.on(EVENT_REFRESH, listener);
}
pluginsUpdated() {
this.eventEmitter.emit(EVENT_PLUGINS_UPDATED);
}
onPluginsUpdated(listener) {
this.eventEmitter.on(EVENT_PLUGINS_UPDATED, listener);
}
removeListener(event, listener) {
this.eventEmitter.removeListener(event, listener);
}
}, _class3);
var eventService = new EventService();
// src/core/globals.ts
var moduleName2 = "hawtio-core";
var log4 = Logger.get(moduleName2);
// src/core/core.ts
var DEFAULT_REMOTE_ENTRY_FILE = "remoteEntry.js";
var DEFAULT_PLUGIN_ORDER = 100;
var DEFAULT_PLUGIN_ENTRY = "plugin";
var HAWTIO_DISABLE_THEME_LISTENER = "hawtio.disableThemeListener";
var PATTERNFLY_THEME_CLASS = "pf-v5-theme-dark";
function isUniversalHeaderItem(item) {
return "component" in item && "universal" in item && typeof item.universal === "boolean";
}
var HawtioCore = (_class4 = class {constructor() { _class4.prototype.__init30.call(this);_class4.prototype.__init31.call(this);_class4.prototype.__init32.call(this);_class4.prototype.__init33.call(this);_class4.prototype.__init34.call(this); }
/**
* Hawtio base path.
*/
/**
* List of URLs that the plugin loader will try and discover plugins from.
*/
__init30() {this.urls = []}
/**
* Holds all of the Hawtio plugins that need to be bootstrapped.
*/
__init31() {this.plugins = {}}
/**
* Holds all of the Hawtio plugins which will be evaluated in deferred way.
*/
__init32() {this.deferredPlugins = {}}
/**
* The Window Theme Listener callback function
*/
__init33() {this.windowThemeListener = () => {
this.updateFromTheme();
}}
/**
* Flag set once the window theme listener has been added
*/
__init34() {this.windowListenerAdded = false}
// --- External Public API (IHawtio)
addPlugin(plugin) {
log4.info("Add plugin:", plugin.id);
configManager.initItem("Registering plugin: " + plugin.id, 0 /* started */, "plugins");
if (this.plugins[plugin.id]) {
throw new Error(`Plugin "${plugin.id}" already exists`);
}
this.plugins[plugin.id] = plugin;
setTimeout(() => {
configManager.initItem("Registering plugin: " + plugin.id, 2 /* finished */, "plugins");
}, 0);
return this;
}
addDeferredPlugin(id, deferred) {
log4.info("Add deferred plugin:", id);
configManager.initItem("Registering deferred plugin: " + id, 0 /* started */, "plugins");
if (this.deferredPlugins[id]) {
throw new Error(`Deferred plugin "${id}" already exists`);
}
if (this.plugins[id]) {
throw new Error(`Conflict registering a deferred plugin with id="${id}". Plugin already registered.`);
}
this.deferredPlugins[id] = deferred;
return this;
}
addUrl(url) {
if (URL.canParse(url)) {
} else {
url = new URL(url, document.baseURI).href;
}
log4.info("Add URL:", url);
this.urls.push(url);
return this;
}
async bootstrap() {
log4.info("Bootstrapping Hawtio...");
await configManager.initialize();
await this.loadPlugins();
const brandingApplied = await configManager.applyBranding();
log4.info("Branding applied:", brandingApplied);
log4.info("Bootstrapped Hawtio");
configManager.initItem("Finish", 2 /* finished */, "finish");
return configManager.ready().then(() => {
log4.debug("configManager.ready() resolved");
return true;
});
}
// --- Public API
/**
* Returns all plugins registered using different plugin registration methods.
*/
getPlugins() {
return Object.values(this.plugins);
}
/**
* Resolves which of registered plugins are active in current environment.
*
* There are two types of plugins: normal plugins and login plugins.
* If it's normal, it's only resolved when the user is already logged in.
* If it's login, it's only resolved when the user is not logged in yet, and thus
* can only affects the login page.
*
* Therefore, this method depends on the login status provided by the `userService`.
*/
async resolvePlugins() {
const userLoggedIn = await userService.isLogin();
log4.debug("Resolve plugins: login =", userLoggedIn);
const resolved = [];
for (const plugin of this.getPlugins()) {
log4.debug("Resolve plugin:", plugin.id);
if (userLoggedIn && plugin.isLogin || !userLoggedIn && !plugin.isLogin) {
continue;
}
if (await plugin.isActive()) {
resolved.push(plugin);
}
}
log4.debug("Resolved plugins:", resolved);
resolved.sort((a, b) => (_nullishCoalesce(a.order, () => ( DEFAULT_PLUGIN_ORDER))) - (_nullishCoalesce(b.order, () => ( DEFAULT_PLUGIN_ORDER))));
return resolved;
}
/**
* Sets the base path of the Hawtio console.
* If the given path includes trailing '/', it will be trimmed.
*/
setBasePath(path) {
if (path.length > 1 && path.endsWith("/")) {
this.basePath = path.slice(0, -1);
} else {
this.basePath = path;
}
}
/**
* Returns the base path of the Hawtio console without trailing '/'.
*/
getBasePath() {
if (!this.basePath) {
const basePath = this.documentBase();
log4.info("Base path from html head:", basePath);
if (basePath && basePath.length > 1 && basePath.endsWith("/")) {
this.basePath = basePath.slice(0, -1);
} else {
this.basePath = basePath;
}
}
return this.basePath;
}
/**
* Adds an event listener to the window theme to update
* css values in the event of a change in theme
*/
addWindowThemeListener() {
if (this.windowListenerAdded) {
return;
}
const disableListener = _nullishCoalesce(localStorage.getItem(HAWTIO_DISABLE_THEME_LISTENER), () => ( "false"));
if (disableListener === "true") {
return;
}
this.updateFromTheme();
this.themeList().addEventListener("change", this.windowThemeListener);
this.windowListenerAdded = true;
}
/**
* Removes the event listener to the window theme
*/
removeWindowThemeListener() {
this.themeList().removeEventListener("change", this.windowThemeListener);
this.windowListenerAdded = false;
}
// --- private methods
/**
* Returns the base URL specified in html/head/base element, href attribute. It should end with trailing '/'.
* Specified base affects how `fetch()` global function works.
* @private
*/
documentBase() {
const base = document.querySelector("head base");
if (base) {
return _nullishCoalesce(base.getAttribute("href"), () => ( void 0));
}
return void 0;
}
/**
* Downloads plugins from any configured URLs and loads them.
* It is invoked at Hawtio's bootstrapping.
*
* This plugin mechanism is implemented using [Webpack Module Federation](https://module-federation.github.io/).
*/
async loadPlugins() {
if (this.urls.length === 0 && Object.entries(this.deferredPlugins).length === 0) {
log4.info("No URLs provided to load external plugins and no deferred plugins registered");
return;
}
const numBefore = Object.keys(this.plugins).length;
log4.info(numBefore, "plugins before loading:", { ...this.plugins });
await Promise.all(this.urls.map(this.loadExternalPlugins));
configManager.initItem("Loading deferred plugins", 0 /* started */, "plugins");
await this.loadDeferredPlugins();
configManager.initItem("Loading deferred plugins", 2 /* finished */, "plugins");
const numAfter = Object.keys(this.plugins).length;
log4.info(numAfter, "plugins after loaded:", this.plugins);
if (numBefore !== numAfter) {
log4.debug("Notify plugins update");
eventService.pluginsUpdated();
}
}
/**
* Loads external plugins from the given URL. The URL endpoint is expected to
* return an array of HawtioRemote objects. These are not strictly "Hawtio plugins", but rather
* "remote entry points" that when called may register Hawtio plugins (with `hawtio.addPlugin()` or
* `hawtio.addDeferredPlugin()`).
*/
async loadExternalPlugins(url) {
log4.debug("Trying url:", url);
try {
configManager.initItem("Loading plugins descriptor from URL " + url, 0 /* started */, "plugins");
const res = await fetch(url);
if (!res.ok) {
if (res.status == 401 || res.status == 403) {
configManager.initItem("Loading plugins descriptor from URL " + url, 3 /* error */, "plugins");
log4.error("Failed to fetch url:", url, "-", res.status, res.statusText);
} else {
configManager.initItem("Loading plugins descriptor from URL " + url, 1 /* skipped */, "plugins");
log4.warn("Failed to fetch url:", url, "-", res.status, res.statusText);
}
return;
}
const remotes = await res.json();
log4.info("Loaded remotes from url:", url, "=", remotes);
configManager.initItem("Loading plugins descriptor from URL " + url, 2 /* finished */, "plugins");
return Promise.all(
remotes.map(async (remote) => {
let url2;
if (typeof remote.url === "function") {
url2 = await remote.url();
} else {
url2 = remote.url;
}
if (!url2.endsWith("/")) {
url2 += "/";
}
if (URL.canParse(url2)) {
url2 += _nullishCoalesce(remote.remoteEntryFileName, () => ( DEFAULT_REMOTE_ENTRY_FILE));
} else {
url2 = new URL(url2 + (_nullishCoalesce(remote.remoteEntryFileName, () => ( DEFAULT_REMOTE_ENTRY_FILE))), document.baseURI).href;
}
log4.info("Loading remote", remote);
try {
configManager.initItem("Importing plugin from: " + url2, 0 /* started */, "plugins");
const plugin = await _utilities.importRemote.call(void 0, remote);
const entryFn = plugin[remote.pluginEntry || DEFAULT_PLUGIN_ENTRY];
if (!entryFn) {
configManager.initItem("Importing plugin from: " + url2, 1 /* skipped */, "plugins");
log4.info("Ignored remote", remote, "(no entry point available)");
return Promise.resolve(true);
} else {
const result = entryFn();
let resultPromise;
if (result instanceof Promise) {
resultPromise = result;
} else {
resultPromise = Promise.resolve(result);
}
return resultPromise.then(() => {
configManager.initItem("Importing plugin from: " + url2, 2 /* finished */, "plugins");
log4.info("Loaded remote", remote);
});
}
} catch (err) {
configManager.initItem("Importing plugin from: " + url2, 3 /* error */, "plugins");
log4.error("Error loading remote:", remote, "-", err);
return Promise.resolve(false);
}
})
);
} catch (err) {
configManager.initItem("Loading plugins descriptor from URL " + url, 1 /* skipped */, "plugins");
log4.warn("Error fetching url:", url, "-", err);
return;
}
}
/**
* Evaluate all deferred plugins, so they are added to `plugins` array of available plugins
* @private
*/
async loadDeferredPlugins() {
log4.debug("Loading deferred plugins");
return Promise.all(
Object.entries(this.deferredPlugins).map(async (e) => {
const [id, deferred] = e;
return deferred().then((result) => {
let plugins;
if (!Array.isArray(result)) {
plugins = [result];
} else {
plugins = result;
}
for (const plugin of plugins) {
if (this.plugins[plugin.id]) {
throw new Error(`Plugin "${plugin.id}" already exists`);
}
this.plugins[plugin.id] = plugin;
}
configManager.initItem("Registering deferred plugin: " + id, 2 /* finished */, "plugins");
}).catch((err) => {
log4.error("Error registering deferred plugin:", id, "-", err);
configManager.initItem("Registering deferred plugin: " + id, 3 /* error */, "plugins");
});
})
);
}
// Actual window theme query
themeList() {
return window.matchMedia("(prefers-color-scheme: dark)");
}
/**
* Detect what theme the browser has been set to and
* return 'dark' | 'light'
*/
windowTheme() {
return this.themeList().matches ? "dark" : "light";
}
/**
* Update the document root with the PatternFly dark class
* see https://www.patternfly.org/developer-resources/dark-theme-handbook
*/
updateFromTheme() {
if (this.windowTheme() === "dark") {
document.documentElement.classList.add(PATTERNFLY_THEME_CLASS);
} else {
document.documentElement.classList.remove(PATTERNFLY_THEME_CLASS);
}
}
}, _class4);
var hawtio = new HawtioCore();
// src/core/hooks.ts
function usePlugins() {
const [plugins, setPlugins] = _react.useState.call(void 0, []);
const [pluginsLoaded, setPluginsLoaded] = _react.useState.call(void 0, false);
log4.debug("usePlugins - Plugins:", hawtio.getPlugins());
_react.useEffect.call(void 0, () => {
const loadPlugins = async () => {
const activePlugins = await hawtio.resolvePlugins();
const enabledPlugins = await configManager.filterEnabledPlugins(activePlugins);
setPlugins(enabledPlugins);
setPluginsLoaded(true);
};
loadPlugins();
eventService.onPluginsUpdated(loadPlugins);
return () => eventService.removeListener(EVENT_PLUGINS_UPDATED, loadPlugins);
}, []);
return { plugins, pluginsLoaded };
}
function useHawtconfig() {
const [hawtconfig, setHawtconfig] = _react.useState.call(void 0, {});
const [hawtconfigLoaded, setHawtconfigLoaded] = _react.useState.call(void 0, false);
_react.useEffect.call(void 0, () => {
const loadHawtconfig = async () => {
setHawtconfig(await configManager.getHawtconfig());
setHawtconfigLoaded(true);
};
loadHawtconfig();
}, []);
return { hawtconfig, hawtconfigLoaded };
}
// src/core/index.ts
__reExport(core_exports, logging_exports);
// src/auth/user-service.ts
var UserService = (_class5 = class {
/** The main promise resolving to `User` instance. That's why we need full browser redirect on logout. */
/** user promise resolve method - to be called by registered auth plugins or default auth plugin */
__init35() {this.resolveUser = () => {
}}
/** Result of fetching user with plugins. When it indicates an error `user` promise won't be resolved */
__init36() {this.userAuthResult = null}
/** Named authentication hooks used to fetch information about actual user logged into Hawtio. */
__init37() {this.fetchUserHooks = {}}
/** Named authentication hooks used to logout the user. */
__init38() {this.logoutHooks = {}}
/** Bearer Token to be used by Jolokia, set by plugins on successful authentication */
// TODO: Jolokia service should use auth plugins to configure the headers
__init39() {this.token = null}
constructor() {;_class5.prototype.__init35.call(this);_class5.prototype.__init36.call(this);_class5.prototype.__init37.call(this);_class5.prototype.__init38.call(this);_class5.prototype.__init39.call(this);
this.user = new Promise((resolve) => {
this.resolveUser = resolve;
});
}
addFetchUserHook(name, hook) {
this.fetchUserHooks[name] = hook;
configManager.initItem(`Registration of ${name} auth hook`, 2 /* finished */, "config");
}
addLogoutHook(name, hook) {
this.logoutHooks[name] = hook;
}
/**
* Sync login status with the server by fetching login user.
*/
async fetchUser(retry = true, proceed) {
for (const [name, fetchUser] of Object.entries(this.fetchUserHooks)) {
const result = await fetchUser(this.resolveUser, proceed);
if (proceed && !proceed()) {
return;
}
this.userAuthResult = result;
log.debug("Invoke fetch user hook", name, ": resolved =", result);
if (!result.isIgnore) {
if (!result.isError) {
eventService.login();
}
return;
}
}
await this.defaultFetchUser(retry, proceed);
}
/**
* Default user fetching logic that checks `/user` endpoint that returns json string value with named/logged-in user
* @param retry
* @param proceed
* @private
*/
async defaultFetchUser(retry = true, proceed) {
try {
const res = await fetch(PATH_USER);
if (!res.ok) {
log.error("Failed to fetch user:", res.status, res.statusText);
if (retry && res.status === 403) {
await new Promise((resolve) => setTimeout(resolve, 1e3));
return this.defaultFetchUser(false, proceed);
}
this.resolveUser({ username: PUBLIC_USER, isLogin: false, loginMethod: "form" });
return;
}
const username = await res.json();
log.info("Logged in as:", username);
this.resolveUser({ username, isLogin: true, loginMethod: "form" });
eventService.login();
} catch (err) {
log.debug("Failed to get logged-in user from", PATH_USER, "-", err);
this.resolveUser({ username: PUBLIC_USER, isLogin: false, loginMethod: "form" });
}
}
async getUsername() {
return this.userAuthResult == null || !this.userAuthResult.isError ? (await this.user).username : null;
}
async isLogin() {
return this.userAuthResult == null || !this.userAuthResult.isError ? (await this.user).isLogin : false;
}
async isLoginError() {
return this.userAuthResult != null && this.userAuthResult.isError;
}
async loginError() {
return this.userAuthResult != null && this.userAuthResult.isError ? this.userAuthResult.errorMessage : null;
}
async getLoginMethod() {
return this.userAuthResult != null ? this.userAuthResult.loginMethod : (await this.user).loginMethod;
}
getToken() {
return this.token;
}
setToken(token) {
this.token = token;
}
async logout() {
const login = await this.user;
if (!login.isLogin) {
log.debug("Not logged in");
return false;
}
log.info("Log out:", login.username, "Login method:", login.loginMethod);
let attempted = false;
let proceed = false;
for (const [name, logout] of Object.entries(this.logoutHooks)) {
if (name !== login.loginMethod) {
continue;
}
attempted = true;
const result = await logout();
log.debug("Invoke logout hook", name, ": result =", result);
if (result) {
proceed = true;
break;
}
}
if (attempted) {
if (proceed) {
eventService.logout();
return true;
} else {
return false;
}
}
eventService.logout();
log.debug("Redirect to:", PATH_LOGOUT);
window.location.href = PATH_LOGOUT;
return true;
}
}, _class5);
var userService = new UserService();
var __testing__ = {
UserService
};
exports.__export = __export; exports.__reExport = __reExport; exports.isBlank = isBlank; exports.toString = toString; exports.trimQuotes = trimQuotes; exports.stringSorter = stringSorter; exports.parseBoolean = parseBoolean; exports.humanizeLabels = humanizeLabels; exports.matchWithWildcard = matchWithWildcard; exports.STORAGE_KEY_LOG_LEVEL = STORAGE_KEY_LOG_LEVEL; exports.STORAGE_KEY_CHILD_LOGGERS = STORAGE_KEY_CHILD_LOGGERS; exports.Logger = Logger; exports.logging_exports = logging_exports; exports.PUBLIC_USER = PUBLIC_USER; exports.DEFAULT_APP_NAME = DEFAULT_APP_NAME; exports.DEFAULT_LOGIN_TITLE = DEFAULT_LOGIN_TITLE; exports.HAWTCONFIG_JSON = HAWTCONFIG_JSON; exports.TaskState = TaskState; exports.AuthenticationResult = AuthenticationResult; exports.ConfigManager = ConfigManager; exports.configManager = configManager; exports.userService = userService; exports.__testing__ = __testing__; exports.useUser = useUser; exports.EVENT_NOTIFY = EVENT_NOTIFY; exports.EVENT_LOGIN = EVENT_LOGIN; exports.EVENT_LOGOUT = EVENT_LOGOUT; exports.EVENT_REFRESH = EVENT_REFRESH; exports.EVENT_PLUGINS_UPDATED = EVENT_PLUGINS_UPDATED; exports.eventService = eventService; exports.isUniversalHeaderItem = isUniversalHeaderItem; exports.HawtioCore = HawtioCore; exports.hawtio = hawtio; exports.usePlugins = usePlugins; exports.useHawtconfig = useHawtconfig; exports.core_exports = core_exports;
//# sourceMappingURL=chunk-BHIEXRGK.js.map