Compare commits

...

14 Commits

20 changed files with 576 additions and 21 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "reverse-proxy-frontend", "name": "reverse-proxy-frontend",
"version": "1.4.8", "version": "1.4.10",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@ -1,6 +1,6 @@
{ {
"name": "reverse-proxy-frontend", "name": "reverse-proxy-frontend",
"version": "1.4.8", "version": "1.4.15",
"private": true, "private": true,
"description": "Reverse proxy frontend application", "description": "Reverse proxy frontend application",
"author": { "author": {

View File

@ -57,6 +57,10 @@
"Label": "Trailing slash", "Label": "Trailing slash",
"Tooltip": "A trailing slash is the forward slash placed at the end of a URL. The trailing slash is generally used to mark a directory, and if a URL is not terminated using a trailing slash, this generally points to a file." "Tooltip": "A trailing slash is the forward slash placed at the end of a URL. The trailing slash is generally used to mark a directory, and if a URL is not terminated using a trailing slash, this generally points to a file."
}, },
"UpstreamScheme": {
"Label": "Upstream scheme",
"Tooltip": "UpstreamScheme option will force the reverse proxy to set on the HttpRequest the scheme found in the headers 'X-Forwarded-Scheme' or 'X-Forwarded-Proto'."
},
"PathOverwrite": { "PathOverwrite": {
"Label": "Path overwrite", "Label": "Path overwrite",
"Tooltip": "Option by which the base path of the application is automatically overwritten in all http text responses received from it." "Tooltip": "Option by which the base path of the application is automatically overwritten in all http text responses received from it."
@ -94,6 +98,35 @@
"Definition": "Change points are combinations of html tags and attributes that are used by system to identify elements from http content and replace the key in theirs content with respect for exceptions.", "Definition": "Change points are combinations of html tags and attributes that are used by system to identify elements from http content and replace the key in theirs content with respect for exceptions.",
"HtmlTag": "Html tag", "HtmlTag": "Html tag",
"Attribute": "Attribute" "Attribute": "Attribute"
},
"SslPolicy": {
"Label": "SSL policy",
"Tooltip": "SSLPolicy is used to set the SSL options for the forward. It can be used to bypass self signed or invalid SSL certificates for the upstream server.",
"Bypass": {
"InvalidCertificates": "Bypass invalid certificates",
"InvalidCertificatesDescription": "With this option is enabled, the reverse proxy will not validate the SSL certificate of the upstream server. This is useful for self-signed certificates or when the upstream server has an invalid certificate.",
"CertificateNameMismatch": "Bypass certificate name mismatch",
"CertificateNameMismatchDescription": "With this option is enabled, the reverse proxy will not validate the SSL certificate name against the upstream server's hostname. This is useful when the upstream server's certificate does not match its hostname.",
"CertificateChainErrors": "Bypass certificate chain errors",
"CertificateChainErrorsDescription": "With this option is enabled, the reverse proxy will not validate the SSL certificate chain of the upstream server. This is useful when the upstream server's certificate chain is incomplete or invalid."
}
},
"IpFiltering": {
"Label": "IP filtering",
"Tooltip": "IPFiltering is used to filter the IP addresses that can access the forward. It can be used to allow or deny access to specific IP addresses or ranges of IP addresses.",
"Mode": "Mode",
"RulesCount": "Rules count",
"Profiles": "Profiles",
"Rules": {
"Title": "Rules",
"IpAddress": "IP address",
"Description": "Description",
"Profile": "Profile"
}
},
"ExcludeFromAnalytics": {
"Label": "Exclude from analytics",
"Tooltip": "Exclude this forward from analytics. This will prevent the reverse proxy from collecting any data about this forward."
} }
} }
}, },
@ -109,6 +142,8 @@
"Wizard": "wizard", "Wizard": "wizard",
"HostName": "Server host", "HostName": "Server host",
"SessionsCount": "Sessions count", "SessionsCount": "Sessions count",
"IsChainMember": "Is chain member",
"IsChainMemberTooltip": "If the proxy is a chain member, it runs behind another proxy; if it isn't, its the first or only proxy receiving the request.",
"Domain": "Domain", "Domain": "Domain",
"ActiveSession": "Active session", "ActiveSession": "Active session",
"ActiveSessionSubtitle": "Expand to see forwards", "ActiveSessionSubtitle": "Expand to see forwards",
@ -177,7 +212,7 @@
"Api": "API: {{version}}", "Api": "API: {{version}}",
"Frontend": "UI: {{version}}" "Frontend": "UI: {{version}}"
}, },
"LastUpdateDate": "Last update date: {{date}}", "LastUpdateDate": "Last release date: {{date}}",
"Components": { "Components": {
"Server": "Server:", "Server": "Server:",
"Api": "API:", "Api": "API:",

View File

@ -48,6 +48,10 @@
"Label": "Trailing slash", "Label": "Trailing slash",
"Tooltip": "Bara oblică plasată la sfarșitul unei adrese URL. Slash-ul final este de obicei utilizat pentru a marca un director și dacă o adresă URL nu este încheiată folosind o bară oblică, aceasta indică în general un fișier." "Tooltip": "Bara oblică plasată la sfarșitul unei adrese URL. Slash-ul final este de obicei utilizat pentru a marca un director și dacă o adresă URL nu este încheiată folosind o bară oblică, aceasta indică în general un fișier."
}, },
"UpstreamScheme": {
"Label": "Upstream scheme",
"Tooltip": "Opțiunea UpstreamScheme va forța proxy-ul invers să seteze pe HttpRequest schema găsită în anteturile 'X-Forwarded-Scheme' sau 'X-Forwarded-Proto'."
},
"PathOverwrite": { "PathOverwrite": {
"Label": "Path overwrite", "Label": "Path overwrite",
"Tooltip": "Opțiune prin care calea de bază a aplicației este suprascrisă automat în toate răspunsurile de text http primite de la aceasta." "Tooltip": "Opțiune prin care calea de bază a aplicației este suprascrisă automat în toate răspunsurile de text http primite de la aceasta."
@ -85,6 +89,35 @@
"Definition": "Punctele de schimbare sunt combinații de etichete și atribute html care sunt utilizate de sistem pentru a identifica elemente din conținutul http și pentru a înlocui cheia în conținutul lor, dar cu respectarea excepțiilor.", "Definition": "Punctele de schimbare sunt combinații de etichete și atribute html care sunt utilizate de sistem pentru a identifica elemente din conținutul http și pentru a înlocui cheia în conținutul lor, dar cu respectarea excepțiilor.",
"HtmlTag": "Etichetă html", "HtmlTag": "Etichetă html",
"Attribute": "Atribut" "Attribute": "Atribut"
},
"SslPolicy": {
"Label": "Politica SSL",
"Tooltip": "Politica SSL este utilizată pentru a seta opțiunile SSL pentru redirecționare. Poate fi utilizată pentru a ocoli certificatele SSL autosemnate sau nevalide pentru serverul către care redirecționează traficul.",
"Bypass": {
"InvalidCertificates": "Ocolește certificatele nevalide",
"InvalidCertificatesDescription": "Cu această opțiune este activată, proxy-ul nu va valida certificatul SSL al serverului din amonte. Acest lucru este util pentru certificatele autosemnate sau atunci când serverul din amonte are un certificat nevalid.",
"CertificateNameMismatch": "Ocolește nepotrivirea numelui certificatului",
"CertificateNameMismatchDescription": "Cu această opțiune este activată, proxy-ul nu va valida numele certificatului SSL în raport cu numele de gazdă al serverului din amonte. Acest lucru este util atunci când certificatul serverului din amonte nu corespunde cu numele său de gazdă.",
"CertificateChainErrors": "Ocolește erorile lanțului de certificate",
"CertificateChainErrorsDescription": "Cu această opțiune este activată, proxy-ul nu va valida lanțul de certificate SSL al serverului din amonte. Acest lucru este util atunci când lanțul de certificate al serverului din amonte este incomplet sau invalid."
}
},
"IpFiltering": {
"Label": "Filtrare IP",
"Tooltip": "Filtrarea IP este utilizată pentru a filtra adresele IP care pot accesa redirecționarea. Poate fi utilizată pentru a permite sau a refuza accesul la anumite adrese IP sau intervale de adrese IP.",
"Mode": "Mod",
"RulesCount": "Număr reguli",
"Profiles": "Profiluri",
"Rules": {
"Title": "Reguli",
"IpAddress": "Adresă IP",
"Description": "Descriere",
"Profile": "Profil"
}
},
"ExcludeFromAnalytics": {
"Label": "Exclude din analitice",
"Tooltip": "Exclude această redirecționare din analitice. Aceasta va împiedica reverse proxy-ul să colecteze date despre această redirecționare."
} }
} }
}, },
@ -100,6 +133,8 @@
"Wizard": "vrăjitor", "Wizard": "vrăjitor",
"HostName": "Gazdă server", "HostName": "Gazdă server",
"SessionsCount": "Număr sesiuni", "SessionsCount": "Număr sesiuni",
"IsChainMember": "Este membru al unui lanț",
"IsChainMemberTooltip": "Dacă proxy-ul este membru al lanțului, acesta rulează în spatele unui alt proxy; dacă nu este, este primul sau singurul proxy care primește cererea.",
"Domain": "Domeniu", "Domain": "Domeniu",
"ActiveSession": "Sesiune activă", "ActiveSession": "Sesiune activă",
"ActiveSessionSubtitle": "Extindeţi pentru a vedea redirectările", "ActiveSessionSubtitle": "Extindeţi pentru a vedea redirectările",

View File

@ -3,7 +3,7 @@ import PropTypes from "prop-types";
import { CheckCircleOutlineRounded, RemoveRounded } from "@material-ui/icons"; import { CheckCircleOutlineRounded, RemoveRounded } from "@material-ui/icons";
import { LinearProgress, Grid } from "@material-ui/core"; import { LinearProgress, Grid } from "@material-ui/core";
const ActiveIcon = ({ active, loading }) => { const ActiveIcon = ({ active, loading, color }) => {
if (loading && loading === true) { if (loading && loading === true) {
return ( return (
<Grid container> <Grid container>
@ -16,8 +16,9 @@ const ActiveIcon = ({ active, loading }) => {
); );
} }
const circleColor = color || "primary";
return active && active === true ? ( return active && active === true ? (
<CheckCircleOutlineRounded color="primary" fontSize="small" /> <CheckCircleOutlineRounded color={circleColor} fontSize="small" />
) : ( ) : (
<RemoveRounded fontSize="small" /> <RemoveRounded fontSize="small" />
); );
@ -25,7 +26,8 @@ const ActiveIcon = ({ active, loading }) => {
ActiveIcon.propTypes = { ActiveIcon.propTypes = {
active: PropTypes.bool, active: PropTypes.bool,
loading: PropTypes.bool loading: PropTypes.bool,
color: PropTypes.string
}; };
export default ActiveIcon; export default ActiveIcon;

View File

@ -11,11 +11,11 @@ const SlimChips = ({ elements }) => {
return ( return (
<> <>
{elements.map(protocol => ( {elements.map(item => (
<Chip <Chip
key={elements.indexOf(protocol)} key={elements.indexOf(item)}
size="small" size="small"
label={protocol} label={item}
className={classes.smallChip} className={classes.smallChip}
/> />
))} ))}

View File

@ -5,6 +5,10 @@ import PathOverwriteCard from "./pathOverwrite/PathOverwriteCard";
import PathInjectionCard from "./pathInjection/PathInjectionCard"; import PathInjectionCard from "./pathInjection/PathInjectionCard";
import KeyOverwriteCard from "./keyOverwrite/KeyOverwriteCard"; import KeyOverwriteCard from "./keyOverwrite/KeyOverwriteCard";
import SpecificHeadersCard from "./specificHeaders/SpecificHeadersCard"; import SpecificHeadersCard from "./specificHeaders/SpecificHeadersCard";
import UpstreamSchemeCard from "./upstreamScheme/UpstreamSchemeCard";
import SslPolicyCard from "./sslPolicy/SslPolicyCard";
import IpFilteringCard from "./ipFiltering/IpFilteringCard";
import ExcludeAnalyticsCard from "./analytics/ExcludeAnalyticsCard";
const ForwardOptionsAdvancedComponent = ({ options }) => { const ForwardOptionsAdvancedComponent = ({ options }) => {
return ( return (
@ -39,6 +43,30 @@ const ForwardOptionsAdvancedComponent = ({ options }) => {
<br /> <br />
</> </>
)} )}
{options.upstreamScheme && (
<>
<UpstreamSchemeCard enabled={options.upstreamScheme} />
<br />
</>
)}
{options.sslPolicy && (
<>
<SslPolicyCard data={options.sslPolicy} />
<br />
</>
)}
{options.ipFiltering && (
<>
<IpFilteringCard data={options.ipFiltering} />
<br />
</>
)}
{options.excludeFromAnalytics && (
<>
<ExcludeAnalyticsCard enabled={options.excludeFromAnalytics} />
<br />
</>
)}
</> </>
); );
}; };

View File

@ -0,0 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import ExpandableCard from "../../../../../../components/common/ExpandableCard";
import { useTranslation } from "react-i18next";
import ExcludeAnalyticsSummary from "./ExcludeAnalyticsSummary";
import CancelScheduleSendIcon from "@material-ui/icons/CancelScheduleSend";
const ExcludeAnalyticsCard = ({ enabled }) => {
const { t } = useTranslation();
return (
<ExpandableCard
Icon={<CancelScheduleSendIcon />}
title={t("Forward.Options.ExcludeFromAnalytics.Label")}
subtitle={t("Forward.Options.ExcludeFromAnalytics.Tooltip")}
Summary={<ExcludeAnalyticsSummary enabled={enabled} />}
expandable={false}
Content={<div>...</div>}
/>
);
};
ExcludeAnalyticsCard.propTypes = {
enabled: PropTypes.bool.isRequired
};
export default ExcludeAnalyticsCard;

View File

@ -0,0 +1,24 @@
import React from "react";
import PropTypes from "prop-types";
import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import ActiveIcon from "../../../../../../components/common/ActiveIcon";
const TrailingSlashSummary = ({ enabled }) => {
const { t } = useTranslation();
return (
<Grid container>
<Grid item xs={6} sm={3} md={3}>
{`${t("General.Enabled")}: `}
<ActiveIcon active={enabled} />
</Grid>
</Grid>
);
};
TrailingSlashSummary.propTypes = {
enabled: PropTypes.bool.isRequired
};
export default TrailingSlashSummary;

View File

@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import ExpandableCard from "../../../../../../components/common/ExpandableCard";
import { useTranslation } from "react-i18next";
import FilterTiltShiftIcon from "@material-ui/icons/FilterTiltShift";
import IpFilteringSummary from "./IpFilteringSummary";
const IpFilteringCard = ({ data }) => {
const { t } = useTranslation();
return (
<ExpandableCard
Icon={<FilterTiltShiftIcon />}
title={t("Forward.Options.IpFiltering.Label")}
subtitle={t("Forward.Options.IpFiltering.Tooltip")}
expandable={false}
Summary={<IpFilteringSummary data={data} />}
/>
);
};
IpFilteringCard.propTypes = {
data: PropTypes.object.isRequired
};
export default IpFilteringCard;

View File

@ -0,0 +1,105 @@
import React from "react";
import PropTypes from "prop-types";
import ExpandableCard from "../../../../../../components/common/ExpandableCard";
import { useTranslation } from "react-i18next";
import { List } from "@material-ui/icons";
import { makeStyles } from "@material-ui/core/styles";
import {
Table,
TableBody,
TableContainer,
TableHead,
TableRow,
Paper,
Chip
} from "@material-ui/core";
import styles from "../../../../../../components/common/styles/tableStyles";
import {
StyledTableCell,
StyledTableRow
} from "../../../../../../components/common/MaterialTable";
import InputIcon from "@material-ui/icons/Input";
import BlockIcon from "@material-ui/icons/Block";
import BrokenImageIcon from "@material-ui/icons/BrokenImage";
const useStyles = makeStyles(styles);
const IpFilteringRules = ({ mode, rules }) => {
const isAllowedMode = mode === "Allow";
const classes = useStyles();
const { t } = useTranslation();
return (
<ExpandableCard
Icon={<List />}
iconVariant="rounded"
title={t("Forward.Options.IpFiltering.Rules.Title")}
smallHeader
Content={
<TableContainer component={Paper}>
<Table
className={classes.narrowTable}
size="small"
aria-label="customized table"
>
<TableHead>
<TableRow>
<StyledTableCell>
{t("Forward.Options.IpFiltering.Rules.IpAddress")}
</StyledTableCell>
<StyledTableCell>
{t("Forward.Options.IpFiltering.Rules.Description")}
</StyledTableCell>
<StyledTableCell>
{t("Forward.Options.IpFiltering.Rules.Profile")}
</StyledTableCell>
</TableRow>
</TableHead>
<TableBody>
<>
{rules.map(rule => {
return (
<StyledTableRow key={rules.indexOf(rule)}>
<StyledTableCell style={{ width: "20%" }}>
{isAllowedMode ? (
<InputIcon
fontSize="small"
style={{ marginRight: 8, color: "#34964fff" }}
/>
) : (
<BlockIcon
fontSize="small"
style={{ marginRight: 8, color: "#e74c3c" }}
/>
)}
{rule.ipAddress}
</StyledTableCell>
<StyledTableCell>{rule.description}</StyledTableCell>
<StyledTableCell style={{ width: "15%" }}>
{rule.profileReference && (
<Chip
icon={<BrokenImageIcon />}
size="small"
label={rule.profileReference}
style={{ maxHeight: 20 }}
/>
)}
</StyledTableCell>
</StyledTableRow>
);
})}
</>
</TableBody>
</Table>
</TableContainer>
}
/>
);
};
IpFilteringRules.propTypes = {
mode: PropTypes.string.isRequired,
rules: PropTypes.array.isRequired
};
export default IpFilteringRules;

View File

@ -0,0 +1,56 @@
import React, { useMemo } from "react";
import PropTypes from "prop-types";
import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import ActiveIcon from "../../../../../../components/common/ActiveIcon";
import { makeStyles } from "@material-ui/core/styles";
import styles from "../../../../../../components/common/styles/gridStyles";
import IpFilteringRules from "./IpFilteringRules";
import SlimChips from "../../../../../../components/common/chips/SlimChips";
const useStyles = makeStyles(styles);
const IpFilteringSummary = ({ data }) => {
const { t } = useTranslation();
const classes = useStyles();
const profileCodes = useMemo(() => {
if (!data.rules) return null;
const codes = data.rules
.map(r => r.profileReference)
.filter(code => !!code);
return Array.from(new Set(codes));
}, [data.rules]);
return (
<>
<Grid container>
<Grid item xs={6} sm={3} md={3}>
{`${t("General.Enabled")}: `}
<ActiveIcon active={data.on} />
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.IpFiltering.Mode")}: `}
<span className={classes.value}>{data.mode}</span>
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.IpFiltering.RulesCount")}: `}
<span className={classes.value}>{data.rules?.length || 0}</span>
</Grid>
{profileCodes && (
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.IpFiltering.Profiles")}: `}
<SlimChips elements={profileCodes} />
</Grid>
)}
</Grid>
<br />
<IpFilteringRules mode={data.mode} rules={data.rules} />
</>
);
};
IpFilteringSummary.propTypes = {
data: PropTypes.object.isRequired
};
export default IpFilteringSummary;

View File

@ -65,7 +65,11 @@ const KeyOverwriteDetailsComponent = ({ details }) => {
{displayConditions && ( {displayConditions && (
<StyledTableCell> <StyledTableCell>
{detail.conditions && ( {detail.conditions && (
<SlimChips elements={detail.conditions} /> <SlimChips
elements={detail.conditions.map(
c => `${c.target}: ${c.expression}`
)}
/>
)} )}
</StyledTableCell> </StyledTableCell>
)} )}

View File

@ -0,0 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import ExpandableCard from "../../../../../../components/common/ExpandableCard";
import { useTranslation } from "react-i18next";
import HttpsIcon from "@material-ui/icons/Https";
import SslPolicySummary from "./SslPolicySummary";
import SslPolicyComponent from "./SslPolicyComponent";
const SslPolicyCard = ({ data }) => {
const { t } = useTranslation();
return (
<ExpandableCard
Icon={<HttpsIcon />}
title={t("Forward.Options.SslPolicy.Label")}
subtitle={t("Forward.Options.SslPolicy.Tooltip")}
Summary={<SslPolicySummary data={data} />}
Content={<SslPolicyComponent data={data.bypass} />}
/>
);
};
SslPolicyCard.propTypes = {
data: PropTypes.object.isRequired
};
export default SslPolicyCard;

View File

@ -0,0 +1,73 @@
import React from "react";
import PropTypes from "prop-types";
import { Grid, useTheme } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import ActiveIcon from "../../../../../../components/common/ActiveIcon";
const SslPolicyComponent = ({ data }) => {
const { t } = useTranslation();
const theme = useTheme();
const fields = [
{
key: "invalidCertificates",
label: t("Forward.Options.SslPolicy.Bypass.InvalidCertificates"),
description: t(
"Forward.Options.SslPolicy.Bypass.InvalidCertificatesDescription"
)
},
{
key: "certificateNameMismatch",
label: t("Forward.Options.SslPolicy.Bypass.CertificateNameMismatch"),
description: t(
"Forward.Options.SslPolicy.Bypass.CertificateNameMismatchDescription"
)
},
{
key: "certificateChainErrors",
label: t("Forward.Options.SslPolicy.Bypass.CertificateChainErrors"),
description: t(
"Forward.Options.SslPolicy.Bypass.CertificateChainErrorsDescription"
)
}
];
return (
<Grid container spacing={2}>
{fields.map(({ key, label, description }) => (
<Grid item xs={12} key={key}>
<Grid container spacing={1} alignItems="center">
<Grid item xs={3}>
<>
<ActiveIcon active={!!data[key]} />
<span
style={{
fontWeight: theme.typography.fontWeightMedium,
marginLeft: 8
}}
>
{label}:
</span>
</>
</Grid>
<Grid item xs={9}>
<span style={{ color: "#666", fontSize: "0.95em" }}>
{description}
</span>
</Grid>
</Grid>
</Grid>
))}
</Grid>
);
};
SslPolicyComponent.propTypes = {
data: PropTypes.shape({
acinvalidCertificates: PropTypes.bool,
certificateNameMismatch: PropTypes.bool,
certificateChainErrors: PropTypes.bool
}).isRequired
};
export default SslPolicyComponent;

View File

@ -0,0 +1,37 @@
import React from "react";
import PropTypes from "prop-types";
import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import ActiveIcon from "../../../../../../components/common/ActiveIcon";
const SslPolicySummary = ({ data }) => {
const { t } = useTranslation();
return (
<>
<Grid container>
<Grid item xs={6} sm={3} md={3}>
{`${t("General.Enabled")}: `}
<ActiveIcon active={data.on} />
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.SslPolicy.Bypass.InvalidCertificates")}: `}
<ActiveIcon active={!!data.bypass.invalidCertificates} />
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.SslPolicy.Bypass.CertificateNameMismatch")}: `}
<ActiveIcon active={!!data.bypass.certificateNameMismatch} />
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Forward.Options.SslPolicy.Bypass.CertificateChainErrors")}: `}
<ActiveIcon active={!!data.bypass.certificateChainErrors} />
</Grid>
</Grid>
</>
);
};
SslPolicySummary.propTypes = {
data: PropTypes.object.isRequired
};
export default SslPolicySummary;

View File

@ -0,0 +1,26 @@
import React from "react";
import PropTypes from "prop-types";
import ExpandableCard from "../../../../../../components/common/ExpandableCard";
import { useTranslation } from "react-i18next";
import UpstreamSchemeSummary from "./UpstreamSchemeSummary";
import FlipToFrontIcon from "@material-ui/icons/FlipToFront";
const UpstreamSchemeCard = ({ enabled }) => {
const { t } = useTranslation();
return (
<ExpandableCard
Icon={<FlipToFrontIcon />}
title={t("Forward.Options.UpstreamScheme.Label")}
subtitle={t("Forward.Options.UpstreamScheme.Tooltip")}
Summary={<UpstreamSchemeSummary enabled={enabled} />}
Content={<div>...</div>}
/>
);
};
UpstreamSchemeCard.propTypes = {
enabled: PropTypes.bool.isRequired
};
export default UpstreamSchemeCard;

View File

@ -0,0 +1,24 @@
import React from "react";
import PropTypes from "prop-types";
import { Grid } from "@material-ui/core";
import { useTranslation } from "react-i18next";
import ActiveIcon from "../../../../../../components/common/ActiveIcon";
const UpstreamSchemeSummary = ({ enabled }) => {
const { t } = useTranslation();
return (
<Grid container>
<Grid item xs={6} sm={3} md={3}>
{`${t("General.Enabled")}: `}
<ActiveIcon active={enabled} />
</Grid>
</Grid>
);
};
UpstreamSchemeSummary.propTypes = {
enabled: PropTypes.bool.isRequired
};
export default UpstreamSchemeSummary;

View File

@ -53,6 +53,26 @@ const ForwardOptionsComponent = ({ title, options }) => {
label: "Forward.Options.SpecificHeaders.Label", label: "Forward.Options.SpecificHeaders.Label",
tooltip: "Forward.Options.SpecificHeaders.Tooltip", tooltip: "Forward.Options.SpecificHeaders.Tooltip",
active: options.specificHeaders?.on active: options.specificHeaders?.on
},
{
label: "Forward.Options.UpstreamScheme.Label",
tooltip: "Forward.Options.UpstreamScheme.Tooltip",
active: options.upstreamScheme
},
{
label: "Forward.Options.SslPolicy.Label",
tooltip: "Forward.Options.SslPolicy.Tooltip",
active: !!options.sslPolicy
},
{
label: "Forward.Options.IpFiltering.Label",
tooltip: "Forward.Options.IpFiltering.Tooltip",
active: !!options.ipFiltering
},
{
label: "Forward.Options.ExcludeFromAnalytics.Label",
tooltip: "Forward.Options.ExcludeFromAnalytics.Tooltip",
active: !!options.excludeFromAnalytics
} }
], ],
[options] [options]

View File

@ -1,9 +1,10 @@
import React from "react"; import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { Grid, Typography, Link } from "@material-ui/core"; import { Grid, Typography, Link, Tooltip } from "@material-ui/core";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import styles from "../../../components/common/styles/gridStyles"; import styles from "../../../components/common/styles/gridStyles";
import ActiveIcon from "../../../components/common/ActiveIcon";
const useStyles = makeStyles(styles); const useStyles = makeStyles(styles);
@ -18,22 +19,27 @@ const ServerSummary = ({
return ( return (
<Grid container> <Grid container>
<Grid item xs={6} sm={3} md={3}> <Grid item xs={6} sm={3} md={2}>
{`${t("Server.Domain")}: `}
<span className={classes.value}>{data.domain.name}</span>
</Grid>
<Grid item xs={6} sm={3} md={3}>
{`${t("Server.HostName")}: `} {`${t("Server.HostName")}: `}
<span className={classes.value}>{serverHost || ""}</span> <span className={classes.value}>{serverHost || ""}</span>
</Grid> </Grid>
<Grid item xs={6} sm={3} md={2}>
<Grid item xs={6} sm={3} md={3}>
{`${t("Server.SessionsCount")}: `} {`${t("Server.SessionsCount")}: `}
<span className={classes.value}>{data.sessionsCount}</span> <span className={classes.value}>{data.sessionsCount}</span>
</Grid> </Grid>
<Grid item xs={6} sm={3} md={2}>
<Grid item xs={6} sm={3} md={3}> <Tooltip title={t("Server.IsChainMemberTooltip")}>
<span>
{`${t("Server.IsChainMember")}: `}
<ActiveIcon active={data.isChainMember} color="action" />
</span>
</Tooltip>
</Grid>
<Grid item xs={6} sm={3} md={2}>
{`${t("Server.Domain")}: `}
<span className={classes.value}>{data.domain.name}</span>
</Grid>
<Grid item xs={6} sm={3} md={2}>
{`${t("Server.DDNSProvider")}: `} {`${t("Server.DDNSProvider")}: `}
<Link <Link
href="#" href="#"