From 104d42abae741ed7bee64a8034181e4b1921984d Mon Sep 17 00:00:00 2001 From: Ali Osman Date: Sat, 29 Jul 2023 21:07:39 +0300 Subject: [PATCH] feat: update authentication for wordigo backend implementation --- .vscode/settings.json | 2 +- apps/extension/src/background/index.ts | 2 +- .../src/background/messages/getToken.ts | 17 ++-- apps/extension/src/contents/translate.tsx | 14 ++- .../translate/components/TranslatePopup.tsx | 16 +++- apps/next/package.json | 1 + .../components/Auth/SocialProviders/index.tsx | 11 +-- .../Layout/Dashboard/Header/MainNav.tsx | 1 - .../Layout/MainLayout/Header/index.tsx | 6 +- .../components/Layout/NavProfile/index.tsx | 15 +-- apps/next/src/env.mjs | 39 +++++--- apps/next/src/hooks/useAuthStore.ts | 4 +- apps/next/src/middleware.ts | 36 ++++---- apps/next/src/pages/_app.tsx | 15 +-- apps/next/src/pages/api/auth/[...nextauth].ts | 92 +++++++++++++++++++ apps/next/src/pages/auth/signin/index.tsx | 18 ++++ .../src/pages/auth/signin/signin-form.tsx | 39 +++++--- apps/next/src/pages/auth/signup/index.tsx | 18 ++++ apps/next/src/utils/getUrl.ts | 13 +-- apps/next/tsconfig.json | 1 + apps/next/types/next-auth.d.ts | 26 ++++++ packages/config/eslint/index.js | 2 + turbo.json | 6 +- yarn.lock | 74 ++++++++++++++- 24 files changed, 363 insertions(+), 105 deletions(-) create mode 100644 apps/next/src/pages/api/auth/[...nextauth].ts create mode 100644 apps/next/types/next-auth.d.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 57294807..00462f16 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.codeActionsOnSave": { "source.fixAll.eslint": true, - "source.organizeImports": true + "source.organizeImports": false }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, diff --git a/apps/extension/src/background/index.ts b/apps/extension/src/background/index.ts index 744b57fc..6320fc57 100644 --- a/apps/extension/src/background/index.ts +++ b/apps/extension/src/background/index.ts @@ -1,6 +1,6 @@ chrome.runtime.onInstalled.addListener(({ reason }) => { - openWelcomePage() if (reason !== chrome.runtime.OnInstalledReason.INSTALL) { + // openWelcomePage() return } }) diff --git a/apps/extension/src/background/messages/getToken.ts b/apps/extension/src/background/messages/getToken.ts index 8729c662..5cda7b84 100644 --- a/apps/extension/src/background/messages/getToken.ts +++ b/apps/extension/src/background/messages/getToken.ts @@ -1,24 +1,19 @@ import type { PlasmoMessaging } from "@plasmohq/messaging" -export type RequestBody = { -} +export type RequestBody = {} export type RequestResponse = string | null -const handler: PlasmoMessaging.MessageHandler< - RequestBody, - RequestResponse -> = async (req, res) => { - +const handler: PlasmoMessaging.MessageHandler = async (req, res) => { try { const token = JSON.parse( decodeURIComponent( ( await chrome.cookies.get({ - name: 'supabase-auth-token', - url: 'http://localhost:3000', + name: "token", + url: "https://wordigo.app" }) - )?.value || '' + )?.value || "" ) )[0] res.send(token as string) @@ -27,4 +22,4 @@ const handler: PlasmoMessaging.MessageHandler< } } -export default handler \ No newline at end of file +export default handler diff --git a/apps/extension/src/contents/translate.tsx b/apps/extension/src/contents/translate.tsx index a9b33832..3a612b91 100644 --- a/apps/extension/src/contents/translate.tsx +++ b/apps/extension/src/contents/translate.tsx @@ -6,6 +6,7 @@ import "~/styles/globals.css" import styleText from "data-text:~/styles/globals.css" import type { PlasmoCSConfig } from "plasmo" import { Fragment } from "react" +import { QueryClient, QueryClientProvider } from "react-query" import TranslatePopup from "~features/translate/components/TranslatePopup" import Provider from "~providers" @@ -36,13 +37,16 @@ const Translate = () => { Translate.Layout = () => { const popover = usePopover({}) + const client = new QueryClient() return ( - - - - - + + + + + + + ) } diff --git a/apps/extension/src/features/translate/components/TranslatePopup.tsx b/apps/extension/src/features/translate/components/TranslatePopup.tsx index df98168b..ff68fa51 100644 --- a/apps/extension/src/features/translate/components/TranslatePopup.tsx +++ b/apps/extension/src/features/translate/components/TranslatePopup.tsx @@ -17,20 +17,31 @@ import { motion } from "framer-motion" import { ArrowRightLeft, Copy, Settings, Volume2 } from "lucide-react" import { useEffect, useMemo } from "react" import ReactCountryFlag from "react-country-flag" +import { useMutation } from "react-query" import { sendToBackground } from "@plasmohq/messaging" import DictionarySelector from "~features/translate/components/DictionarySelector" -import trpc from "~libs/trpc" import { TRANSLATE_CARD_WIDTH } from "~utils/constants" import { useContextPopover } from "../context/popover" +const postTodo = async (params: any): Promise => { + const response = await fetch("https://api.wordigo.app/api/v1/translation/translate", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(params) + }) + return await response.json() +} + const TranslatePopup = () => { const toast = useToast() const { cordinate, selectedText, setPopup, targetLanguage } = useContextPopover() - const { mutate: handleTranslate, isLoading, data } = trpc.translation.translate.useMutation({}) + const { mutate: handleTranslate, isLoading, data } = useMutation(postTodo, {}) const getSourceLanguageFlag = useMemo( () => AllCountryLanguages.find((lang) => lang.code === (data?.sourceLanguage || "").toUpperCase()), @@ -43,6 +54,7 @@ const TranslatePopup = () => { ) useEffect(() => { + // @ts-ignore handleTranslate({ query: selectedText, sourceLanguage: null, targetLanguage }) }, [selectedText]) diff --git a/apps/next/package.json b/apps/next/package.json index eda9e528..dcc12d84 100755 --- a/apps/next/package.json +++ b/apps/next/package.json @@ -36,6 +36,7 @@ "i18next": "^23.2.7", "lucide-react": "^0.144.0", "next": "^13.3.0", + "next-auth": "^4.22.3", "next-i18next": "^14.0.0", "next-seo": "^6.1.0", "next-themes": "^0.2.1", diff --git a/apps/next/src/components/Auth/SocialProviders/index.tsx b/apps/next/src/components/Auth/SocialProviders/index.tsx index 075ef4f4..49e9e52a 100644 --- a/apps/next/src/components/Auth/SocialProviders/index.tsx +++ b/apps/next/src/components/Auth/SocialProviders/index.tsx @@ -1,18 +1,11 @@ import getUrl from "@/utils/getUrl"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; +import { signIn } from "next-auth/react"; import { Button } from "@wordigo/ui"; const SocialProviders = () => { - const supabase = useSupabaseClient(); - const signInWithGoogle = async () => { - const result = await supabase.auth.signInWithOAuth({ - provider: "google", - options: { - redirectTo: getUrl(), - }, - }); + await signIn("google", { callbackUrl: getUrl() }); }; return ( diff --git a/apps/next/src/components/Layout/Dashboard/Header/MainNav.tsx b/apps/next/src/components/Layout/Dashboard/Header/MainNav.tsx index 629626d1..5a274227 100644 --- a/apps/next/src/components/Layout/Dashboard/Header/MainNav.tsx +++ b/apps/next/src/components/Layout/Dashboard/Header/MainNav.tsx @@ -1,6 +1,5 @@ import { type FunctionComponent } from "react"; import dynamic from "next/dynamic"; -import Link from "next/link"; import { useRouter } from "next/router"; import NavProfile from "@/components/Layout/NavProfile"; import useCommonStore from "@/stores/Common"; diff --git a/apps/next/src/components/Layout/MainLayout/Header/index.tsx b/apps/next/src/components/Layout/MainLayout/Header/index.tsx index 88198476..c2eefa1f 100644 --- a/apps/next/src/components/Layout/MainLayout/Header/index.tsx +++ b/apps/next/src/components/Layout/MainLayout/Header/index.tsx @@ -1,6 +1,7 @@ import Image from "next/image"; import Link from "next/link"; import { useAuthStore } from "@/hooks/useAuthStore"; +import { useSession } from "next-auth/react"; import { useTranslation } from "next-i18next"; import { buttonVariants } from "@wordigo/ui"; @@ -14,6 +15,7 @@ import Navigation from "./Navigation"; export default function HomeHeader() { const { t } = useTranslation(); const { isLoggedIn, userLoading } = useAuthStore(); + const { data, status } = useSession(); return (
@@ -28,9 +30,9 @@ export default function HomeHeader() {
- {userLoading ? ( + {status === "loading" ? ( - ) : isLoggedIn ? ( + ) : status === "authenticated" ? ( ) : ( diff --git a/apps/next/src/components/Layout/NavProfile/index.tsx b/apps/next/src/components/Layout/NavProfile/index.tsx index 97656978..1912452c 100644 --- a/apps/next/src/components/Layout/NavProfile/index.tsx +++ b/apps/next/src/components/Layout/NavProfile/index.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { useAuthStore } from "@/hooks/useAuthStore"; +import { signOut, useSession } from "next-auth/react"; import { Avatar, @@ -17,17 +18,17 @@ import { } from "@wordigo/ui"; const NavProfile = () => { - const { user, logout } = useAuthStore(); + const { data } = useSession(); - const splittedText = user?.user_metadata.name?.split(" ") || ([] as string[]); - const computedName = splittedText?.[0]?.[0] + splittedText?.[1]?.[0]; + const splittedText = data?.user?.name?.toUpperCase()?.split(" "); + const computedName = (splittedText?.[0]?.[0] || "") + (splittedText?.[1]?.[0] || ""); return ( @@ -35,8 +36,8 @@ const NavProfile = () => {
-

{user?.user_metadata.full_name}

-

{user?.user_metadata.email}

+

{data?.user.name}

+

{data?.user.email}

@@ -48,7 +49,7 @@ const NavProfile = () => { Settings - + signOut()}> Log out
diff --git a/apps/next/src/env.mjs b/apps/next/src/env.mjs index 68b4f72e..43d9da1d 100755 --- a/apps/next/src/env.mjs +++ b/apps/next/src/env.mjs @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { z } from "zod" /** * Specify your server-side environment variables schema here. This way you can ensure the app isn't @@ -7,7 +7,7 @@ import { z } from "zod"; const server = z.object({ DATABASE_URL: z.string().url(), NODE_ENV: z.enum(["development", "test", "production"]), -}); +}) /** * Specify your client-side environment variables schema here. This way you can ensure the app isn't @@ -16,7 +16,11 @@ const server = z.object({ const client = z.object({ NEXT_PUBLIC_SUPABASE_URL: z.string().min(1), NEXT_PUBLIC_ANON_KEY: z.string().min(1), -}); + GOOGLE_OAUTH2_ID: z.string().min(1), + GOOGLE_OAUTH2_SECRET: z.string().min(1), + NEXTAUTH_SECRET: z.string().min(1), + WORDIG_BACKEND_URL: z.string().min(1), +}) /** * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. @@ -29,39 +33,43 @@ const processEnv = { NODE_ENV: process.env.NODE_ENV, NEXT_PUBLIC_SUPABASE_URL: process.env.NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_ANON_KEY: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, -}; + GOOGLE_OAUTH2_ID: process.env.GOOGLE_OAUTH2_SECRET, + GOOGLE_OAUTH2_SECRET: process.env.GOOGLE_OAUTH2_SECRET, + NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, + WORDIG_BACKEND_URL: process.env.WORDIG_BACKEND_URL +} // Don't touch the part below // -------------------------- -const merged = server.merge(client); +const merged = server.merge(client) /** @typedef {z.input} MergedInput */ /** @typedef {z.infer} MergedOutput */ /** @typedef {z.SafeParseReturnType} MergedSafeParseReturn */ -let env = /** @type {MergedOutput} */ (process.env); +let env = /** @type {MergedOutput} */ (process.env) if (!!process.env.SKIP_ENV_VALIDATION == false) { - const isServer = typeof window === "undefined"; + const isServer = typeof window === "undefined" const parsed = /** @type {MergedSafeParseReturn} */ ( isServer ? merged.safeParse(processEnv) // on server we can validate all env vars : client.safeParse(processEnv) // on client we can only validate the ones that are exposed - ); + ) if (parsed.success === false) { console.error( "❌ Invalid environment variables:", parsed.error.flatten().fieldErrors, - ); - throw new Error("Invalid environment variables"); + ) + throw new Error("Invalid environment variables") } env = new Proxy(parsed.data, { get(target, prop) { - if (typeof prop !== "string") return undefined; + if (typeof prop !== "string") return undefined // Throw a descriptive error if a server-side env var is accessed on the client // Otherwise it would just be returning `undefined` and be annoying to debug if (!isServer && !prop.startsWith("NEXT_PUBLIC_")) @@ -69,10 +77,11 @@ if (!!process.env.SKIP_ENV_VALIDATION == false) { process.env.NODE_ENV === "production" ? "❌ Attempted to access a server-side environment variable on the client" : `❌ Attempted to access server-side environment variable '${prop}' on the client`, - ); - return target[/** @type {keyof typeof target} */ (prop)]; + ) + return target[/** @type {keyof typeof target} */ (prop)] }, - }); + }) } -export { env }; +export { env } + diff --git a/apps/next/src/hooks/useAuthStore.ts b/apps/next/src/hooks/useAuthStore.ts index 848aed1b..2713db66 100644 --- a/apps/next/src/hooks/useAuthStore.ts +++ b/apps/next/src/hooks/useAuthStore.ts @@ -36,9 +36,9 @@ export const useAuth = (): IAuthStore => { const [isLoggedIn, setIsLoggedIn] = useState(false); const getUserMe = async () => { - const { data } = await supabase.auth.getSession(); + const data = null as any; - if (data.session) { + if (data?.session) { setUserLoading(true); const { data: { user }, diff --git a/apps/next/src/middleware.ts b/apps/next/src/middleware.ts index 5ea9a905..0654d061 100644 --- a/apps/next/src/middleware.ts +++ b/apps/next/src/middleware.ts @@ -1,26 +1,24 @@ -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; -import { createServerSupabaseClient } from "@supabase/auth-helpers-nextjs"; +export { default } from "next-auth/middleware"; -import { decodeToken } from "./utils/decodeToken"; +// export async function middleware(request: NextRequest, response: NextResponse) { +// // try { +// // const supabase = createServerSupabaseClient({ req: request as any, res: response as any }); +// // const token = request.cookies.get("supabase-auth-token"); -export async function middleware(request: NextRequest, response: NextResponse) { - try { - const supabase = createServerSupabaseClient({ req: request as any, res: response as any }); - const token = request.cookies.get("supabase-auth-token"); +// // const decodedToken = await decodeToken(token?.value as string); - const decodedToken = await decodeToken(token?.value as string); +// // const user = await supabase.auth.getUser(decodedToken as string); - const user = await supabase.auth.getUser(decodedToken as string); - - if (!user.error) { - const response = NextResponse.next(); - return response; - } else throw Error; - } catch (err) { - return NextResponse.redirect(new URL("/auth/signin", request.url)); - } -} +// // if (!user.error) { +// // const response = NextResponse.next(); +// // return response; +// // } else throw Error; +// // } catch (err) { +// // return NextResponse.redirect(new URL("/auth/signin", request.url)); +// // } +// const test = NextResponse.next(); +// return test; +// } export const config = { matcher: ["/dashboard", "/:lang/dashboard"], diff --git a/apps/next/src/pages/_app.tsx b/apps/next/src/pages/_app.tsx index e21e613b..05c32870 100755 --- a/apps/next/src/pages/_app.tsx +++ b/apps/next/src/pages/_app.tsx @@ -1,21 +1,22 @@ import AppProviders from "@/components/providers"; import seo from "@/constants/seo"; import { api } from "@/libs/trpc"; -import { type Session, createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; -import { SessionContextProvider } from "@supabase/auth-helpers-react"; +import { createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; +// import { type Session, createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; import { Analytics } from "@vercel/analytics/react"; import "@wordigo/ui/styles/globals.css"; import { Fragment, useState } from "react"; import type { AppProps } from "next/app"; import Head from "next/head"; +import { SessionProvider } from "next-auth/react"; import { appWithTranslation } from "next-i18next"; import { DefaultSeo } from "next-seo"; -import "../styles/styles.css"; import "../styles/globals.css"; +import "../styles/styles.css"; -const App = ({ Component, pageProps }: AppProps<{ initialSession: Session | null }>) => { +const App = ({ Component, pageProps }: AppProps<{ session: any }>) => { const [supabase] = useState(() => createBrowserSupabaseClient()); return ( @@ -24,11 +25,13 @@ const App = ({ Component, pageProps }: AppProps<{ initialSession: Session | null - + {/* */} + - + + {/* */} ); diff --git a/apps/next/src/pages/api/auth/[...nextauth].ts b/apps/next/src/pages/api/auth/[...nextauth].ts new file mode 100644 index 00000000..70686e48 --- /dev/null +++ b/apps/next/src/pages/api/auth/[...nextauth].ts @@ -0,0 +1,92 @@ +import { env } from "@/env.mjs"; +import { type AuthLoginValues } from "@/pages/auth/signin/signin-form"; +import NextAuth, { type NextAuthOptions, type User } from "next-auth"; +import CredentialsProvider from "next-auth/providers/credentials"; +import GoogleProvider from "next-auth/providers/google"; + +export const authOptions: NextAuthOptions = { + callbacks: { + redirect({ baseUrl, url }) { + // Burada istediğiniz özel callback URL'i oluşturun + return url ?? "/"; // Varsayılan olarak, URL belirtilmemişse anasayfaya yönlendirir. + }, + async signIn({ user, account }) { + if (account?.provider === "google") { + const request = await fetch(`${env.WORDIG_BACKEND_URL}/auth/googleAuth?accessToken=${account.access_token}`); + + const profile = await request.json(); + + // @ts-ignore + user.accessToken = profile.data; + return true; + } + + if (account.provider === "credentials") { + // @ts-ignore + user.accessToken = user?.token; + return true; + } + + return false; + }, + jwt({ token, user }) { + return { ...token, ...user }; + }, + async session({ session, token }) { + const request = await fetch(`${env.WORDIG_BACKEND_URL}/users/getUserMe`, { + headers: { + authorization: "Bearer " + token.accessToken, + }, + }); + + const profile = await request.json(); + + session.user = profile.data; + + return session; + }, + }, + providers: [ + GoogleProvider({ + clientId: process.env.GOOGLE_OAUTH2_ID, + clientSecret: process.env.GOOGLE_OAUTH2_SECRET, + }), + CredentialsProvider({ + name: "credentials", + credentials: { + email: { label: "Email", type: "text" }, + password: { label: "Password", type: "password" }, + }, + authorize: async (credentials) => { + if (!credentials) throw new Error("no credentials"); + + try { + const { email, password } = credentials as AuthLoginValues; + + const request = await fetch(`${env.WORDIG_BACKEND_URL}/auth/signIn`, { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + method: "POST", + body: JSON.stringify({ email, password }), + }); + const response = await request.json(); + + if (response?.data?.user) { + return { user: response.data.user as User, token: response.data.accessToken } as any; + } else { + throw new Error(response.message as string); + } + } catch (err) { + throw new Error(err.message as string); + } + }, + }), + ], + pages: { + signIn: "/auth/signin", + }, + secret: process.env.NEXTAUTH_SECRET, +}; +export default NextAuth(authOptions); diff --git a/apps/next/src/pages/auth/signin/index.tsx b/apps/next/src/pages/auth/signin/index.tsx index 3dddaf9c..8a6cbde4 100644 --- a/apps/next/src/pages/auth/signin/index.tsx +++ b/apps/next/src/pages/auth/signin/index.tsx @@ -1,8 +1,26 @@ import AuthLayout from "@/components/Auth/Layout/AuthLayout"; import SocialProviders from "@/components/Auth/SocialProviders"; +import { getSession } from "next-auth/react"; import AuthSıgnInForm from "./signin-form"; +export async function getServerSideProps(context) { + const session = await getSession(context); + + if (session) { + return { + redirect: { + destination: "/dashboard", + permanent: false, + }, + }; + } + + return { + props: { session }, + }; +} + const SıgnIn = () => { return ( diff --git a/apps/next/src/pages/auth/signin/signin-form.tsx b/apps/next/src/pages/auth/signin/signin-form.tsx index 65c93d45..09370798 100644 --- a/apps/next/src/pages/auth/signin/signin-form.tsx +++ b/apps/next/src/pages/auth/signin/signin-form.tsx @@ -1,8 +1,8 @@ -import React from "react"; +import React, { useState } from "react"; import { useRouter } from "next/router"; import CButton from "@/components/UI/Button"; -import { useAuthStore } from "@/hooks/useAuthStore"; import { zodResolver } from "@hookform/resolvers/zod"; +import { signIn } from "next-auth/react"; import { useForm } from "react-hook-form"; import z from "zod"; @@ -18,16 +18,16 @@ const AuthLoginSchema = z.object({ password: z.string().min(6, { message: "Password must be atleast 6 characters" }), }); // extracting the type -type AuthLoginValues = z.infer; +export type AuthLoginValues = z.infer; const AuthLoginForm = ({ className, ...props }: UserAuthFormProps) => { const { toast } = useToast(); const router = useRouter(); - const authStore = useAuthStore(); + const [isLoading, setIsLoading] = useState(false); const defaultValues: Partial = { - email: "", - password: "", + email: "osmandlsmn34@gmail.com", + password: "deneme", }; const form = useForm({ @@ -36,24 +36,35 @@ const AuthLoginForm = ({ className, ...props }: UserAuthFormProps) => { }); const handleSubmit = async (values: AuthLoginValues) => { - const { user, error } = await authStore.signIn(values.email, values.password); + setIsLoading(true); + const { error } = await signIn("credentials", { email: values.email, password: values.password, redirect: false }); + setIsLoading(false); + if (error) { toast({ + variant: "destructive", title: "Warning", - description: error.message, + description: error, }); } else { toast({ title: "Success", - description: "sign up successful you are redirected to homepage.", + description: "Sign in successful you are redirected to homepage.", }); - await router.push("/"); + + const callbackUrl = router.query.callbackUrl; + + if (typeof callbackUrl === "string") { + void router.push(callbackUrl); + } else { + void router.push("/"); + } } }; return (
-
+
@@ -61,7 +72,7 @@ const AuthLoginForm = ({ className, ...props }: UserAuthFormProps) => { Email ( @@ -78,7 +89,7 @@ const AuthLoginForm = ({ className, ...props }: UserAuthFormProps) => { Password ( @@ -91,7 +102,7 @@ const AuthLoginForm = ({ className, ...props }: UserAuthFormProps) => { />
- + Sign in diff --git a/apps/next/src/pages/auth/signup/index.tsx b/apps/next/src/pages/auth/signup/index.tsx index 552d54aa..1ebfdcff 100644 --- a/apps/next/src/pages/auth/signup/index.tsx +++ b/apps/next/src/pages/auth/signup/index.tsx @@ -1,8 +1,26 @@ import AuthLayout from "@/components/Auth/Layout/AuthLayout"; import SocialProviders from "@/components/Auth/SocialProviders"; +import { getSession } from "next-auth/react"; import AuthSıgnupForm from "./signup-form"; +export async function getServerSideProps(context) { + const session = await getSession(context); + + if (session) { + return { + redirect: { + destination: "/dashboard", + permanent: false, + }, + }; + } + + return { + props: { session }, + }; +} + const SignUp = () => { return ( diff --git a/apps/next/src/utils/getUrl.ts b/apps/next/src/utils/getUrl.ts index 81b44daf..1304f8ed 100644 --- a/apps/next/src/utils/getUrl.ts +++ b/apps/next/src/utils/getUrl.ts @@ -1,9 +1,6 @@ export default function () { - let url = - process?.env?.NEXT_PUBLIC_SITE_URL ?? - process?.env?.NEXT_PUBLIC_VERCEL_URL ?? - "http://localhost:3000/" - url = url.includes("http") ? url : `https://${url}` - url = url.charAt(url.length - 1) === "/" ? url : `${url}/` - return url -} \ No newline at end of file + let url = process?.env?.NEXT_PUBLIC_SITE_URL ?? process?.env?.NEXT_PUBLIC_VERCEL_URL ?? "http://localhost:3000/"; + url = url.includes("http") ? url : `https://${url}`; + url = url.charAt(url.length - 1) === "/" ? url : `${url}/`; + return url; +} diff --git a/apps/next/tsconfig.json b/apps/next/tsconfig.json index afa8e551..318acb6f 100755 --- a/apps/next/tsconfig.json +++ b/apps/next/tsconfig.json @@ -2,6 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "baseUrl": ".", + "strict": false, "paths": { "@/*": [ "./src/*" diff --git a/apps/next/types/next-auth.d.ts b/apps/next/types/next-auth.d.ts new file mode 100644 index 00000000..b2a29bb3 --- /dev/null +++ b/apps/next/types/next-auth.d.ts @@ -0,0 +1,26 @@ +import NextAuth from "next-auth"; + +export interface LoggedInUser { + avatar_url: string; + createdDate: string; + email: string; + id: string; + name: string; + nativeLanguage: string; + passwordHash: string; + passwordSalt: string; + provider: "local" | "google"; + surname: string; + targetLanguage: string; + updatedDate: string; + username: string; +} + +declare module "next-auth" { + /** + * Returned by `useSession`, `getSession` and received as a prop on the `SessionProvider` React Context + */ + interface Session { + user: LoggedInUser; + } +} diff --git a/packages/config/eslint/index.js b/packages/config/eslint/index.js index 30a538a4..a27fa23d 100644 --- a/packages/config/eslint/index.js +++ b/packages/config/eslint/index.js @@ -8,6 +8,8 @@ const config = { "prettier", ], rules: { + "no-unused-expressions": "off", + "@typescript-eslint/ban-ts-comment": "off", "react-hooks/rules-of-hooks": "off", "typescript-eslint/no-explicit-any": "off", "@next/next/no-img-element": "off", diff --git a/turbo.json b/turbo.json index 72e3b9f3..dce1df5a 100644 --- a/turbo.json +++ b/turbo.json @@ -47,6 +47,7 @@ }, "globalEnv": [ "CI", + "WORDIG_BACKEND_URL", "DATABASE_URL", "EXPO_ROUTER_APP_ROOT", "NEXT_PUBLIC_SUPABASE_URL", @@ -55,6 +56,9 @@ "SKIP_ENV_VALIDATION", "VERCEL_URL", "CLOUD_TRANSLATE_PROJECT_ID", - "CLOUD_TRANSLATE_API_KEY" + "CLOUD_TRANSLATE_API_KEY", + "GOOGLE_OAUTH2_ID", + "GOOGLE_OAUTH2_SECRET", + "NEXTAUTH_SECRET" ] } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 059b5d54..29d6065f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -825,6 +825,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@panva/hkdf@^1.0.2": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@panva/hkdf/-/hkdf-1.1.1.tgz#ab9cd8755d1976e72fc77a00f7655a64efe6cd5d" + integrity sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA== + "@parcel/bundler-default@2.9.3": version "2.9.3" resolved "https://registry.yarnpkg.com/@parcel/bundler-default/-/bundler-default-2.9.3.tgz#df18c4b8390a03f83ac6c89da302f9edf48c8fe2" @@ -4078,6 +4083,11 @@ convert-source-map@^1.7.0: resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +cookie@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + copy-anything@^2.0.1: version "2.0.6" resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" @@ -6144,7 +6154,7 @@ jju@^1.4.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== -jose@^4.14.0: +jose@^4.11.4, jose@^4.14.0, jose@^4.14.4: version "4.14.4" resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== @@ -6860,6 +6870,21 @@ needle@^3.1.0: iconv-lite "^0.6.3" sax "^1.2.4" +next-auth@^4.22.3: + version "4.22.3" + resolved "https://registry.yarnpkg.com/next-auth/-/next-auth-4.22.3.tgz#bbe5152e0e3ea6d625731f23adba4fd2e3d2d570" + integrity sha512-XAgy9xV3J2eJOXrQhmxdjV6MLM29ibm6WtMXc3KY6IPZeApf+SuBuPvlqCUfbu5YsAzlg9WSw6u01dChTfeZOA== + dependencies: + "@babel/runtime" "^7.20.13" + "@panva/hkdf" "^1.0.2" + cookie "^0.5.0" + jose "^4.11.4" + oauth "^0.9.15" + openid-client "^5.4.0" + preact "^10.6.3" + preact-render-to-string "^5.1.19" + uuid "^8.3.2" + next-i18next@^14.0.0: version "14.0.0" resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-14.0.0.tgz#fd637c81df96c9d6f2cc4716037130ac4e5d6968" @@ -7033,11 +7058,21 @@ nullthrows@1.1.1, nullthrows@^1.1.1: resolved "https://registry.yarnpkg.com/nullthrows/-/nullthrows-1.1.1.tgz#7818258843856ae971eae4208ad7d7eb19a431b1" integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw== +oauth@^0.9.15: + version "0.9.15" + resolved "https://registry.yarnpkg.com/oauth/-/oauth-0.9.15.tgz#bd1fefaf686c96b75475aed5196412ff60cfb9c1" + integrity sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA== + object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== +object-hash@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" + integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== + object-hash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" @@ -7103,6 +7138,11 @@ oblivious-set@1.0.0: resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566" integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw== +oidc-token-hash@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz#9a229f0a1ce9d4fc89bcaee5478c97a889e7b7b6" + integrity sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -7134,6 +7174,16 @@ open@^9.1.0: is-inside-container "^1.0.0" is-wsl "^2.2.0" +openid-client@^5.4.0: + version "5.4.3" + resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.3.tgz#c75d2f6d07a25d383a72c8ff34605a36b7e2cd73" + integrity sha512-sVQOvjsT/sbSfYsQI/9liWQGVZH/Pp3rrtlGEwgk/bbHfrUDZ24DN57lAagIwFtuEu+FM9Ev7r85s8S/yPjimQ== + dependencies: + jose "^4.14.4" + lru-cache "^6.0.0" + object-hash "^2.2.0" + oidc-token-hash "^5.0.3" + optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" @@ -7493,6 +7543,18 @@ posthtml@^0.16.4, posthtml@^0.16.5: posthtml-parser "^0.11.0" posthtml-render "^3.0.0" +preact-render-to-string@^5.1.19: + version "5.2.6" + resolved "https://registry.yarnpkg.com/preact-render-to-string/-/preact-render-to-string-5.2.6.tgz#0ff0c86cd118d30affb825193f18e92bd59d0604" + integrity sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw== + dependencies: + pretty-format "^3.8.0" + +preact@^10.6.3: + version "10.16.0" + resolved "https://registry.yarnpkg.com/preact/-/preact-10.16.0.tgz#68a06d70b191b8a313ea722d61e09c6b2a79a37e" + integrity sha512-XTSj3dJ4roKIC93pald6rWuB2qQJO9gO2iLLyTe87MrjQN+HklueLsmskbywEWqCHlclgz3/M4YLL2iBr9UmMA== + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -7536,6 +7598,11 @@ prettier@2.8.8, prettier@^2.8.7: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +pretty-format@^3.8.0: + version "3.8.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-3.8.0.tgz#bfbed56d5e9a776645f4b1ff7aa1a3ac4fa3c385" + integrity sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew== + prisma@^4.12.0: version "4.16.1" resolved "https://registry.yarnpkg.com/prisma/-/prisma-4.16.1.tgz#c6d723a4326138a72489098a6c39a698a670fbbf" @@ -8964,6 +9031,11 @@ utility-types@^3.10.0: resolved "https://registry.yarnpkg.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b" integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg== +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^9.0.0: version "9.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"