mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: add select of Tenant ID (#3673)
* feat: add select of tenantID * feat: replace tenantID to default url * fix: move the tenantID selector to the top header * fix: hide tenantID selector by condition * fix: correct z-index * app/vmselect/vmui: `make vmui-update` --------- Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
51ad94677c
commit
ac14d50c18
23 changed files with 235 additions and 99 deletions
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"files": {
|
||||
"main.css": "./static/css/main.9ca6b743.css",
|
||||
"main.js": "./static/js/main.8969be5f.js",
|
||||
"main.css": "./static/css/main.9c397960.css",
|
||||
"main.js": "./static/js/main.df0f4d01.js",
|
||||
"static/js/27.c1ccfd29.chunk.js": "./static/js/27.c1ccfd29.chunk.js",
|
||||
"static/media/Lato-Regular.ttf": "./static/media/Lato-Regular.d714fec1633b69a9c2e9.ttf",
|
||||
"static/media/Lato-Bold.ttf": "./static/media/Lato-Bold.32360ba4b57802daa4d6.ttf",
|
||||
"index.html": "./index.html"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.9ca6b743.css",
|
||||
"static/js/main.8969be5f.js"
|
||||
"static/css/main.9c397960.css",
|
||||
"static/js/main.df0f4d01.js"
|
||||
]
|
||||
}
|
|
@ -1 +1 @@
|
|||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.8969be5f.js"></script><link href="./static/css/main.9ca6b743.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="UI for VictoriaMetrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><script src="./dashboards/index.js" type="module"></script><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="./preview.jpg"><meta name="twitter:title" content="UI for VictoriaMetrics"><meta name="twitter:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta name="twitter:site" content="@VictoriaMetrics"><meta property="og:title" content="Metric explorer for VictoriaMetrics"><meta property="og:description" content="Explore and troubleshoot your VictoriaMetrics data"><meta property="og:image" content="./preview.jpg"><meta property="og:type" content="website"><script defer="defer" src="./static/js/main.df0f4d01.js"></script><link href="./static/css/main.9c397960.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
1
app/vmselect/vmui/static/css/main.9c397960.css
Normal file
1
app/vmselect/vmui/static/css/main.9c397960.css
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
app/vmselect/vmui/static/js/main.df0f4d01.js
Normal file
2
app/vmselect/vmui/static/js/main.df0f4d01.js
Normal file
File diff suppressed because one or more lines are too long
2
app/vmui/packages/vmui/src/api/accountId.ts
Normal file
2
app/vmui/packages/vmui/src/api/accountId.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const getAccountIds = (server: string) =>
|
||||
`${server.replace(/^(.+)(\/select.+)/, "$1")}/admin/tenants`;
|
|
@ -1,6 +1,4 @@
|
|||
import React, { FC } from "preact/compat";
|
||||
import { getAppModeParams } from "../../../utils/app-mode";
|
||||
import TenantsConfiguration from "../TenantsConfiguration/TenantsConfiguration";
|
||||
import { useCustomPanelDispatch, useCustomPanelState } from "../../../state/customPanel/CustomPanelStateContext";
|
||||
import { useQueryDispatch, useQueryState } from "../../../state/query/QueryStateContext";
|
||||
import "./style.scss";
|
||||
|
@ -8,8 +6,6 @@ import Switch from "../../Main/Switch/Switch";
|
|||
|
||||
const AdditionalSettings: FC = () => {
|
||||
|
||||
const { inputTenantID } = getAppModeParams();
|
||||
|
||||
const { autocomplete } = useQueryState();
|
||||
const queryDispatch = useQueryDispatch();
|
||||
|
||||
|
@ -44,11 +40,6 @@ const AdditionalSettings: FC = () => {
|
|||
value={isTracingEnabled}
|
||||
onChange={onChangeQueryTracing}
|
||||
/>
|
||||
{!!inputTenantID && (
|
||||
<div className="vm-additional-settings__input">
|
||||
<TenantsConfiguration/>
|
||||
</div>
|
||||
)}
|
||||
</div>;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { FC, useState } from "preact/compat";
|
||||
import React, { FC, useEffect, useState } from "preact/compat";
|
||||
import ServerConfigurator from "./ServerConfigurator/ServerConfigurator";
|
||||
import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
|
||||
import { SettingsIcon } from "../../Main/Icons";
|
||||
|
@ -43,6 +43,11 @@ const GlobalSettings: FC = () => {
|
|||
handleClose();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (stateServerUrl === serverUrl) return;
|
||||
setServerUrl(stateServerUrl);
|
||||
}, [stateServerUrl]);
|
||||
|
||||
return <>
|
||||
<Tooltip title={title}>
|
||||
<Button
|
||||
|
@ -92,13 +97,13 @@ const GlobalSettings: FC = () => {
|
|||
color="error"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={handlerApply}
|
||||
>
|
||||
apply
|
||||
apply
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -22,18 +22,14 @@ const ServerConfigurator: FC<ServerConfiguratorProps> = ({ serverUrl, onChange ,
|
|||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="vm-server-configurator__title">
|
||||
Server URL
|
||||
</div>
|
||||
<TextField
|
||||
autofocus
|
||||
value={serverUrl}
|
||||
error={error}
|
||||
onChange={onChangeServer}
|
||||
onEnter={onEnter}
|
||||
/>
|
||||
</div>
|
||||
<TextField
|
||||
autofocus
|
||||
label="Server URL"
|
||||
value={serverUrl}
|
||||
error={error}
|
||||
onChange={onChangeServer}
|
||||
onEnter={onEnter}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import React, { FC, useState, useRef, useEffect, useMemo } from "preact/compat";
|
||||
import { useAppDispatch, useAppState } from "../../../../state/common/StateContext";
|
||||
import { useTimeDispatch } from "../../../../state/time/TimeStateContext";
|
||||
import { ArrowDownIcon, StorageIcons } from "../../../Main/Icons";
|
||||
import Button from "../../../Main/Button/Button";
|
||||
import { useFetchAccountIds } from "./hooks/useFetchAccountIds";
|
||||
import "./style.scss";
|
||||
import { replaceTenantId } from "../../../../utils/default-server-url";
|
||||
import classNames from "classnames";
|
||||
import Popper from "../../../Main/Popper/Popper";
|
||||
import { getAppModeEnable } from "../../../../utils/app-mode";
|
||||
import Tooltip from "../../../Main/Tooltip/Tooltip";
|
||||
|
||||
const TenantsConfiguration: FC = () => {
|
||||
const appModeEnable = getAppModeEnable();
|
||||
|
||||
const { tenantId: tenantIdState, serverUrl } = useAppState();
|
||||
const dispatch = useAppDispatch();
|
||||
const timeDispatch = useTimeDispatch();
|
||||
const { accountIds } = useFetchAccountIds();
|
||||
|
||||
const [openOptions, setOpenOptions] = useState(false);
|
||||
const optionsButtonRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const getTenantIdFromUrl = (url: string) => {
|
||||
const regexp = /(\/select\/)(\d+|\d.+)(\/)(.+)/;
|
||||
return (url.match(regexp) || [])[2];
|
||||
};
|
||||
|
||||
const showTenantSelector = useMemo(() => {
|
||||
const id = getTenantIdFromUrl(serverUrl);
|
||||
return accountIds.length > 1 && id;
|
||||
}, [accountIds, serverUrl]);
|
||||
|
||||
const toggleOpenOptions = () => {
|
||||
setOpenOptions(prev => !prev);
|
||||
};
|
||||
|
||||
const handleCloseOptions = () => {
|
||||
setOpenOptions(false);
|
||||
};
|
||||
|
||||
const createHandlerChange = (value: string) => () => {
|
||||
const tenant = value;
|
||||
dispatch({ type: "SET_TENANT_ID", payload: tenant });
|
||||
if (serverUrl) {
|
||||
const updateServerUrl = replaceTenantId(serverUrl, tenant);
|
||||
if (updateServerUrl === serverUrl) return;
|
||||
console.log("SET_SERVER", updateServerUrl);
|
||||
dispatch({ type: "SET_SERVER", payload: updateServerUrl });
|
||||
timeDispatch({ type: "RUN_QUERY" });
|
||||
}
|
||||
handleCloseOptions();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const id = getTenantIdFromUrl(serverUrl);
|
||||
|
||||
if (tenantIdState && tenantIdState !== id) {
|
||||
createHandlerChange(tenantIdState)();
|
||||
} else {
|
||||
createHandlerChange(id)();
|
||||
}
|
||||
}, [serverUrl]);
|
||||
|
||||
if (!showTenantSelector) return null;
|
||||
|
||||
return (
|
||||
<div className="vm-tenant-input">
|
||||
<Tooltip title="Define Tenant ID if you need request to another storage">
|
||||
<div ref={optionsButtonRef}>
|
||||
<Button
|
||||
className={appModeEnable ? "" : "vm-header-button"}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
startIcon={<StorageIcons/>}
|
||||
endIcon={(
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-execution-controls-buttons__arrow": true,
|
||||
"vm-execution-controls-buttons__arrow_open": openOptions,
|
||||
})}
|
||||
>
|
||||
<ArrowDownIcon/>
|
||||
</div>
|
||||
)}
|
||||
onClick={toggleOpenOptions}
|
||||
>
|
||||
{tenantIdState}
|
||||
</Button>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Popper
|
||||
open={openOptions}
|
||||
placement="bottom-left"
|
||||
onClose={handleCloseOptions}
|
||||
buttonRef={optionsButtonRef}
|
||||
fullWidth
|
||||
>
|
||||
<div className="vm-list">
|
||||
{accountIds.map(id => (
|
||||
<div
|
||||
className={classNames({
|
||||
"vm-list-item": true,
|
||||
"vm-list-item_active": id === tenantIdState
|
||||
})}
|
||||
key={id}
|
||||
onClick={createHandlerChange(id)}
|
||||
>
|
||||
{id}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Popper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TenantsConfiguration;
|
|
@ -0,0 +1,41 @@
|
|||
import { useAppState } from "../../../../../state/common/StateContext";
|
||||
import { useEffect, useMemo, useState } from "preact/compat";
|
||||
import { ErrorTypes } from "../../../../../types";
|
||||
import { getAccountIds } from "../../../../../api/accountId";
|
||||
|
||||
export const useFetchAccountIds = () => {
|
||||
const { serverUrl } = useAppState();
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<ErrorTypes | string>();
|
||||
const [accountIds, setAccountIds] = useState<string[]>([]);
|
||||
|
||||
const fetchUrl = useMemo(() => getAccountIds(serverUrl), [serverUrl]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response = await fetch(fetchUrl);
|
||||
const resp = await response.json();
|
||||
const data = (resp.data || []) as string[];
|
||||
setAccountIds(data);
|
||||
|
||||
if (response.ok) {
|
||||
setError(undefined);
|
||||
} else {
|
||||
setError(`${resp.errorType}\r\n${resp?.error}`);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
setError(`${e.name}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
fetchData().catch(console.error);
|
||||
}, [fetchUrl]);
|
||||
|
||||
return { accountIds, isLoading, error };
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
@use "../../../../styles/variables" as *;
|
||||
|
||||
.vm-tenant-input {
|
||||
position: relative;
|
||||
}
|
|
@ -8,9 +8,15 @@
|
|||
|
||||
&__input {
|
||||
|
||||
&_server {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
gap: 0 $padding-small;
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
grid-column: auto / span 2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
import React, { FC, useState, useEffect, useCallback } from "preact/compat";
|
||||
import { useAppDispatch, useAppState } from "../../../state/common/StateContext";
|
||||
import debounce from "lodash.debounce";
|
||||
import { getAppModeParams } from "../../../utils/app-mode";
|
||||
import { useTimeDispatch } from "../../../state/time/TimeStateContext";
|
||||
import { InfoIcon } from "../../Main/Icons";
|
||||
import TextField from "../../Main/TextField/TextField";
|
||||
import Button from "../../Main/Button/Button";
|
||||
import Tooltip from "../../Main/Tooltip/Tooltip";
|
||||
|
||||
const TenantsConfiguration: FC = () => {
|
||||
const { serverURL } = getAppModeParams();
|
||||
const { tenantId: tenantIdState } = useAppState();
|
||||
const dispatch = useAppDispatch();
|
||||
const timeDispatch = useTimeDispatch();
|
||||
|
||||
const [tenantId, setTenantId] = useState<string | number>(tenantIdState || 0);
|
||||
|
||||
const handleApply = (value: string | number) => {
|
||||
const tenantId = Number(value);
|
||||
dispatch({ type: "SET_TENANT_ID", payload: tenantId });
|
||||
if (serverURL) {
|
||||
const updateServerUrl = serverURL.replace(/(\/select\/)([\d]+)(\/prometheus)/, `$1${tenantId}$3`);
|
||||
dispatch({ type: "SET_SERVER", payload: updateServerUrl });
|
||||
timeDispatch({ type: "RUN_QUERY" });
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedHandleApply = useCallback(debounce(handleApply, 700), []);
|
||||
|
||||
const handleChange = (value: string) => {
|
||||
setTenantId(value);
|
||||
debouncedHandleApply(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (tenantId === tenantIdState) return;
|
||||
setTenantId(tenantIdState);
|
||||
}, [tenantIdState]);
|
||||
|
||||
return <TextField
|
||||
label="Tenant ID"
|
||||
type="number"
|
||||
value={tenantId}
|
||||
onChange={handleChange}
|
||||
endIcon={(
|
||||
<Tooltip title={"Define tenant id if you need request to another storage"}>
|
||||
<Button
|
||||
variant={"text"}
|
||||
size={"small"}
|
||||
startIcon={<InfoIcon/>}
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
/>;
|
||||
};
|
||||
|
||||
export default TenantsConfiguration;
|
|
@ -15,6 +15,7 @@ import classNames from "classnames";
|
|||
import StepConfigurator from "../../Configurators/StepConfigurator/StepConfigurator";
|
||||
import { useAppState } from "../../../state/common/StateContext";
|
||||
import HeaderNav from "./HeaderNav/HeaderNav";
|
||||
import TenantsConfiguration from "../../Configurators/GlobalSettings/TenantsConfiguration/TenantsConfiguration";
|
||||
|
||||
const Header: FC = () => {
|
||||
const { darkTheme } = useAppState();
|
||||
|
@ -37,7 +38,6 @@ const Header: FC = () => {
|
|||
const navigate = useNavigate();
|
||||
const { search, pathname } = useLocation();
|
||||
|
||||
|
||||
const headerSetup = useMemo(() => {
|
||||
return ((routerOptions[pathname] || {}) as RouterOptions).header || {};
|
||||
}, [pathname]);
|
||||
|
@ -70,6 +70,7 @@ const Header: FC = () => {
|
|||
background={background}
|
||||
/>
|
||||
<div className="vm-header__settings">
|
||||
{headerSetup?.tenant && <TenantsConfiguration/>}
|
||||
{headerSetup?.stepControl && <StepConfigurator/>}
|
||||
{headerSetup?.timeSelector && <TimeSelector/>}
|
||||
{headerSetup?.cardinalityDatePicker && <CardinalityDatePicker/>}
|
||||
|
|
|
@ -389,3 +389,14 @@ export const QuestionIcon = () => (
|
|||
</svg>
|
||||
|
||||
);
|
||||
|
||||
export const StorageIcons = () => (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
>
|
||||
<path
|
||||
d="M4 20h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2zm0-3h2v2H4v-2zM2 6c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2zm4 1H4V5h2v2zm-2 7h16c1.1 0 2-.9 2-2s-.9-2-2-2H4c-1.1 0-2 .9-2 2s.9 2 2 2zm0-3h2v2H4v-2z"
|
||||
></path>
|
||||
</svg>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,7 @@ $padding-modal: 22px;
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
|
|
@ -11,6 +11,7 @@ const router = {
|
|||
export interface RouterOptions {
|
||||
title?: string,
|
||||
header: {
|
||||
tenant?: boolean,
|
||||
stepControl?: boolean,
|
||||
timeSelector?: boolean,
|
||||
executionControls?: boolean,
|
||||
|
@ -21,6 +22,7 @@ export interface RouterOptions {
|
|||
|
||||
const routerOptionsDefault = {
|
||||
header: {
|
||||
tenant: true,
|
||||
stepControl: true,
|
||||
timeSelector: true,
|
||||
executionControls: true,
|
||||
|
@ -35,6 +37,7 @@ export const routerOptions: {[key: string]: RouterOptions} = {
|
|||
[router.metrics]: {
|
||||
title: "Explore metrics",
|
||||
header: {
|
||||
tenant: true,
|
||||
stepControl: true,
|
||||
timeSelector: true,
|
||||
}
|
||||
|
@ -42,12 +45,15 @@ export const routerOptions: {[key: string]: RouterOptions} = {
|
|||
[router.cardinality]: {
|
||||
title: "Explore cardinality",
|
||||
header: {
|
||||
tenant: true,
|
||||
cardinalityDatePicker: true,
|
||||
}
|
||||
},
|
||||
[router.topQueries]: {
|
||||
title: "Top queries",
|
||||
header: {}
|
||||
header: {
|
||||
tenant: true,
|
||||
}
|
||||
},
|
||||
[router.trace]: {
|
||||
title: "Trace analyzer",
|
||||
|
|
|
@ -4,18 +4,20 @@ import { getFromStorage, saveToStorage } from "../../utils/storage";
|
|||
|
||||
export interface AppState {
|
||||
serverUrl: string;
|
||||
tenantId: number;
|
||||
tenantId: string;
|
||||
darkTheme: boolean
|
||||
}
|
||||
|
||||
export type Action =
|
||||
| { type: "SET_SERVER", payload: string }
|
||||
| { type: "SET_TENANT_ID", payload: number }
|
||||
| { type: "SET_DARK_THEME", payload: boolean }
|
||||
| { type: "SET_TENANT_ID", payload: string }
|
||||
|
||||
const tenantId = getQueryStringValue("g0.tenantID", "") as string;
|
||||
|
||||
export const initialState: AppState = {
|
||||
serverUrl: getDefaultServer(),
|
||||
tenantId: Number(getQueryStringValue("g0.tenantID", 0)),
|
||||
serverUrl: getDefaultServer(tenantId),
|
||||
tenantId,
|
||||
darkTheme: !!getFromStorage("DARK_THEME")
|
||||
};
|
||||
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import { getAppModeParams } from "./app-mode";
|
||||
|
||||
export const getDefaultServer = (): string => {
|
||||
export const getDefaultServer = (tenantId?: string): string => {
|
||||
const { serverURL } = getAppModeParams();
|
||||
return serverURL || window.location.href.replace(/\/(?:prometheus\/)?(?:graph|vmui)\/.*/, "/prometheus");
|
||||
const url = serverURL || window.location.href.replace(/\/(?:prometheus\/)?(?:graph|vmui)\/.*/, "/prometheus");
|
||||
if (tenantId) return replaceTenantId(url, tenantId);
|
||||
return url;
|
||||
};
|
||||
|
||||
export const replaceTenantId = (serverUrl: string, tenantId: string) => {
|
||||
const regexp = /(\/select\/)(\d+|\d.+)(\/)(.+)/;
|
||||
return serverUrl.replace(regexp, `$1${tenantId}/$4`);
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
|||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add dark mode - it can be seleted via `settings` menu in the top right corner. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3704).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): improve visual appearance of the top menu. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3678).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): embed fonts into binary instead of loading them from external sources. This allows using `vmui` in full from isolated networks without access to Internet. Thanks to @ScottKevill for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3696).
|
||||
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add ability to switch between tenants by selecting the needed tenant in the drop-down list at the top right corner of the UI. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3673).
|
||||
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html): reduce memory usage when sending stale markers for targets, which expose big number of metrics. See [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3668) and [this](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3675) issues.
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth.html): allow limiting the number of concurrent requests sent to `vmauth` via `-maxConcurrentRequests` command-line flag. This allows controlling memory usage of `vmauth` and the resource usage of backends behind `vmauth`. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3346). Thanks to @dmitryk-dk for [the initial implementation](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/3486).
|
||||
* FEATURE: allow using VictoriaMetrics components behind proxies, which communicate with the backend via [proxy protocol](https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3335). For example, [vmauth](https://docs.victoriametrics.com/vmauth.html) accepts proxy protocol connections when it starts with `-httpListenAddr.useProxyProtocol` command-line flag.
|
||||
|
|
Loading…
Reference in a new issue