diff --git a/backend/models/User.js b/backend/models/User.js index 779294f..22d6acc 100644 --- a/backend/models/User.js +++ b/backend/models/User.js @@ -2,22 +2,26 @@ const mongoose = require("mongoose"); const bcrypt = require("bcryptjs"); const UserSchema = new mongoose.Schema({ - username: { - type: String, - required: true, - unique: true, - }, - email: { - type: String, - required: true, - unique: true, - }, - password: { - type: String, - required: true, - }, + username: { + type: String, + required: true, + }, + email: { + type: String, + required: true, + unique: true, + }, + password: { + type: String, + }, + googleId: { + type: String, + unique: true, + sparse: true, + }, }); + UserSchema.pre('save', async function (next) { if (!this.isModified('password')) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index e26c7a9..267c199 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -27,6 +27,31 @@ router.post("/login", passport.authenticate('local'), (req, res) => { res.status(200).json( { message: 'Login successful', user: req.user } ); }); +router.post("/google-login", async (req, res) => { + try { + const { email, name, googleId } = req.body; + + let user = await User.findOne({ email }); + + if (!user) { + user = new User({ + username: name, + email, + googleId, + }); + await user.save(); + console.log("New Google user saved:", user); + } else { + console.log("Google user found:", user); + } + + return res.json({ message: "Login successful", user }); + } catch (error) { + console.error("Google login error:", error); + return res.status(500).json({ message: "Server error" }); + } +}); + // Logout route router.get("/logout", (req, res) => { @@ -40,3 +65,4 @@ router.get("/logout", (req, res) => { }); module.exports = router; + diff --git a/package.json b/package.json index f2d89f5..96e5b0f 100644 --- a/package.json +++ b/package.json @@ -19,12 +19,15 @@ "@primer/octicons-react": "^19.15.5", "@vitejs/plugin-react": "^4.3.3", "axios": "^1.7.7", + "firebase": "^12.1.0", "framer-motion": "^12.23.12", + "i": "^0.3.7", "lucide-react": "^0.525.0", "octokit": "^4.0.2", "postcss": "^8.4.47", "react": "^18.3.1", "react-dom": "^18.3.1", + "react-firebase-hooks": "^5.1.1", "react-hot-toast": "^2.4.1", "react-icons": "^5.3.0", "react-router-dom": "^6.28.0", diff --git a/src/App.tsx b/src/App.tsx index b00eba8..8696045 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,4 @@ + import Navbar from "./components/Navbar"; import Footer from "./components/Footer"; import ScrollProgressBar from "./components/ScrollProgressBar"; diff --git a/src/components/GoogleSignIn.tsx b/src/components/GoogleSignIn.tsx new file mode 100644 index 0000000..4dabaf2 --- /dev/null +++ b/src/components/GoogleSignIn.tsx @@ -0,0 +1,60 @@ +import { signInWithGoogle } from "../hooks/firebase"; +import React, { useState } from "react"; +import axios from "axios"; +import { useNavigate } from "react-router-dom"; + +const backendUrl = import.meta.env.VITE_BACKEND_URL; + +const GoogleSignIn = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + + const handleSignIn = async () => { + setIsLoading(true); + try { + const userCredential = await signInWithGoogle(); + console.log("Google userCredential:", userCredential); + + if (userCredential && userCredential.user) { + const idToken = await userCredential.user.getIdToken(); + const { email, displayName, uid } = userCredential.user; + console.log("Google user info:", { email, displayName, uid }); + + const response = await axios.post( + `${backendUrl}/api/auth/google-login`, + { + email, + name: displayName, + googleId: uid, + } + ); + console.log("Backend response:", response.data); + + if (response.data.message === "Login successful") { + navigate("/"); + } else { + alert(response.data.message || "Google login failed"); + } + } else { + alert("No user returned from Google sign-in"); + } + } catch (error) { + console.error("Google sign-in error:", error); + alert("Google sign-in failed"); + } finally { + setIsLoading(false); + } + }; + + return ( + + ); +}; + +export default GoogleSignIn; \ No newline at end of file diff --git a/src/hooks/firebase.ts b/src/hooks/firebase.ts new file mode 100644 index 0000000..c1f1cec --- /dev/null +++ b/src/hooks/firebase.ts @@ -0,0 +1,23 @@ +import { initializeApp } from 'firebase/app'; +import { getAuth, GoogleAuthProvider, signInWithPopup, setPersistence, browserLocalPersistence } from 'firebase/auth'; + +const firebaseConfig = { + apiKey: import.meta.env.VITE_FIREBASE_API_KEY, + authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN, + projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID, + storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET, + messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID, + appId: import.meta.env.VITE_FIREBASE_APP_ID +}; + +const app = initializeApp(firebaseConfig); + +const auth = getAuth(app); +const googleAuthProvider = new GoogleAuthProvider(); + +const signInWithGoogle = async () => { + await setPersistence(auth, browserLocalPersistence); + return await signInWithPopup(auth, googleAuthProvider); +}; + +export { auth, signInWithGoogle }; \ No newline at end of file diff --git a/src/pages/Login/Login.tsx b/src/pages/Login/Login.tsx index d6f21a7..4bdebdb 100644 --- a/src/pages/Login/Login.tsx +++ b/src/pages/Login/Login.tsx @@ -3,6 +3,9 @@ import axios from "axios"; import { useNavigate, Link } from "react-router-dom"; import { ThemeContext } from "../../context/ThemeContext"; import type { ThemeContextType } from "../../context/ThemeContext"; +import GoogleSignIn from "../../components/GoogleSignIn"; + + const backendUrl = import.meta.env.VITE_BACKEND_URL; @@ -122,12 +125,23 @@ const Login: React.FC = () => { +
+
+ or +
+
+ + {/* Google Sign-In Button */} +
+ +
+ {/* Message */} {message && (
Create Account + + {/* OR separator */} +
+
+ or +
+
+ + {/* Google Sign-In Button */} +
+ +
{message && ( @@ -141,6 +155,7 @@ const navigate = useNavigate(); }`}> {message}
+ )}
diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..98b2f12 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,4 +4,8 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + server:{ + host:'0.0.0.0', + port:5173 + } })