grafana/packages/grafana-ui/src/components/DataSourceSettings/DataSourceHttpSettings.tsx

217 lines
7.2 KiB
TypeScript

import React, { useState, useCallback } from 'react';
import { SelectableValue } from '@grafana/data';
import { css, cx } from 'emotion';
import { useTheme } from '../../themes';
import { BasicAuthSettings } from './BasicAuthSettings';
import { HttpProxySettings } from './HttpProxySettings';
import { TLSAuthSettings } from './TLSAuthSettings';
import { DataSourceSettings } from '@grafana/data';
import { HttpSettingsProps } from './types';
import { CustomHeadersSettings } from './CustomHeadersSettings';
import { Select } from '../Select/Select';
import { Input } from '../Input/Input';
import { FormField } from '../FormField/FormField';
import { FormLabel } from '../FormLabel/FormLabel';
import { Switch } from '../Switch/Switch';
import { TagsInput } from '../TagsInput/TagsInput';
const ACCESS_OPTIONS: Array<SelectableValue<string>> = [
{
label: 'Server (default)',
value: 'proxy',
},
{
label: 'Browser',
value: 'direct',
},
];
const DEFAULT_ACCESS_OPTION = {
label: 'Server (default)',
value: 'proxy',
};
const HttpAccessHelp = () => (
<div className="grafana-info-box m-t-2">
<p>
Access mode controls how requests to the data source will be handled.
<strong>
<i>Server</i>
</strong>{' '}
should be the preferred way if nothing else stated.
</p>
<div className="alert-title">Server access mode (Default):</div>
<p>
All requests will be made from the browser to Grafana backend/server which in turn will forward the requests to
the data source and by that circumvent possible Cross-Origin Resource Sharing (CORS) requirements. The URL needs
to be accessible from the grafana backend/server if you select this access mode.
</p>
<div className="alert-title">Browser access mode:</div>
<p>
All requests will be made from the browser directly to the data source and may be subject to Cross-Origin Resource
Sharing (CORS) requirements. The URL needs to be accessible from the browser if you select this access mode.
</p>
</div>
);
export const DataSourceHttpSettings: React.FC<HttpSettingsProps> = props => {
const { defaultUrl, dataSourceConfig, onChange, showAccessOptions } = props;
let urlTooltip;
const [isAccessHelpVisible, setIsAccessHelpVisible] = useState(false);
const theme = useTheme();
const onSettingsChange = useCallback(
(change: Partial<DataSourceSettings<any, any>>) => {
onChange({
...dataSourceConfig,
...change,
});
},
[dataSourceConfig]
);
switch (dataSourceConfig.access) {
case 'direct':
urlTooltip = (
<>
Your access method is <em>Browser</em>, this means the URL needs to be accessible from the browser.
</>
);
break;
case 'proxy':
urlTooltip = (
<>
Your access method is <em>Server</em>, this means the URL needs to be accessible from the grafana
backend/server.
</>
);
break;
default:
urlTooltip = 'Specify a complete HTTP URL (for example http://your_server:8080)';
}
const accessSelect = (
<Select
width={20}
options={ACCESS_OPTIONS}
value={ACCESS_OPTIONS.filter(o => o.value === dataSourceConfig.access)[0] || DEFAULT_ACCESS_OPTION}
onChange={selectedValue => onSettingsChange({ access: selectedValue.value })}
/>
);
const isValidUrl = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/.test(
dataSourceConfig.url
);
const notValidStyle = css`
box-shadow: inset 0 0px 5px ${theme.colors.red};
`;
const inputStyle = cx({ [`width-20`]: true, [notValidStyle]: !isValidUrl });
const urlInput = (
<Input
className={inputStyle}
placeholder={defaultUrl}
value={dataSourceConfig.url}
onChange={event => onSettingsChange({ url: event.currentTarget.value })}
/>
);
return (
<div className="gf-form-group">
<>
<h3 className="page-heading">HTTP</h3>
<div className="gf-form-group">
<div className="gf-form">
<FormField label="URL" labelWidth={11} tooltip={urlTooltip} inputEl={urlInput} />
</div>
{showAccessOptions && (
<>
<div className="gf-form-inline">
<div className="gf-form">
<FormField label="Access" labelWidth={11} inputWidth={20} inputEl={accessSelect} />
</div>
<div className="gf-form">
<label
className="gf-form-label query-keyword pointer"
onClick={() => setIsAccessHelpVisible(isVisible => !isVisible)}
>
Help&nbsp;
<i className={`fa fa-caret-${isAccessHelpVisible ? 'down' : 'right'}`} />
</label>
</div>
</div>
{isAccessHelpVisible && <HttpAccessHelp />}
</>
)}
{dataSourceConfig.access === 'proxy' && (
<div className="gf-form">
<FormLabel
width={11}
tooltip="Grafana Proxy deletes forwarded cookies by default. Specify cookies by name that should be forwarded to the data source."
>
Whitelisted Cookies
</FormLabel>
<TagsInput
tags={dataSourceConfig.jsonData.keepCookies}
onChange={cookies =>
onSettingsChange({ jsonData: { ...dataSourceConfig.jsonData, keepCookies: cookies } })
}
width={20}
/>
</div>
)}
</div>
</>
<>
<h3 className="page-heading">Auth</h3>
<div className="gf-form-group">
<div className="gf-form-inline">
<Switch
label="Basic auth"
labelClass="width-13"
checked={dataSourceConfig.basicAuth}
onChange={event => {
onSettingsChange({ basicAuth: event!.currentTarget.checked });
}}
/>
<Switch
label="With Credentials"
labelClass="width-13"
checked={dataSourceConfig.withCredentials}
onChange={event => {
onSettingsChange({ withCredentials: event!.currentTarget.checked });
}}
tooltip="Whether credentials such as cookies or auth headers should be sent with cross-site requests."
/>
</div>
{dataSourceConfig.access === 'proxy' && (
<HttpProxySettings
dataSourceConfig={dataSourceConfig}
onChange={jsonData => onSettingsChange({ jsonData })}
/>
)}
</div>
{dataSourceConfig.basicAuth && (
<>
<h6>Basic Auth Details</h6>
<div className="gf-form-group">
<BasicAuthSettings {...props} />
</div>
</>
)}
{(dataSourceConfig.jsonData.tlsAuth || dataSourceConfig.jsonData.tlsAuthWithCACert) && (
<TLSAuthSettings dataSourceConfig={dataSourceConfig} onChange={onChange} />
)}
<CustomHeadersSettings dataSourceConfig={dataSourceConfig} onChange={onChange} />
</>
</div>
);
};