Merge pull request #2515 from scott-ray-wilson/region-select

Feature: Add Data Region Select
This commit is contained in:
Maidul Islam
2024-09-30 14:56:47 -04:00
committed by GitHub
4 changed files with 228 additions and 73 deletions

View File

@@ -0,0 +1,141 @@
import { useRouter } from "next/router";
import { faCheck } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { Modal, ModalContent, ModalTrigger, Select, SelectItem } from "@app/components/v2";
enum Region {
US = "us",
EU = "eu"
}
const regions = [
{
value: Region.US,
label: "United States",
location: "Virginia, USA",
flag: (
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-us" viewBox="0 0 640 480">
<path fill="#bd3d44" d="M0 0h640v480H0" />
<path
stroke="#fff"
strokeWidth="37"
d="M0 55.3h640M0 129h640M0 203h640M0 277h640M0 351h640M0 425h640"
/>
<path fill="#192f5d" d="M0 0h364.8v258.5H0" />
<marker id="us-a" markerHeight="30" markerWidth="30">
<path fill="#fff" d="m14 0 9 27L0 10h28L5 27z" />
</marker>
<path
fill="none"
markerMid="url(#us-a)"
d="m0 0 16 11h61 61 61 61 60L47 37h61 61 60 61L16 63h61 61 61 61 60L47 89h61 61 60 61L16 115h61 61 61 61 60L47 141h61 61 60 61L16 166h61 61 61 61 60L47 192h61 61 60 61L16 218h61 61 61 61 60z"
/>
</svg>
)
},
{
value: Region.EU,
label: "Europe",
location: "Frankfurt, Germany",
flag: (
<svg xmlns="http://www.w3.org/2000/svg" id="flag-icons-eu" viewBox="0 0 512 512">
<defs>
<g id="eu-d">
<g id="eu-b">
<path id="eu-a" d="m0-1-.3 1 .5.1z" />
<use xlinkHref="#eu-a" transform="scale(-1 1)" />
</g>
<g id="eu-c">
<use xlinkHref="#eu-b" transform="rotate(72)" />
<use xlinkHref="#eu-b" transform="rotate(144)" />
</g>
<use xlinkHref="#eu-c" transform="scale(-1 1)" />
</g>
</defs>
<path fill="#039" d="M0 0h512v512H0z" />
<g fill="#fc0" transform="translate(256 258.4)scale(25.28395)">
<use xlinkHref="#eu-d" width="100%" height="100%" y="-6" />
<use xlinkHref="#eu-d" width="100%" height="100%" y="6" />
<g id="eu-e">
<use xlinkHref="#eu-d" width="100%" height="100%" x="-6" />
<use xlinkHref="#eu-d" width="100%" height="100%" transform="rotate(-144 -2.3 -2.1)" />
<use xlinkHref="#eu-d" width="100%" height="100%" transform="rotate(144 -2.1 -2.3)" />
<use xlinkHref="#eu-d" width="100%" height="100%" transform="rotate(72 -4.7 -2)" />
<use xlinkHref="#eu-d" width="100%" height="100%" transform="rotate(72 -5 .5)" />
</g>
<use xlinkHref="#eu-e" width="100%" height="100%" transform="scale(-1 1)" />
</g>
</svg>
)
}
];
export const RegionSelect = () => {
const router = useRouter();
const handleRegionSelect = (value: Region) => {
router.push(`https://${value}.infisical.com/${router.pathname}`);
};
const [subdomain, domain] = window.location.host.split(".");
// only display region select for cloud
if (!domain?.match(/infisical/)) return null;
// default to US if not eu
const currentRegion = subdomain === Region.EU ? regions[1] : regions[0];
return (
<div className="mb-8 flex flex-col items-center">
<Select
className="w-44"
onValueChange={handleRegionSelect}
defaultValue={currentRegion.value}
>
{regions.map(({ value, label, flag }) => (
<SelectItem value={value} key={value}>
<div className="flex items-center gap-2">
<div className="w-4">{flag}</div>
{label}
</div>
</SelectItem>
))}
</Select>
<Modal>
<ModalTrigger>
<button type="button" className="mt-1 text-right text-xs text-mineshaft-400 underline">
Help me pick a data region
</button>
</ModalTrigger>
<ModalContent
title="Infisical Cloud data regions"
subTitle="Select the closest region to you and your team. Contact Infisical if you need to migrate regions."
>
{regions.map(({ value, label, location, flag }) => (
<div className="mb-6" key={value}>
<p className="font-medium">
<span className="mr-2 inline-block w-4">{flag}</span>
{value.toUpperCase()} Region
</p>
<ul className="ml-6 mt-2 flex flex-col gap-1">
<li>
<FontAwesomeIcon size="xs" className="mr-0.5 text-green" icon={faCheck} /> Fastest
option if you are based in {value === Region.US ? "the" : ""} {label}
</li>
<li>
<FontAwesomeIcon size="xs" className="mr-0.5 text-green" icon={faCheck} /> Data
storage compliance for this region
</li>
<li>
<FontAwesomeIcon size="xs" className="mr-0.5 text-green" icon={faCheck} /> Hosted
in {location}
</li>
</ul>
</div>
))}
</ModalContent>
</Modal>
</div>
);
};

View File

@@ -4,6 +4,7 @@ import { faGithub, faGitlab, faGoogle } from "@fortawesome/free-brands-svg-icons
import { faEnvelope } from "@fortawesome/free-regular-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { RegionSelect } from "@app/components/navigation/RegionSelect";
import { useServerConfig } from "@app/context";
import { LoginMethod } from "@app/hooks/api/admin/types";
@@ -25,6 +26,7 @@ export default function InitialSignupStep({
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
{t("signup.initial-title")}
</h1>
<RegionSelect />
{shouldDisplaySignupMethod(LoginMethod.GOOGLE) && (
<div className="w-1/4 min-w-[20rem] rounded-md lg:w-1/6">
<Button

View File

@@ -88,14 +88,16 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
return (
<ErrorBoundaryWrapper>
<QueryClientProvider client={queryClient}>
<NotificationContainer />
<ServerConfigProvider>
<UserProvider>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</UserProvider>
</ServerConfigProvider>
<TooltipProvider>
<NotificationContainer />
<ServerConfigProvider>
<UserProvider>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</UserProvider>
</ServerConfigProvider>
</TooltipProvider>
</QueryClientProvider>
</ErrorBoundaryWrapper>
);

View File

@@ -8,11 +8,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import HCaptcha from "@hcaptcha/react-hcaptcha";
import Error from "@app/components/basic/Error";
import { RegionSelect } from "@app/components/navigation/RegionSelect";
import { createNotification } from "@app/components/notifications";
import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
import attemptLogin from "@app/components/utilities/attemptLogin";
import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
import { Button, Input } from "@app/components/v2";
import { Button, IconButton, Input, Tooltip } from "@app/components/v2";
import { useServerConfig } from "@app/context";
import { useFetchServerStatus } from "@app/hooks/api";
import { LoginMethod } from "@app/hooks/api/admin/types";
@@ -166,70 +167,9 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
<h1 className="mb-8 bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
Login to Infisical
</h1>
{shouldDisplayLoginMethod(LoginMethod.GOOGLE) && (
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/google${callbackPort ? `?callback_port=${callbackPort}` : ""}`
);
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-2" />}
className="mx-0 h-10 w-full"
>
{t("login.continue-with-google")}
</Button>
</div>
)}
{shouldDisplayLoginMethod(LoginMethod.GITHUB) && (
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/github${callbackPort ? `?callback_port=${callbackPort}` : ""}`
);
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGithub} className="mr-2" />}
className="mx-0 h-10 w-full"
>
Continue with GitHub
</Button>
</div>
)}
{shouldDisplayLoginMethod(LoginMethod.GITLAB) && (
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/gitlab${callbackPort ? `?callback_port=${callbackPort}` : ""}`
);
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGitlab} className="mr-2" />}
className="mx-0 h-10 w-full"
>
Continue with GitLab
</Button>
</div>
)}
<RegionSelect />
{shouldDisplayLoginMethod(LoginMethod.SAML) && (
<div className="mt-2 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<div className="w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
colorSchema="primary"
variant="outline_bg"
@@ -273,6 +213,76 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
</Button>
</div>
)}
<div className="mt-2 flex w-1/4 min-w-[21.2rem] gap-2 md:min-w-[20.1rem] lg:w-1/6">
{shouldDisplayLoginMethod(LoginMethod.GOOGLE) && (
<Tooltip position="bottom" content={t("login.continue-with-google")}>
<IconButton
ariaLabel={t("login.continue-with-google")}
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/google${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
>
<FontAwesomeIcon icon={faGoogle} />
</IconButton>
</Tooltip>
)}
{shouldDisplayLoginMethod(LoginMethod.GITHUB) && (
<Tooltip position="bottom" content="Continue with GitHub">
<IconButton
ariaLabel="Login continue with GitHub"
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/github${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
>
<FontAwesomeIcon icon={faGithub} />
</IconButton>
</Tooltip>
)}
{shouldDisplayLoginMethod(LoginMethod.GITLAB) && (
<Tooltip position="bottom" content="Continue with GitLab">
<IconButton
ariaLabel="Login continue with GitLab"
colorSchema="primary"
variant="outline_bg"
onClick={() => {
const callbackPort = queryParams.get("callback_port");
window.open(
`/api/v1/sso/redirect/gitlab${
callbackPort ? `?callback_port=${callbackPort}` : ""
}`
);
window.close();
}}
className="h-10 w-full bg-mineshaft-600"
>
<FontAwesomeIcon icon={faGitlab} />
</IconButton>
</Tooltip>
)}
</div>
{(!config.enabledLoginMethods ||
(shouldDisplayLoginMethod(LoginMethod.EMAIL) && config.enabledLoginMethods.length > 1)) && (
<div className="my-4 flex w-1/4 min-w-[20rem] flex-row items-center py-2 lg:w-1/6">
@@ -316,7 +326,7 @@ export const InitialStep = ({ setStep, email, setEmail, password, setPassword }:
/>
</div>
)}
<div className="mt-3 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<div className="mt-4 w-1/4 min-w-[21.2rem] rounded-md text-center md:min-w-[20.1rem] lg:w-1/6">
<Button
disabled={shouldShowCaptcha && captchaToken === ""}
type="submit"