diff --git a/components/MessagePage.tsx b/components/MessagePage.tsx new file mode 100644 index 0000000000..6c7879b3dc --- /dev/null +++ b/components/MessagePage.tsx @@ -0,0 +1,13 @@ +export default function MessagePage({ text }) { + return ( +
+
+
+
+ {text} +
+
+
+
+ ); +} diff --git a/components/layout/BaseLayoutAuthorized.tsx b/components/layout/BaseLayoutManagement.tsx similarity index 72% rename from components/layout/BaseLayoutAuthorized.tsx rename to components/layout/BaseLayoutManagement.tsx index 9144006b84..8ba3ac951a 100644 --- a/components/layout/BaseLayoutAuthorized.tsx +++ b/components/layout/BaseLayoutManagement.tsx @@ -1,15 +1,11 @@ -import { signIn, useSession } from "next-auth/react"; import Head from "next/head"; -import { useEffect, useState } from "react"; -import { identifyPoshogUser } from "../../lib/posthog"; import { classNames } from "../../lib/utils"; -import Loading from "../Loading"; import MenuBreadcrumbs from "./MenuBreadcrumbs"; import MenuProfile from "./MenuProfile"; import MenuSteps from "./MenuSteps"; import NewFormNavButton from "./NewFormNavButton"; -interface BaseLayoutAuthorizedProps { +interface BaseLayoutManagementProps { title: string; breadcrumbs: any; steps?: any; @@ -19,7 +15,7 @@ interface BaseLayoutAuthorizedProps { limitHeightScreen?: boolean; } -export default function BaseLayoutAuthorized({ +export default function BaseLayoutManagement({ title, breadcrumbs, steps, @@ -27,25 +23,7 @@ export default function BaseLayoutAuthorized({ children, bgClass = "bg-ui-gray-lighter", limitHeightScreen = false, -}: BaseLayoutAuthorizedProps) { - const { data: session, status } = useSession(); - const [loading, setLoading] = useState(true); - - useEffect(() => { - if (status !== "loading") { - if (!session) { - signIn(); - } else { - setLoading(false); - identifyPoshogUser(session.user.email); - } - } - }, [session, status]); - - if (status === "loading" || loading) { - return ; - } - +}: BaseLayoutManagementProps) { return ( <> diff --git a/components/layout/WithAuthentication.tsx b/components/layout/WithAuthentication.tsx new file mode 100644 index 0000000000..cec6aec170 --- /dev/null +++ b/components/layout/WithAuthentication.tsx @@ -0,0 +1,21 @@ +import { signIn, useSession } from "next-auth/react"; +import Loading from "../Loading"; + +const withAuthentication = (Component) => + function WithAuth(props) { + const { status } = useSession({ + required: true, + onUnauthenticated() { + // The user is not authenticated, handle it here. + return signIn(); + }, + }); + + if (status === "loading") { + return ; + } + + return ; + }; + +export default withAuthentication; diff --git a/lib/auth.ts b/lib/auth.ts index 976b6c32ce..dd727b1195 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -9,3 +9,22 @@ export async function verifyPassword(password: string, hashedPassword: string) { const isValid = await compare(password, hashedPassword); return isValid; } +export function requireAuthentication(gssp) { + return async (context) => { + const { req, resolvedUrl } = context; + const token = req.cookies.userToken; + + if (!token) { + return { + redirect: { + destination: `/auth/signin?callbackUrl=${encodeURIComponent( + resolvedUrl + )}`, + statusCode: 302, + }, + }; + } + + return await gssp(context); // Continue on to call `getServerSideProps` logic + }; +} diff --git a/pages/auth/signin.tsx b/pages/auth/signin.tsx index 480096d539..3c0cdb6060 100644 --- a/pages/auth/signin.tsx +++ b/pages/auth/signin.tsx @@ -12,15 +12,15 @@ export default function SignInPage() { const handleSubmit = async (e) => { e.preventDefault(); await signIn("credentials", { - callbackUrl: router.query.callbackUrl?.toString(), + callbackUrl: router.query.callbackUrl?.toString() || "/forms", email: e.target.elements.email.value, password: e.target.elements.password.value, }); - router.push( + /* router.push( `/auth/verification-requested?email=${encodeURIComponent( e.target.elements.email.value )}` - ); + ); */ }; return ( diff --git a/pages/f/[id].tsx b/pages/f/[id].tsx index cc74929b20..797fd9a6d7 100644 --- a/pages/f/[id].tsx +++ b/pages/f/[id].tsx @@ -1,10 +1,9 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; import App from "../../components/frontend/App"; import BaseLayoutUnauthorized from "../../components/layout/BaseLayoutUnauthorized"; import Loading from "../../components/Loading"; +import MessagePage from "../../components/MessagePage"; import { useNoCodeFormPublic } from "../../lib/noCodeForm"; +import { useRouter } from "next/router"; export default function Share({}) { const router = useRouter(); @@ -13,7 +12,9 @@ export default function Share({}) { useNoCodeFormPublic(formId); if (isErrorNoCodeForm) { - return

Not found

; + return ( + + ); } if (isLoadingNoCodeForm) { @@ -26,11 +27,3 @@ export default function Share({}) {
); } - -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; diff --git a/pages/forms/[id]/form/index.tsx b/pages/forms/[id]/form/index.tsx index 8ac93d537a..331b6f9f36 100644 --- a/pages/forms/[id]/form/index.tsx +++ b/pages/forms/[id]/form/index.tsx @@ -1,34 +1,45 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; +import BaseLayoutManagement from "../../../../components/layout/BaseLayoutManagement"; import Builder from "../../../../components/builder/Builder"; import FormCode from "../../../../components/form/FormCode"; -import BaseLayoutAuthorized from "../../../../components/layout/BaseLayoutAuthorized"; import FullWidth from "../../../../components/layout/FullWidth"; import LimitedWidth from "../../../../components/layout/LimitedWidth"; -import SecondNavBar from "../../../../components/layout/SecondNavBar"; import Loading from "../../../../components/Loading"; -import { useForm } from "../../../../lib/forms"; +import MessagePage from "../../../../components/MessagePage"; +import SecondNavBar from "../../../../components/layout/SecondNavBar"; import { useCodeSecondNavigation } from "../../../../lib/navigation/formCodeSecondNavigation"; +import { useForm } from "../../../../lib/forms"; import { useFormMenuSteps } from "../../../../lib/navigation/formMenuSteps"; +import { useMemo } from "react"; +import { useRouter } from "next/router"; +import withAuthentication from "../../../../components/layout/WithAuthentication"; -export default function FormPage() { +function FormPage() { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const codeSecondNavigation = useCodeSecondNavigation(formId); const formMenuSteps = useFormMenuSteps(formId); + const breadcrumbs = useMemo(() => { + if (form) { + return [{ name: form.name, href: "#", current: true }]; + } + }, [form]); + if (isLoadingForm) { return ; } - const breadcrumbs = [{ name: form.name, href: "#", current: true }]; - - if (form.formType === "NOCODE") { + if (isErrorForm) { return ( - <> - + ); + } + + return ( + <> + {form.formType === "NOCODE" ? ( + - - - ); - } else { - return ( - - + + ) : ( + + - - - - - ); - } + + + + + )} + + ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(FormPage); diff --git a/pages/forms/[id]/form/react.tsx b/pages/forms/[id]/form/react.tsx index 2fdd61453e..9b91f04c92 100644 --- a/pages/forms/[id]/form/react.tsx +++ b/pages/forms/[id]/form/react.tsx @@ -1,30 +1,30 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import { FaDiscord } from "react-icons/fa"; import hljs from "highlight.js"; +import bash from "highlight.js/lib/languages/bash"; +import javascript from "highlight.js/lib/languages/javascript"; +import { useRouter } from "next/router"; import { useEffect } from "react"; -import BaseLayoutAuthorized from "../../../../components/layout/BaseLayoutAuthorized"; +import { FaDiscord } from "react-icons/fa"; +import BaseLayoutManagement from "../../../../components/layout/BaseLayoutManagement"; import LimitedWidth from "../../../../components/layout/LimitedWidth"; import SecondNavBar from "../../../../components/layout/SecondNavBar"; +import withAuthentication from "../../../../components/layout/WithAuthentication"; import Loading from "../../../../components/Loading"; +import MessagePage from "../../../../components/MessagePage"; import { useForm } from "../../../../lib/forms"; import { useCodeSecondNavigation } from "../../../../lib/navigation/formCodeSecondNavigation"; import { useFormMenuSteps } from "../../../../lib/navigation/formMenuSteps"; -import javascript from "highlight.js/lib/languages/javascript"; -import bash from "highlight.js/lib/languages/bash"; hljs.registerLanguage("javascript", javascript); hljs.registerLanguage("bash", bash); -export default function ReactPage() { +function ReactPage() { useEffect(() => { hljs.initHighlighting(); }, []); const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const codeSecondNavigation = useCodeSecondNavigation(formId); const formMenuSteps = useFormMenuSteps(formId); @@ -32,9 +32,15 @@ export default function ReactPage() { return ; } + if (isErrorForm) { + return ( + + ); + } + return ( <> - {` + domain="${window?.location.host}" + protocol="${window?.location.protocol.replace(":", "")}"> - + ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(ReactPage); diff --git a/pages/forms/[id]/index.tsx b/pages/forms/[id]/index.tsx index 9a4c588d45..f4f70404d1 100644 --- a/pages/forms/[id]/index.tsx +++ b/pages/forms/[id]/index.tsx @@ -1,4 +1,3 @@ -import { GetServerSideProps } from "next"; import { getSession } from "next-auth/react"; import Loading from "../../../components/Loading"; import { formHasOwnership } from "../../../lib/api"; @@ -8,21 +7,27 @@ export default function FormIndex() { return ; } -export const getServerSideProps: GetServerSideProps = async ({ - req, - res, - params, -}) => { +export async function getServerSideProps({ req, params, resolvedUrl }) { const session = await getSession({ req }); if (!session) { - res.statusCode = 403; - return { props: {} }; + return { + redirect: { + destination: `/auth/signin?callbackUrl=${encodeURIComponent( + resolvedUrl + )}`, + statusCode: 302, + }, + }; } const formId = params.id.toString(); const ownership = await formHasOwnership(session, formId); if (!ownership) { - res.statusCode = 403; - return { props: {} }; + return { + redirect: { + destination: resolvedUrl, + statusCode: 404, + }, + }; } // redirect based on number of submissionSession const submissionSessionsData = await prisma.submissionSession.findMany({ @@ -36,7 +41,6 @@ export const getServerSideProps: GetServerSideProps = async ({ permanent: false, destination: `/forms/${formId}/results/summary`, }, - props: {}, }; } else { // redirect to /form if there isn't one submissionSession @@ -45,7 +49,6 @@ export const getServerSideProps: GetServerSideProps = async ({ permanent: false, destination: `/forms/${formId}/form`, }, - props: {}, }; } -}; +} diff --git a/pages/forms/[id]/pipelines.tsx b/pages/forms/[id]/pipelines.tsx index 7ce67320db..3744716574 100644 --- a/pages/forms/[id]/pipelines.tsx +++ b/pages/forms/[id]/pipelines.tsx @@ -1,6 +1,4 @@ import { CodeIcon, PuzzleIcon } from "@heroicons/react/outline"; -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; import { useRouter } from "next/router"; import { SiAirtable, @@ -9,10 +7,12 @@ import { SiSlack, SiZapier, } from "react-icons/si"; -import BaseLayoutAuthorized from "../../../components/layout/BaseLayoutAuthorized"; +import BaseLayoutManagement from "../../../components/layout/BaseLayoutManagement"; import EmptyPageFiller from "../../../components/layout/EmptyPageFiller"; import LimitedWidth from "../../../components/layout/LimitedWidth"; +import withAuthentication from "../../../components/layout/WithAuthentication"; import Loading from "../../../components/Loading"; +import MessagePage from "../../../components/MessagePage"; import { useForm } from "../../../lib/forms"; import { useFormMenuSteps } from "../../../lib/navigation/formMenuSteps"; import { classNames } from "../../../lib/utils"; @@ -68,117 +68,113 @@ const libs = [ }, ]; -export default function PipelinesPage() { +function PipelinesPage() { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const formMenuSteps = useFormMenuSteps(formId); if (isLoadingForm) { return ; } - return ( - <> - - -
-
-

- Data Pipelines -

-
-
-
-

- Pipe your data exactly where you need it. Add conditions for - variable data piping. -

-
- - - - +
+ ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(PipelinesPage); diff --git a/pages/forms/[id]/preview.tsx b/pages/forms/[id]/preview.tsx index 95a4064a9f..0a8e1ef358 100644 --- a/pages/forms/[id]/preview.tsx +++ b/pages/forms/[id]/preview.tsx @@ -1,19 +1,19 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import { useState } from "react"; -import { toast } from "react-toastify"; -import { v4 as uuidv4 } from "uuid"; import App from "../../../components/frontend/App"; import LayoutPreview from "../../../components/layout/LayoutPreview"; import Loading from "../../../components/Loading"; +import MessagePage from "../../../components/MessagePage"; +import { toast } from "react-toastify"; import { useForm } from "../../../lib/forms"; import { useNoCodeForm } from "../../../lib/noCodeForm"; +import { useRouter } from "next/router"; +import { useState } from "react"; +import { v4 as uuidv4 } from "uuid"; +import withAuthentication from "../../../components/layout/WithAuthentication"; -export default function Share({}) { +function SharePage({}) { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(formId); + const { form, isLoadingForm, isErrorForm } = useForm(formId); const [appId, setAppId] = useState(uuidv4()); const { noCodeForm, isLoadingNoCodeForm } = useNoCodeForm(formId); @@ -27,6 +27,12 @@ export default function Share({}) { return ; } + if (isErrorForm) { + return ( + + ); + } + if (form.formType !== "NOCODE") { return (
Preview is only avaiblable for Forms built with No-Code-Editor
@@ -45,10 +51,4 @@ export default function Share({}) { ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(SharePage); diff --git a/pages/forms/[id]/results/insights.tsx b/pages/forms/[id]/results/insights.tsx index 48dfcdff4b..65b9ec2145 100644 --- a/pages/forms/[id]/results/insights.tsx +++ b/pages/forms/[id]/results/insights.tsx @@ -1,19 +1,19 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import BaseLayoutAuthorized from "../../../../components/layout/BaseLayoutAuthorized"; +import BaseLayoutManagement from "../../../../components/layout/BaseLayoutManagement"; import LimitedWidth from "../../../../components/layout/LimitedWidth"; -import SecondNavBar from "../../../../components/layout/SecondNavBar"; import Loading from "../../../../components/Loading"; +import MessagePage from "../../../../components/MessagePage"; import ResultsInsights from "../../../../components/results/ResultsInsights"; +import SecondNavBar from "../../../../components/layout/SecondNavBar"; import { useForm } from "../../../../lib/forms"; import { useFormMenuSteps } from "../../../../lib/navigation/formMenuSteps"; import { useFormResultsSecondNavigation } from "../../../../lib/navigation/formResultsSecondNavigation"; +import { useRouter } from "next/router"; +import withAuthentication from "../../../../components/layout/WithAuthentication"; -export default function ResultsInsightsPage() { +function ResultsInsightsPage() { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const formMenuSteps = useFormMenuSteps(formId); const formResultsSecondNavigation = useFormResultsSecondNavigation(formId); @@ -21,10 +21,16 @@ export default function ResultsInsightsPage() { return ; } + if (isErrorForm) { + return ( + + ); + } + return ( - - + ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(ResultsInsightsPage); diff --git a/pages/forms/[id]/results/responses.tsx b/pages/forms/[id]/results/responses.tsx index 607973e314..7085c67abb 100644 --- a/pages/forms/[id]/results/responses.tsx +++ b/pages/forms/[id]/results/responses.tsx @@ -1,19 +1,19 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import BaseLayoutAuthorized from "../../../../components/layout/BaseLayoutAuthorized"; +import BaseLayoutManagement from "../../../../components/layout/BaseLayoutManagement"; import FullWidth from "../../../../components/layout/FullWidth"; -import SecondNavBar from "../../../../components/layout/SecondNavBar"; import Loading from "../../../../components/Loading"; +import MessagePage from "../../../../components/MessagePage"; import ResultsResponses from "../../../../components/results/ResultsResponses"; +import SecondNavBar from "../../../../components/layout/SecondNavBar"; import { useForm } from "../../../../lib/forms"; import { useFormMenuSteps } from "../../../../lib/navigation/formMenuSteps"; import { useFormResultsSecondNavigation } from "../../../../lib/navigation/formResultsSecondNavigation"; +import { useRouter } from "next/router"; +import withAuthentication from "../../../../components/layout/WithAuthentication"; -export default function ResultsResponsesPage() { +function ResultsResponsesPage() { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const formMenuSteps = useFormMenuSteps(formId); const formResultsSecondNavigation = useFormResultsSecondNavigation(formId); @@ -21,8 +21,14 @@ export default function ResultsResponsesPage() { return ; } + if (isErrorForm) { + return ( + + ); + } + return ( - - + ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(ResultsResponsesPage); diff --git a/pages/forms/[id]/results/summary.tsx b/pages/forms/[id]/results/summary.tsx index b073ae6068..c6ab8044ce 100644 --- a/pages/forms/[id]/results/summary.tsx +++ b/pages/forms/[id]/results/summary.tsx @@ -1,19 +1,19 @@ -import { GetServerSideProps } from "next"; -import { getSession } from "next-auth/react"; -import { useRouter } from "next/router"; -import BaseLayoutAuthorized from "../../../../components/layout/BaseLayoutAuthorized"; +import BaseLayoutManagement from "../../../../components/layout/BaseLayoutManagement"; import LimitedWidth from "../../../../components/layout/LimitedWidth"; -import SecondNavBar from "../../../../components/layout/SecondNavBar"; import Loading from "../../../../components/Loading"; +import MessagePage from "../../../../components/MessagePage"; import ResultsSummary from "../../../../components/results/ResultsSummary"; +import SecondNavBar from "../../../../components/layout/SecondNavBar"; import { useForm } from "../../../../lib/forms"; import { useFormMenuSteps } from "../../../../lib/navigation/formMenuSteps"; import { useFormResultsSecondNavigation } from "../../../../lib/navigation/formResultsSecondNavigation"; +import { useRouter } from "next/router"; +import withAuthentication from "../../../../components/layout/WithAuthentication"; -export default function ResultsSummaryPage() { +function ResultsSummaryPage() { const router = useRouter(); const formId = router.query.id.toString(); - const { form, isLoadingForm } = useForm(router.query.id); + const { form, isLoadingForm, isErrorForm } = useForm(router.query.id); const formMenuSteps = useFormMenuSteps(formId); const formResultsSecondNavigation = useFormResultsSecondNavigation(formId); @@ -21,8 +21,14 @@ export default function ResultsSummaryPage() { return ; } + if (isErrorForm) { + return ( + + ); + } + return ( - - + ); } -export const getServerSideProps: GetServerSideProps = async ({ req, res }) => { - const session = await getSession({ req }); - if (!session) { - res.statusCode = 403; - } - return { props: {} }; -}; +export default withAuthentication(ResultsSummaryPage); diff --git a/pages/forms/index.tsx b/pages/forms/index.tsx index a2d49918d2..adf0bb83f8 100644 --- a/pages/forms/index.tsx +++ b/pages/forms/index.tsx @@ -1,25 +1,26 @@ import FormList from "../../components/FormList"; -import BaseLayoutAuthorized from "../../components/layout/BaseLayoutAuthorized"; +import BaseLayoutManagement from "../../components/layout/BaseLayoutManagement"; import LimitedWidth from "../../components/layout/LimitedWidth"; +import withAuthentication from "../../components/layout/WithAuthentication"; import Loading from "../../components/Loading"; import { useForms } from "../../lib/forms"; -export default function Forms({}) { +function FormsPage({}) { const { isLoadingForms } = useForms(); if (isLoadingForms) { ; } return ( - <> - - - - - - + + + + + ); } + +export default withAuthentication(FormsPage);