Move in progress apps temporarily

This commit is contained in:
Darren Clarke 2023-03-07 14:09:49 +00:00
parent ba04aa108c
commit 6eaaf8e9be
360 changed files with 6171 additions and 55 deletions

View file

@ -1,58 +0,0 @@
/* eslint-disable react/jsx-props-no-spreading */
import { AppProps } from "next/app";
import { SessionProvider } from "next-auth/react";
import { useRouter } from "next/router";
import { CssBaseline } from "@mui/material";
import { CacheProvider, EmotionCache } from "@emotion/react";
import { CookiesProvider } from "react-cookie";
import { I18n } from "react-polyglot";
import { AdapterDateFns } from "@mui/x-date-pickers-pro/AdapterDateFns";
import { LocalizationProvider } from "@mui/x-date-pickers-pro";
import { AppProvider } from "components/AppProvider";
import createEmotionCache from "lib/createEmotionCache";
import en from "locales/en.json";
import fr from "locales/fr.json";
import "@fontsource/poppins/400.css";
import "@fontsource/poppins/700.css";
import "@fontsource/roboto/400.css";
import "@fontsource/roboto/700.css";
import "@fontsource/playfair-display/900.css";
import "styles/global.css";
import { LicenseInfo } from "@mui/x-data-grid-pro";
LicenseInfo.setLicenseKey(
"fd009c623acc055adb16370731be92e4T1JERVI6NDA3NTQsRVhQSVJZPTE2ODAyNTAwMTUwMDAsS0VZVkVSU0lPTj0x"
);
const clientSideEmotionCache: any = createEmotionCache();
const messages = { en, fr };
interface LeafcutterWebProps extends AppProps {
// eslint-disable-next-line react/require-default-props
emotionCache?: EmotionCache;
}
const LeafcutterWeb = (props: LeafcutterWebProps) => {
const { locale } = useRouter();
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<SessionProvider session={(pageProps as any).session}>
<CacheProvider value={emotionCache}>
<CookiesProvider>
<CssBaseline />
<AppProvider>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<I18n locale={locale} messages={messages[locale]}>
<Component {...pageProps} />
</I18n>
</LocalizationProvider>
</AppProvider>
</CookiesProvider>
</CacheProvider>
</SessionProvider>
);
};
export default LeafcutterWeb;

View file

@ -1,50 +0,0 @@
// eslint-disable-next-line no-use-before-define
import * as React from "react";
import Document, { Html, Head, Main, NextScript } from "next/document";
import createEmotionServer from "@emotion/server/create-instance";
import createEmotionCache from "lib/createEmotionCache";
export default class LeafcutterDocument extends Document {
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
LeafcutterDocument.getInitialProps = async (ctx) => {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache as any);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App: any) => (props) =>
<App emotionCache={cache} {...props} />,
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(" ")}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
styles: [
...React.Children.toArray(initialProps.styles),
...emotionStyleTags,
],
};
};

View file

@ -1,172 +0,0 @@
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { useTranslate } from "react-polyglot";
import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { Grid, Container, Box, Button } from "@mui/material";
import { Layout } from "components/Layout";
import { checkAuth } from "lib/checkAuth";
import { useAppContext } from "components/AppProvider";
import { PageHeader } from "components/PageHeader";
import { AboutFeature } from "components/AboutFeature";
import { AboutBox } from "components/AboutBox";
import AbstractDiagram from "images/abstract-diagram.png";
import AboutHeader from "images/about-header.png";
import Globe from "images/globe.png";
import Controls from "images/controls.png";
import CommunityBackground from "images/community-background.png";
import Bicycle from "images/bicycle.png";
const About = () => {
const t = useTranslate();
const {
colors: { white, leafcutterElectricBlue, cdrLinkOrange },
typography: { h1, h4, p },
} = useAppContext();
return (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader
backgroundColor={leafcutterElectricBlue}
sx={{
backgroundImage: `url(${AboutHeader.src})`,
backgroundSize: "200px",
backgroundPosition: "bottom right",
backgroundRepeat: "no-repeat",
}}
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item xs={9}>
<Box component="h1" sx={h1}>
{t("aboutLeafcutterTitle")}
</Box>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("aboutLeafcutterDescription")}
</Box>
</Grid>
</Grid>
</PageHeader>
<Container maxWidth="lg">
<AboutFeature
title={t("whatIsLeafcutterTitle")}
description={t("whatIsLeafcutterDescription")}
direction="row"
image={AbstractDiagram}
showBackground={false}
textColumns={8}
/>
<AboutFeature
title={t("whatIsItForTitle")}
description={t("whatIsItForDescription")}
direction="row-reverse"
image={Controls}
showBackground
textColumns={8}
/>
<AboutFeature
title={t("whoCanUseItTitle")}
description={t("whoCanUseItDescription")}
direction="row"
image={Globe}
showBackground
textColumns={6}
/>
</Container>
<AboutBox backgroundColor={cdrLinkOrange}>
<Box component="h4" sx={{ ...h4, mt: 0 }}>
{t("whereDataComesFromTitle")}
</Box>
{t("whereDataComesFromDescription")
.split("\n")
.map((line: string, i: number) => (
<Box component="p" key={i} sx={p}>
{line}
</Box>
))}
</AboutBox>
<AboutBox backgroundColor={leafcutterElectricBlue}>
<Box component="h4" sx={{ ...h4, mt: 0 }}>
{t("projectSupportTitle")}
</Box>
{t("projectSupportDescription")
.split("\n")
.map((line: string, i: number) => (
<Box component="p" key={i} sx={p}>
{line}
</Box>
))}
</AboutBox>
<Box
sx={{
backgroundImage: `url(${CommunityBackground.src})`,
backgroundSize: "90%",
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
position: "relative",
height: "700px",
}}
>
<Box sx={{ position: "absolute", left: 0, bottom: -20, width: 300 }}>
<Image src={Bicycle} alt="" />
</Box>
<Container
maxWidth="md"
sx={{ textAlign: "center", paddingTop: "280px" }}
>
<Box
component="h4"
sx={{ ...h4, maxWidth: 500, margin: "0 auto", mt: 3 }}
>
{t("interestedInLeafcutterTitle")}
</Box>
{t("interestedInLeafcutterDescription")
.split("\n")
.map((line: string, i: number) => (
<Box
component="p"
key={i}
sx={{ ...p, maxWidth: 500, margin: "0 auto" }}
>
{line}
</Box>
))}
<Link href="mailto:info@digiresilience.org" passHref>
<Button
sx={{
fontSize: 14,
borderRadius: 500,
color: white,
backgroundColor: cdrLinkOrange,
fontWeight: "bold",
textTransform: "uppercase",
pl: 6,
pr: 5,
mt: 4,
":hover": {
backgroundColor: leafcutterElectricBlue,
color: white,
opacity: 0.8,
},
}}
>
{t("contactUs")}
</Button>
</Link>
</Container>
</Box>
</Layout>
);
};
export default About;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => checkAuth(context);

View file

@ -1,17 +0,0 @@
import NextAuth from "next-auth";
import Google from "next-auth/providers/google";
import Apple from "next-auth/providers/apple";
export default NextAuth({
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
Apple({
clientId: process.env.APPLE_CLIENT_ID,
clientSecret: process.env.APPLE_CLIENT_SECRET
}),
],
secret: process.env.NEXTAUTH_SECRET,
});

View file

@ -1,38 +0,0 @@
import { createProxyMiddleware } from "http-proxy-middleware";
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
const withAuthInfo =
(handler) => async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
req.headers["x-proxy-user"] = session.email.toLowerCase();
req.headers["x-proxy-roles"] = "leafcutter_user";
const auth = `${session.email.toLowerCase()}:${process.env.OPENSEARCH_USER_PASSWORD}`;
const buff = Buffer.from(auth);
const base64data = buff.toString("base64");
req.headers.Authorization = `Basic ${base64data}`;
return handler(req, res);
};
const proxy = createProxyMiddleware({
target: process.env.OPENSEARCH_DASHBOARDS_URL,
changeOrigin: true,
xfwd: true,
});
export default withAuthInfo(proxy);
export const config = {
api: {
bodyParser: false,
externalResolver: true,
},
};

View file

@ -1,33 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { getUserMetadata, saveUserMetadata } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "POST") {
return res.status(500).json({ message: "Only POST requests are allowed" });
}
const { email } = session;
const { name, query } = JSON.parse(req.body);
const result = await getUserMetadata(email);
const { savedSearches } = result;
await saveUserMetadata(email, {
savedSearches: [...savedSearches, { name, query }]
});
const { savedSearches: updatedSavedSearches } = await getUserMetadata(email);
return res.json(updatedSavedSearches)
}
export default handler;

View file

@ -1,31 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { getUserMetadata, saveUserMetadata } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "POST") {
return res.status(500).json({ message: "Only POST requests are allowed" });
}
const { email } = session;
const { name } = JSON.parse(req.body);
const { savedSearches } = await getUserMetadata(email);
const updatedSavedSearches = savedSearches.filter(search => search.name !== name);
const result = await saveUserMetadata(email, { savedSearches: updatedSavedSearches })
return res.json({ result })
}
export default handler;

View file

@ -1,25 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { getUserMetadata } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "GET") {
return res.status(500).json({ message: "Only GET requests are allowed" });
}
const { email } = session;
const { savedSearches } = await getUserMetadata(email);
return res.json(savedSearches)
}
export default handler;

View file

@ -1,19 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { getTrends } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
const results = await getTrends(5);
return res.json(results)
};
export default handler;

View file

@ -1,32 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { createUserVisualization } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "POST") {
return res.status(500).json({ message: "Only POST requests are allowed" });
}
const { visualizationID, title, description, query } = req.body;
const id = await createUserVisualization({
email: session.email,
visualizationID,
title,
description,
query
});
return res.json({ id })
};
export default handler;

View file

@ -1,26 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { deleteUserVisualization } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "POST") {
return res.status(500).json({ message: "Only POST requests are allowed" });
}
const { id } = req.body;
await deleteUserVisualization(session.email, id);
return res.json({ id })
};
export default handler;

View file

@ -1,23 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { performQuery } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
const { searchQuery } = req.query;
const rawQuery = await JSON.parse(decodeURI(searchQuery as string));
const results = await performQuery(rawQuery, 1000);
return res.json(results)
};
export default handler;

View file

@ -1,32 +0,0 @@
import { NextApiRequest, NextApiResponse } from "next";
import { getToken } from "next-auth/jwt";
import { updateUserVisualization } from "lib/opensearch";
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const session = await getToken({
req,
secret: process.env.NEXTAUTH_SECRET,
});
if (!session) {
return res.redirect("/login");
}
if (req.method !== "POST") {
return res.status(500).json({ message: "Only POST requests are allowed" });
}
const { id, title, description, query } = req.body;
await updateUserVisualization({
email: session.email,
id,
title,
description,
query
});
return res.json({ id })
};
export default handler;

View file

@ -1,83 +0,0 @@
import { useEffect } from "react";
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import Head from "next/head";
import { useTranslate } from "react-polyglot";
import { useRouter } from "next/router";
import { Box, Grid } from "@mui/material";
import { useCookies } from "react-cookie";
import { getTemplates } from "lib/opensearch";
import { Layout } from "components/Layout";
import { checkAuth } from "lib/checkAuth";
import { useAppContext } from "components/AppProvider";
import { PageHeader } from "components/PageHeader";
import { VisualizationBuilder } from "components/VisualizationBuilder";
const Create = ({ templates }) => {
const t = useTranslate();
const {
colors: { cdrLinkOrange },
typography: { h1, h4 },
} = useAppContext();
const router = useRouter();
const cookieName = "searchIntroComplete";
const [cookies, setCookie] = useCookies([cookieName]);
const searchIntroComplete = parseInt(cookies[cookieName], 10) || 0;
useEffect(() => {
if (searchIntroComplete === 0) {
setCookie(cookieName, `${1}`, { path: "/" });
router.push(`${router.pathname}?group=search&tooltip=1&checklist=1`);
}
}, [searchIntroComplete, router, setCookie]);
return (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader backgroundColor={cdrLinkOrange}>
<Grid container direction="row" spacing={2} alignItems="center">
{/* <Grid item xs={2} sx={{ textAlign: "center" }}>
<Image src={SearchCreateHeader} width={100} height={100} alt="" />
</Grid> */}
<Grid container direction="column" item xs={10}>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("searchAndCreateTitle")}
</Box>
</Grid>
<Grid item>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("searchAndCreateSubtitle")}
</Box>
</Grid>
{/* <Grid>
<Box component="p" sx={{ ...p }}>
{t("searchAndCreateDescription")}
</Box>
</Grid> */}
</Grid>
</Grid>
</PageHeader>
<VisualizationBuilder templates={templates} />
</Layout>
);
};
export default Create;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const res: any = await checkAuth(context);
if (res.redirect) {
return res;
}
res.props.templates = await getTemplates(100);
return res;
};

View file

@ -1,110 +0,0 @@
import Head from "next/head";
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { useTranslate } from "react-polyglot";
import { Box, Grid } from "@mui/material";
import { Layout } from "components/Layout";
import { checkAuth } from "lib/checkAuth";
import { PageHeader } from "components/PageHeader";
import { Question } from "components/Question";
import { useAppContext } from "components/AppProvider";
import FaqHeader from "images/faq-header.svg";
const FAQ = () => {
const t = useTranslate();
const {
colors: { lavender },
typography: { h1, h4, p },
} = useAppContext();
const questions = [
{
question: t("whatIsLeafcutterQuestion"),
answer: t("whatIsLeafcutterAnswer"),
},
{
question: t("whoBuiltLeafcutterQuestion"),
answer: t("whoBuiltLeafcutterAnswer"),
},
{
question: t("whoCanUseLeafcutterQuestion"),
answer: t("whoCanUseLeafcutterAnswer"),
},
{
question: t("whatCanYouDoWithLeafcutterQuestion"),
answer: t("whatCanYouDoWithLeafcutterAnswer"),
},
{
question: t("whereIsTheDataComingFromQuestion"),
answer: t("whereIsTheDataComingFromAnswer"),
},
{
question: t("whereIsTheDataStoredQuestion"),
answer: t("whereIsTheDataStoredAnswer"),
},
{
question: t("howDoWeKeepTheDataSafeQuestion"),
answer: t("howDoWeKeepTheDataSafeAnswer"),
},
{
question: t("howLongDoYouKeepTheDataQuestion"),
answer: t("howLongDoYouKeepTheDataAnswer"),
},
{
question: t("whatOrganizationsAreParticipatingQuestion"),
answer: t("whatOrganizationsAreParticipatingAnswer"),
},
{
question: t("howDidYouGetMyProfileInformationQuestion"),
answer: t("howDidYouGetMyProfileInformationAnswer"),
},
{
question: t("howCanILearnMoreAboutLeafcutterQuestion"),
answer: t("howCanILearnMoreAboutLeafcutterAnswer"),
},
];
return (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader
backgroundColor={lavender}
sx={{
backgroundImage: `url(${FaqHeader.src})`,
backgroundSize: "150px",
backgroundPosition: "bottom right",
backgroundRepeat: "no-repeat",
}}
>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("frequentlyAskedQuestionsTitle")}
</Box>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("frequentlyAskedQuestionsSubtitle")}
</Box>
<Box component="p" sx={{ ...p }}>
{t("frequentlyAskedQuestionsDescription")}
</Box>
</Grid>
</Grid>
</PageHeader>
{questions.map((q, index) => (
<Question key={index} question={q.question} answer={q.answer} />
))}
</Layout>
);
};
export default FAQ;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => checkAuth(context);

View file

@ -1,119 +0,0 @@
import { useEffect } from "react";
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { useRouter } from "next/router";
import Head from "next/head";
import Link from "next/link";
import ReactMarkdown from "react-markdown";
import { Grid, Button } from "@mui/material";
import { useTranslate } from "react-polyglot";
import { useCookies } from "react-cookie";
import { Layout } from "components/Layout";
import { checkAuth } from "lib/checkAuth";
import { getUserVisualizations } from "lib/opensearch";
import { Welcome } from "components/Welcome";
import { WelcomeDialog } from "components/WelcomeDialog";
import { VisualizationCard } from "components/VisualizationCard";
import { useAppContext } from "components/AppProvider";
const MyVisualizations = ({ visualizations }) => {
const router = useRouter();
const cookieName = "homeIntroComplete";
const [cookies, setCookie] = useCookies([cookieName]);
const t = useTranslate();
const {
colors: { white, leafcutterElectricBlue },
typography: { h4 },
} = useAppContext();
const homeIntroComplete = parseInt(cookies[cookieName], 10) || 0;
useEffect(() => {
if (homeIntroComplete === 0) {
setCookie(cookieName, `${1}`, { path: "/" });
router.push(`${router.pathname}?tooltip=welcome`);
}
}, [homeIntroComplete, router, setCookie]);
return (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<Welcome />
<Grid
container
spacing={3}
sx={{ pt: "22px", pb: "22px" }}
direction="row-reverse"
>
<Link href="/create" passHref>
<Button
sx={{
fontSize: 14,
borderRadius: 500,
color: leafcutterElectricBlue,
border: `2px solid ${leafcutterElectricBlue}`,
fontWeight: "bold",
textTransform: "uppercase",
pl: 6,
pr: 5,
":hover": {
backgroundColor: leafcutterElectricBlue,
color: white,
opacity: 0.8,
},
}}
>
{t("createVisualization")}
</Button>
</Link>
</Grid>
<Grid
container
direction="row"
wrap="wrap"
spacing={3}
justifyContent="space-between"
>
{visualizations.length === 0 ? (
<Grid
container
sx={{ height: 300, width: "100%", pt: 10 }}
justifyContent="center"
>
<Grid item sx={{ ...h4, width: 450, textAlign: "center" }}>
<ReactMarkdown>{t("noSavedVisualizations")}</ReactMarkdown>
</Grid>
</Grid>
) : null}
{visualizations.map((visualization, index) => (
<VisualizationCard
id={visualization.id}
key={index}
title={visualization.title}
description={visualization.description}
url={visualization.url}
/>
))}
</Grid>
<WelcomeDialog />
</Layout>
);
};
export default MyVisualizations;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const res: any = await checkAuth(context);
if (res.redirect) {
return res;
}
res.props.visualizations = await getUserVisualizations(
res.props.session.user.email,
20
);
return res;
};

View file

@ -1,121 +0,0 @@
import Head from "next/head";
import Link from "next/link";
import Image from "next/image";
import { Box, Grid, Container, IconButton } from "@mui/material";
import { Apple as AppleIcon, Google as GoogleIcon } from "@mui/icons-material";
import { useTranslate } from "react-polyglot";
import { LanguageSelect } from "components/LanguageSelect";
import LeafcutterLogoLarge from "images/leafcutter-logo-large.png";
import { signIn, getSession } from "next-auth/react";
import { useAppContext } from "components/AppProvider";
const Login = ({ session }) => {
const t = useTranslate();
const {
colors: { leafcutterElectricBlue, lightGray },
typography: { h1, h4 },
} = useAppContext();
const buttonStyles = {
backgroundColor: lightGray,
borderRadius: 500,
width: "100%",
fontSize: "16px",
fontWeight: "bold",
};
return (
<>
<Head>
<title>Leafcutter: Login</title>
</Head>
<Grid container direction="row-reverse" sx={{ p: 3 }}>
<Grid item>
<LanguageSelect />
</Grid>
</Grid>
<Container maxWidth="md" sx={{ mt: 3, mb: 20 }}>
<Grid container spacing={2} direction="column" alignItems="center">
<Grid item>
<Box sx={{ maxWidth: 200 }}>
<Image src={LeafcutterLogoLarge} alt="" objectFit="fill" />
</Box>
</Grid>
<Grid item sx={{ textAlign: "center" }}>
<Box component="h1" sx={{ ...h1, color: leafcutterElectricBlue }}>
{t("welcomeToLeafcutter")}
</Box>
<Box component="h4" sx={{ ...h4, mt: 1 }}>
{t("welcomeToLeafcutterDescription")}
</Box>
</Grid>
<Grid item>
{!session ? (
<Grid
container
spacing={3}
direction="column"
alignItems="center"
sx={{ width: 450, mt: 1 }}
>
<Grid item sx={{ width: "100%" }}>
<IconButton
sx={buttonStyles}
onClick={() =>
signIn("google", {
callbackUrl: `${window.location.origin}/setup`,
})
}
>
<GoogleIcon sx={{ mr: 1 }} />
{`${t("signInWith")} Google`}
</IconButton>
</Grid>
<Grid item sx={{ width: "100%" }}>
<IconButton
sx={buttonStyles}
onClick={() =>
signIn("apple", {
callbackUrl: `${window.location.origin}/setup`,
})
}
>
<AppleIcon sx={{ mr: 1 }} />
{`${t("signInWith")} Apple`}
</IconButton>
</Grid>
<Grid item sx={{ mt: 2 }}>
<Box>
{t("dontHaveAccount")}{" "}
<Link href="mailto:info@digiresilience.org">
{t("requestAccessHere")}
</Link>
</Box>
</Grid>
</Grid>
) : null}
{session ? (
<>
<Box component="h4" sx={h4}>
{`${t("welcome")}, ${
session.user.name ?? session.user.email
}.`}
</Box>
<Link href="/">{t("goHome")}</Link>
</>
) : null}
</Grid>
</Grid>
</Container>
</>
);
};
export default Login;
export async function getServerSideProps(context) {
const session = (await getSession(context)) ?? null;
return {
props: { session },
};
}

View file

@ -1,109 +0,0 @@
import { FC } from "react";
/* eslint-disable no-underscore-dangle */
import { GetServerSideProps, GetServerSidePropsContext } from "next";
// import { Client } from "@opensearch-project/opensearch";
import { RawDataViewer } from "components/RawDataViewer";
import { VisualizationDetail } from "components/VisualizationDetail";
import { checkAuth } from "lib/checkAuth";
// import { createVisualization } from "lib/opensearch";
interface PreviewProps {
visualization: any;
visualizationType: string;
data: any[];
}
const Preview: FC<PreviewProps> = ({
visualization,
visualizationType,
data,
}) =>
visualizationType === "rawData" ? (
<RawDataViewer rows={data} height={750} />
) : (
<VisualizationDetail {...visualization} />
);
export default Preview;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const res: any = await checkAuth(context);
if (res.redirect) {
return res;
}
/*
const {
visualizationID,
searchQuery,
visualizationType = "table",
} = context.query;
const node = `https://${process.env.OPENSEARCH_USERNAME}:${process.env.OPENSEARCH_PASSWORD}@${process.env.OPENSEARCH_URL}`;
const client = new Client({
node,
ssl: {
rejectUnauthorized: false,
},
});
res.props.visualizationType = visualizationType as string;
if (visualizationType !== "rawData") {
await createVisualization({
id: visualizationID as string,
query: await JSON.parse(decodeURI(searchQuery as string)),
kind: visualizationType as string,
});
const rawResponse = await client.search({
index: ".kibana_1",
size: 200,
});
const response = rawResponse.body;
const hits = response.hits.hits.filter(
(hit) => hit._id.split(":")[1] === visualizationID[0]
);
const hit = hits[0];
res.props.visualization = {
id: hit._id.split(":")[1],
title: hit._source.visualization.title,
description: hit._source.visualization.description,
url: `/app/visualize?security_tenant=global#/edit/${
hit._id.split(":")[1]
}?embed=true`,
};
}
const rawQuery = await JSON.parse(decodeURI(searchQuery as string));
const query = {
bool: {
should: [],
must_not: [],
},
};
if (rawQuery.impactedTechnology.values.length > 0) {
rawQuery.impactedTechnology.values.forEach((value) => {
query.bool.should.push({
match: { technology: value },
});
});
}
console.log({ query });
const dataResponse = await client.search({
index: "demo_data",
size: 200,
body: { query },
});
console.log({ dataResponse });
res.props.data = dataResponse.body.hits.hits.map((hit) => ({
id: hit._id,
...hit._source,
}));
console.log({ data: res.props.data });
console.log(res.props.data[0]);
*/
return res;
};

View file

@ -1,46 +0,0 @@
import { FC, useLayoutEffect } from "react";
import { useRouter } from "next/router";
import { Grid, CircularProgress } from "@mui/material";
import Iframe from "react-iframe";
import { useAppContext } from "components/AppProvider";
export const Setup: FC = () => {
const {
colors: { leafcutterElectricBlue },
} = useAppContext();
const router = useRouter();
useLayoutEffect(() => {
setTimeout(() => router.push("/"), 4000);
}, [router]);
return (
<Grid
sx={{ width: "100%", height: 700 }}
direction="row"
justifyContent="space-around"
alignItems="center"
alignContent="center"
>
<Grid
item
xs={12}
sx={{
width: "200px",
height: 700,
textAlign: "center",
margin: "0 auto",
pt: 30,
}}
>
<Iframe url="/app/home" height="1" width="1" frameBorder={0} />
<CircularProgress
size={80}
thickness={5}
sx={{ color: leafcutterElectricBlue }}
/>
</Grid>
</Grid>
);
};
export default Setup;

View file

@ -1,88 +0,0 @@
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import Head from "next/head";
import { Grid, Box } from "@mui/material";
import { useTranslate } from "react-polyglot";
import { Layout } from "components/Layout";
import { checkAuth } from "lib/checkAuth";
import { getTrends } from "lib/opensearch";
import { PageHeader } from "components/PageHeader";
import { VisualizationCard } from "components/VisualizationCard";
import { useAppContext } from "components/AppProvider";
const Trends = ({ visualizations }) => {
const t = useTranslate();
const {
colors: { cdrLinkOrange },
typography: { h1, h4, p },
} = useAppContext();
return (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<PageHeader backgroundColor={cdrLinkOrange}>
<Grid
container
direction="row"
spacing={2}
justifyContent="space-between"
alignItems="center"
>
{/* <Grid item xs={3} sx={{ textAlign: "center" }}>
<Image src={SearchCreateHeader} width={200} height={200} alt="" />
</Grid> */}
<Grid item container direction="column" xs={12}>
<Grid item>
<Box component="h1" sx={{ ...h1 }}>
{t("trendsTitle")}
</Box>
</Grid>
<Grid item>
<Box component="h4" sx={{ ...h4, mt: 1, mb: 1 }}>
{t("trendsSubtitle")}
</Box>
</Grid>
<Grid>
<Box component="p" sx={{ ...p }}>
{t("trendsDescription")}
</Box>
</Grid>
</Grid>
</Grid>
</PageHeader>
<Grid
container
direction="row"
wrap="wrap"
spacing={3}
justifyContent="space-between"
>
{visualizations.map((visualization, index) => (
<VisualizationCard
key={index}
id={visualization.id}
title={visualization.title}
description={visualization.description}
url={visualization.url}
/>
))}
</Grid>
</Layout>
);
};
export default Trends;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const res: any = await checkAuth(context);
if (res.redirect) {
return res;
}
res.props.visualizations = await getTrends(25);
return res;
};

View file

@ -1,59 +0,0 @@
/* eslint-disable no-underscore-dangle */
import { GetServerSideProps, GetServerSidePropsContext } from "next";
import { Client } from "@opensearch-project/opensearch";
import Head from "next/head";
import { Layout } from "components/Layout";
import { VisualizationDetail } from "components/VisualizationDetail";
import { checkAuth } from "lib/checkAuth";
const Visualization = ({ visualization }) => (
<Layout>
<Head>
<title>Digital Threat Dashboard Leafcutter</title>
</Head>
<VisualizationDetail {...visualization} />
</Layout>
);
export default Visualization;
export const getServerSideProps: GetServerSideProps = async (
context: GetServerSidePropsContext
) => {
const res: any = await checkAuth(context);
if (res.redirect) {
return res;
}
const { visualizationID } = context.query;
const node = `https://${process.env.OPENSEARCH_USERNAME}:${process.env.OPENSEARCH_PASSWORD}@${process.env.OPENSEARCH_URL}`;
const client = new Client({
node,
ssl: {
rejectUnauthorized: false,
},
});
const rawResponse = await client.search({
index: ".kibana_1",
size: 200,
});
const response = rawResponse.body;
const hits = response.hits.hits.filter(
(hit) => hit._id.split(":")[1] === visualizationID[0]
);
const hit = hits[0];
res.props.visualization = {
id: hit._id.split(":")[1],
title: hit._source.visualization.title,
description: hit._source.visualization.description,
url: `/app/visualize?security_tenant=global#/edit/${
hit._id.split(":")[1]
}?embed=true`,
};
return res;
};