diff --git a/backend/.env.example b/backend/.env.example index 2330c3e..9600c13 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -5,3 +5,9 @@ FRONTEND_URL=http://localhost:5173 # Add a random string here # In order to generate one, you may use this command: openssl rand -base64 32 BETTER_AUTH_SECRET= + +GITHUB_CLIENT_ID= +GITHUB_CLIENT_SECRET= + +DISCORD_CLIENT_ID= +DISCORD_CLIENT_SECRET= diff --git a/backend/README.md b/backend/README.md index 6dbaaed..215b391 100644 --- a/backend/README.md +++ b/backend/README.md @@ -48,7 +48,21 @@ brew install postgresql@18 ### Setting the environment variables -Copy the [.env.example](./.env.example) and replace the `DATABASE_URL` if needed. +Start by copying the [.env.example](./.env.example) to `.env`: + +```sh +cd backend +cp .env.example .env +``` + +Proceed to do the following: + +- Check whether the `DATABASE_URL` needs to be updated for the deployment +- Make sure the URLs for the frontend and backend are correct +- Create and insert the GitHub client ID and client secret: + - This can be done by creating an OAuth application in [GitHub's Developer settings -> OAuth Apps](https://github.com/settings/developers) +- Create and insert the Discord client ID and client secret: + - This can be done by creating an application in [Discord's Developer Portal](https://discord.com/developers/applications) ### Downloading the book dataset diff --git a/backend/src/auth/index.ts b/backend/src/auth/index.ts index fc131a8..0ed79a6 100644 --- a/backend/src/auth/index.ts +++ b/backend/src/auth/index.ts @@ -20,5 +20,15 @@ export const auth = betterAuth({ }, }, }, + socialProviders: { + github: { + clientId: process.env.GITHUB_CLIENT_ID ?? "", + clientSecret: process.env.GITHUB_CLIENT_SECRET ?? "", + }, + discord: { + clientId: process.env.DISCORD_CLIENT_ID ?? "", + clientSecret: process.env.DISCORD_CLIENT_SECRET ?? "", + }, + }, trustedOrigins: [process.env.FRONTEND_URL ?? "http://localhost:5173"], }); diff --git a/frontend/.env.example b/frontend/.env.example index 019061c..b4b7519 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1,2 +1,4 @@ +VITE_APP_URL=http://localhost:5173 + # Must be changed to http://it2810-37.idi.ntnu.no:3001/ for production VITE_API_URL = http://localhost:3001 diff --git a/frontend/README.md b/frontend/README.md index 068e082..78a3ea6 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -21,6 +21,16 @@ cd frontend pnpm i # or: pnpm install ``` +Set up environment variables: + +``` +cd frontend +cp .env.example .env +``` + +Make sure `VITE_APP_URL` matches the frontend URL. +We need this URL to use as the callback URL for Social OAuth logins - otherwise we will be redirected to the backend at port `3001`, which is not what we want. + Run the application: ```sh diff --git a/frontend/app/routes/login.tsx b/frontend/app/routes/login.tsx index 147ca44..1b0c20e 100644 --- a/frontend/app/routes/login.tsx +++ b/frontend/app/routes/login.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Link, useNavigate } from "react-router"; +import { Link, Navigate, useNavigate } from "react-router"; import { useForm } from "@tanstack/react-form"; import { z } from "zod"; import { authClient } from "~/api/auth"; @@ -21,6 +21,7 @@ import { import { Input } from "~/components/ui/Input"; import { Spinner } from "~/components/ui/Spinner"; import { toast } from "~/components/ui/Toast"; +import { SiDiscord, SiGithub } from "@icons-pack/react-simple-icons"; const formSchema = z.object({ email: z.email("Enter a valid email address."), @@ -34,6 +35,7 @@ export function meta() { export default function LoginPage() { const navigate = useNavigate(); const [serverError, setServerError] = useState(null); + const { data: session } = authClient.useSession(); const form = useForm({ defaultValues: { @@ -61,6 +63,10 @@ export default function LoginPage() { }, }); + if (session) { + return ; + } + return (
@@ -149,6 +155,30 @@ export default function LoginPage() { )} + +

Don't have an account?{" "} diff --git a/frontend/app/routes/signup.tsx b/frontend/app/routes/signup.tsx index 3103002..f09d00b 100644 --- a/frontend/app/routes/signup.tsx +++ b/frontend/app/routes/signup.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Link, useNavigate } from "react-router"; +import { Link, Navigate, useNavigate } from "react-router"; import { useForm } from "@tanstack/react-form"; import { z } from "zod"; import { authClient } from "~/api/auth"; @@ -22,6 +22,7 @@ import { import { Input } from "~/components/ui/Input"; import { Spinner } from "~/components/ui/Spinner"; import { toast } from "~/components/ui/Toast"; +import { SiDiscord, SiGithub } from "@icons-pack/react-simple-icons"; // Matches better-auth's default minPasswordLength. const MIN_PASSWORD_LENGTH = 8; @@ -45,6 +46,7 @@ export function meta() { export default function SignupPage() { const navigate = useNavigate(); const [serverError, setServerError] = useState(null); + const { data: session } = authClient.useSession(); const form = useForm({ defaultValues: { @@ -80,6 +82,10 @@ export default function SignupPage() { }, }); + if (session) { + return ; + } + return (

@@ -230,6 +236,30 @@ export default function SignupPage() { )} + +

Already have an account?{" "} diff --git a/frontend/package.json b/frontend/package.json index 4cdefdb..b00cd69 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -22,6 +22,7 @@ "@apollo/client": "^4.3.5", "@base-ui/react": "^1.9.0", "@fontsource-variable/inter": "^5.3.0", + "@icons-pack/react-simple-icons": "^13.15.1", "@react-router/node": "^8.5.0", "@react-router/serve": "^8.5.0", "@tailwindcss/vite": "^4.3.3", diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 186bbfb..6073792 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@fontsource-variable/inter': specifier: ^5.3.0 version: 5.3.0 + '@icons-pack/react-simple-icons': + specifier: ^13.15.1 + version: 13.15.1(react@19.3.0) '@react-router/node': specifier: ^8.5.0 version: 8.5.0(react-router@8.5.0(@types/react-dom@19.3.0(@types/react@19.3.0))(@types/react@19.3.0)(react-dom@19.3.1(react@19.3.1))(react@19.3.1))(typescript@6.0.3) @@ -892,6 +895,11 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} + '@icons-pack/react-simple-icons@13.15.1': + resolution: {integrity: sha512-S35E7vJlJzMeoqjwwI7ZmQUJ2W+2KspnB6bc49KdoDGwp5mCgAy9L3sscfWMewQSMgBdPU8KWWwpSGq5DJyAFw==} + peerDependencies: + react: ^16.13 || ^17 || ^18 || ^19 + '@inquirer/ansi@2.0.9': resolution: {integrity: sha512-0Ywt3S8tqM6TWUcsRlBFQ1x+X3/o8kktX0ZiKAnByYb9GFDknnOhse1PVtfiD2BXPI8ar+BWx9PV9R0lQIcygw==} engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} @@ -4981,6 +4989,10 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} + '@icons-pack/react-simple-icons@13.15.1(react@19.3.0)': + dependencies: + react: 19.3.0 + '@inquirer/ansi@2.0.9': {} '@inquirer/checkbox@5.2.7(@types/node@24.19.2)':