From 1aba39b0e99ff2e25d7d37864981966fa826443f Mon Sep 17 00:00:00 2001 From: Timothy Armes Date: Tue, 9 Apr 2024 14:04:49 +0200 Subject: [PATCH] Sign in mechanism --- .env.example | 10 +- .gitignore | 6 +- .vscode/settings.json | 2 + package.json | 5 +- src/app/[locale]/(main)/auth/sign-in/page.tsx | 104 +++++++++++ src/app/[locale]/(main)/components/Navbar.tsx | 3 + src/app/[locale]/layout.tsx | 4 +- src/app/api/auth/[...nextauth]/route.ts | 1 + src/auth.ts | 30 ++++ src/components/AuthButton.tsx | 36 ++++ src/components/InfoMessage.tsx | 12 +- src/messages/en.json | 25 ++- src/messages/fr.json | 25 ++- src/providers/index.tsx | 14 +- src/utils/nodemailerProvider.ts | 164 ++++++++++++++++++ yarn.lock | 113 ++++++++++-- 16 files changed, 525 insertions(+), 29 deletions(-) create mode 100644 src/app/[locale]/(main)/auth/sign-in/page.tsx create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/auth.ts create mode 100644 src/components/AuthButton.tsx create mode 100644 src/utils/nodemailerProvider.ts diff --git a/.env.example b/.env.example index 0041bcd..4dd29be 100644 --- a/.env.example +++ b/.env.example @@ -5,4 +5,12 @@ DISCORD_WEBHOOK_ERROR_LOGS_URL= DISCORD_WEBHOOK_CONTACT_URL= RESULTS_SCRAPER_URL= -RESULTS_API_KEY= \ No newline at end of file +RESULTS_API_KEY= + +SMTP_USER= +SMTP_PASSWORD= +SMTP_HOST= +SMTP_PORT= +EMAIL_FROM= + +NEXTAUTH_SECRET= diff --git a/.gitignore b/.gitignore index 7fee4b1..dc8f696 100644 --- a/.gitignore +++ b/.gitignore @@ -2,10 +2,12 @@ /.idea/ +/.yarn/ + # dependencies /node_modules /.pnp -.pnp.js +.pnp.* # pwa sw.* @@ -41,3 +43,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +certificates diff --git a/.vscode/settings.json b/.vscode/settings.json index 2af7b68..a13638a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,6 +3,8 @@ "ajouter", "approximative", "Armes", + "bgcolor", + "cellspacing", "colour", "colours", "contactez", diff --git a/package.json b/package.json index 3961d3c..be150ec 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "format:fix": "prettier --write \"**/*.{js,jsx,ts,tsx}\"" }, "dependencies": { + "@auth/mongodb-adapter": "^2.5.0", "@headlessui/react": "^1.7.17", "@headlessui/tailwindcss": "^0.2.0", "@hookform/resolvers": "^3.3.3", @@ -29,11 +30,13 @@ "leaflet.markercluster": "^1.5.3", "leaflet.smooth_marker_bouncing": "^3.0.3", "lodash": "^4.17.21", - "mongodb": "^6.1.0", + "mongodb": "^6.4.0", "next": "^14.0.4", + "next-auth": "^5.0.0-beta.16", "next-intl": "^3.3.2", "next-pwa": "^5.6.0", "next-safe-action": "^6.2.0", + "nodemailer": "^6.9.12", "postcss": "8.4.33", "react": "^18.2.0", "react-date-range": "^1.4.0", diff --git a/src/app/[locale]/(main)/auth/sign-in/page.tsx b/src/app/[locale]/(main)/auth/sign-in/page.tsx new file mode 100644 index 0000000..a09e4e7 --- /dev/null +++ b/src/app/[locale]/(main)/auth/sign-in/page.tsx @@ -0,0 +1,104 @@ +"use client"; + +import { useState } from "react"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { signIn } from "next-auth/react"; +import { useLocale, useTranslations } from "next-intl"; +import { FormProvider, useForm } from "react-hook-form"; +import { z } from "zod"; + +import { clearMessage } from "@/components/InfoMessage"; +import InfoMessage from "@/components/InfoMessage"; +import { TextField } from "@/components/form/TextField"; + +const signInSchema = z.object({ + email: z.string().email(), +}); + +type SignInFormValues = z.infer; + +export default function SignIn() { + const t = useTranslations("SignIn"); + const locale = useLocale(); + + const [responseMessage, setResponseMessage] = useState({ + isSuccessful: false, + message: "", + }); + + const form = useForm({ + resolver: zodResolver(signInSchema), + }); + + const onSubmit = async ({ email }: SignInFormValues) => { + try { + const result = await signIn("nodemailer", { + email, + redirect: false, + callbackUrl: `/${locale}/tournaments`, + }); + + if (result?.error) { + throw new Error(result.error); + } else if (result?.ok) { + setResponseMessage({ + isSuccessful: true, + message: t("checkEmail"), + }); + + form.reset(); + } + } catch (err: unknown) { + console.log(err); + + setResponseMessage({ + isSuccessful: true, + message: t("failure"), + }); + + clearMessage(setResponseMessage); + } + }; + + return ( +
+
+

+ {t("title")} +

+

+ {t("info")} +

+ + +
+ + + + + + +
+
+
+ ); +} diff --git a/src/app/[locale]/(main)/components/Navbar.tsx b/src/app/[locale]/(main)/components/Navbar.tsx index a8f363a..7e8a82f 100644 --- a/src/app/[locale]/(main)/components/Navbar.tsx +++ b/src/app/[locale]/(main)/components/Navbar.tsx @@ -1,5 +1,6 @@ import { useTranslations } from "next-intl"; +import AuthButton from "@/components/AuthButton"; import { Link } from "@/utils/navigation"; import Hamburger from "./Hamburger"; @@ -47,6 +48,8 @@ export default function Navbar() { ))} + + diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx index 25f1290..df4caba 100644 --- a/src/app/[locale]/layout.tsx +++ b/src/app/[locale]/layout.tsx @@ -10,7 +10,6 @@ import "@/css/globals.css"; import Providers from "@/providers"; import Footer from "./(main)/components/Footer"; -import Navbar from "./(main)/components/Navbar"; const inter = Inter({ subsets: ["latin"], variable: "--font-inter" }); const title = Julius_Sans_One({ @@ -77,10 +76,11 @@ export default async function RootLayout({