integrating firebase
This commit is contained in:
parent
166324c2b5
commit
c93f5f24e3
2
.vscode/settings.json
vendored
Normal file
2
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
{
|
||||||
|
}
|
||||||
57
app/firebase/components/GoogleSignInButton.tsx
Normal file
57
app/firebase/components/GoogleSignInButton.tsx
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
'use client';
|
||||||
|
import React from 'react';
|
||||||
|
import { getAuth, GoogleAuthProvider, signInWithPopup } from 'firebase/auth';
|
||||||
|
import { auth } from '@/app/firebase/config';
|
||||||
|
interface GoogleSignInButtonProps {
|
||||||
|
onLoginSuccess?: (user: any) => void; // Callback function when login succeeds
|
||||||
|
}
|
||||||
|
|
||||||
|
const GoogleSignInButton: React.FC<GoogleSignInButtonProps> = ({ onLoginSuccess }) => {
|
||||||
|
const provider = new GoogleAuthProvider();
|
||||||
|
|
||||||
|
const handleGoogleSignIn = async () => {
|
||||||
|
try {
|
||||||
|
const result = await signInWithPopup(auth, provider);
|
||||||
|
const user = result.user;
|
||||||
|
console.log('Google Sign-In successful:', user);
|
||||||
|
if(onLoginSuccess){
|
||||||
|
onLoginSuccess(user);
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Error during Google Sign-In:', error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
onClick={handleGoogleSignIn}
|
||||||
|
className="w-full py-3 bg-blue-600 text-white rounded-md hover:bg-green-500 transition duration-300 flex items-center justify-center gap-2"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="0 0 48 48"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="#EA4335"
|
||||||
|
d="M24 9.5c3.6 0 6.6 1.3 9 3.9l6.7-6.7C35.2 2.8 30.1 0 24 0 14.9 0 7.2 5.5 3.5 13.4l7.9 6.2C13.4 11.1 18.2 9.5 24 9.5z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#34A853"
|
||||||
|
d="M24 48c6.5 0 12-2.2 16-6l-7.5-6.1C30.9 38.2 27.6 39.5 24 39.5c-5.6 0-10.4-3.6-12.2-8.7l-8.1 6.3C7.2 42.7 14.6 48 24 48z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#4A90E2"
|
||||||
|
d="M48 24c0-1.6-.2-3.1-.5-4.6H24v9h13.7c-.6 3-2.2 5.5-4.5 7.3l7.5 6.1C45.6 37.4 48 31 48 24z"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fill="#FBBC05"
|
||||||
|
d="M11.8 30.8c-.6-1.7-1-3.5-1-5.5s.4-3.8 1-5.5l-8-6.3C1.8 16.2 0 20 0 24s1.8 7.8 4.7 10.7l7.1-3.9z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Sign in with Google
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GoogleSignInButton;
|
||||||
34
app/firebase/config.ts
Normal file
34
app/firebase/config.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { initializeApp} from 'firebase/app'
|
||||||
|
import { getAuth } from 'firebase/auth';
|
||||||
|
|
||||||
|
// const firebaseConfig = {
|
||||||
|
// apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY_PK,
|
||||||
|
// authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
|
||||||
|
// projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
|
||||||
|
// storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
|
||||||
|
// messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
|
||||||
|
// appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
|
||||||
|
// measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID
|
||||||
|
// };
|
||||||
|
const firebaseConfig = {
|
||||||
|
|
||||||
|
apiKey: "AIzaSyCpATgMXv9Pu3coBwljh0uv9I9ciFkBJIA",
|
||||||
|
|
||||||
|
authDomain: "sologin-55bbd.firebaseapp.com",
|
||||||
|
|
||||||
|
projectId: "sologin-55bbd",
|
||||||
|
|
||||||
|
storageBucket: "sologin-55bbd.firebasestorage.app",
|
||||||
|
|
||||||
|
messagingSenderId: "652710461284",
|
||||||
|
|
||||||
|
appId: "1:652710461284:web:cb86f9afb033a5bcf54471",
|
||||||
|
|
||||||
|
measurementId: "G-Q6H1J4E92L"
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const app = initializeApp(firebaseConfig);
|
||||||
|
const auth = getAuth(app);
|
||||||
|
|
||||||
|
export {app,auth}
|
||||||
139
app/login/page.tsx
Normal file
139
app/login/page.tsx
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
'use client';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { auth } from '@/app/firebase/config';
|
||||||
|
import { signInWithEmailAndPassword, createUserWithEmailAndPassword } from 'firebase/auth';
|
||||||
|
import GoogleSignInButton from '../firebase/components/GoogleSignInButton';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { LoginSologin, RegisterSologin } from '../sologin';
|
||||||
|
|
||||||
|
const AuthTabs = () => {
|
||||||
|
const [activeTab, setActiveTab] = useState<'signin' | 'signup'>('signin');
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const onAuthSuccess = () => {
|
||||||
|
router.push('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAuth = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
if (activeTab === 'signin') {
|
||||||
|
const userCredential = await signInWithEmailAndPassword(auth, email, password);
|
||||||
|
console.log('User signed in:', userCredential.user);
|
||||||
|
await LoginSologin(userCredential.user);
|
||||||
|
} else {
|
||||||
|
const userCredential = await createUserWithEmailAndPassword(auth, email, password);
|
||||||
|
console.log('User signed up:', userCredential.user);
|
||||||
|
await RegisterSologin(userCredential.user);
|
||||||
|
}
|
||||||
|
onAuthSuccess();
|
||||||
|
setEmail('');
|
||||||
|
setPassword('');
|
||||||
|
setError(null);
|
||||||
|
} catch (err: any) {
|
||||||
|
const errorCode = err.code;
|
||||||
|
switch (errorCode) {
|
||||||
|
case 'auth/user-not-found':
|
||||||
|
setError('User not found. Please check your email.');
|
||||||
|
break;
|
||||||
|
case 'auth/email-already-in-use':
|
||||||
|
setError('This email is already registered. Try signing in.');
|
||||||
|
break;
|
||||||
|
case 'auth/wrong-password':
|
||||||
|
setError('Incorrect email or password.');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setError('An unexpected error occurred. Please try again.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
|
||||||
|
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full sm:w-96">
|
||||||
|
<div className="flex border-b border-gray-600 mb-6">
|
||||||
|
<button
|
||||||
|
className={`flex-1 py-2 text-center ${
|
||||||
|
activeTab === 'signin'
|
||||||
|
? 'text-white border-b-2 border-blue-500'
|
||||||
|
: 'text-gray-400 hover:text-white'
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab('signin')}
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={`flex-1 py-2 text-center ${
|
||||||
|
activeTab === 'signup'
|
||||||
|
? 'text-white border-b-2 border-blue-500'
|
||||||
|
: 'text-gray-400 hover:text-white'
|
||||||
|
}`}
|
||||||
|
onClick={() => setActiveTab('signup')}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<form onSubmit={handleAuth}>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="email">
|
||||||
|
Email Address
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="password">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full py-3 bg-blue-600 text-white rounded-md hover:bg-blue-500 transition duration-300"
|
||||||
|
>
|
||||||
|
{activeTab === 'signin' ? 'Sign In' : 'Sign Up'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div className='h-10'></div>
|
||||||
|
|
||||||
|
<GoogleSignInButton onLoginSuccess={onAuthSuccess}></GoogleSignInButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Error Dialog */}
|
||||||
|
{error && (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 className="text-xl font-bold text-white mb-4">Error</h3>
|
||||||
|
<p className="text-gray-300">{error}</p>
|
||||||
|
<button
|
||||||
|
className="mt-4 px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-500"
|
||||||
|
onClick={() => setError(null)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthTabs;
|
||||||
188
app/page.tsx
188
app/page.tsx
|
|
@ -1,101 +1,97 @@
|
||||||
import Image from "next/image";
|
'use client';
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { getAuth, onAuthStateChanged, User } from 'firebase/auth';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
|
||||||
|
import { app } from '@/app/firebase/config';
|
||||||
|
import { SOLOGIN_API } from './shared';
|
||||||
|
|
||||||
|
const HomePage = () => {
|
||||||
|
const [user, setUser] = useState<any | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const auth = getAuth(app);
|
||||||
|
const [userPubkey, setUserPubkey] = useState();
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
||||||
|
const getSologinUser = async (currentUser: User)=>{
|
||||||
|
const url = `${SOLOGIN_API}getPubkey?email=${currentUser.uid}`;
|
||||||
|
const userDataRes = await fetch(url);
|
||||||
|
const userDataJson = await userDataRes.json();
|
||||||
|
|
||||||
|
return userDataJson;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SetUser = async(currentUser:User)=>{
|
||||||
|
const sologinUser = await getSologinUser(currentUser);
|
||||||
|
console.log(sologinUser);
|
||||||
|
setUser({
|
||||||
|
name: currentUser.displayName || 'Anonymous User',
|
||||||
|
email: currentUser.email,
|
||||||
|
id: currentUser.uid,
|
||||||
|
tokenId: await currentUser.getIdToken(),
|
||||||
|
pubkey: sologinUser.pub_key
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const signout = async()=>{
|
||||||
|
await auth.signOut();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
const unsubscribe = onAuthStateChanged(auth, async (currentUser) => {
|
||||||
|
if (currentUser) {
|
||||||
|
SetUser(currentUser);
|
||||||
|
} else {
|
||||||
|
router.push('/signup'); // Redirect to signup if not logged in
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => unsubscribe(); // Cleanup subscription on unmount
|
||||||
|
}, [auth, router]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex items-center justify-center bg-gray-900 text-white">
|
||||||
|
<p className="text-lg">Loading...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return null; // Return null to avoid rendering during redirection
|
||||||
|
}
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<div className="min-h-screen bg-gray-900 text-white flex items-center justify-center">
|
||||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full sm:w-96">
|
||||||
<Image
|
<h1 className="text-3xl font-bold mb-4">Welcome</h1>
|
||||||
className="dark:invert"
|
<p className="text-lg mb-2">
|
||||||
src="/next.svg"
|
<strong>Name:</strong> {user.name}
|
||||||
alt="Next.js logo"
|
</p>
|
||||||
width={180}
|
<p className="text-lg mb-2">
|
||||||
height={38}
|
<strong>Email:</strong> {user.email}
|
||||||
priority
|
</p>
|
||||||
/>
|
<p className="text-lg">
|
||||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
<strong>ID:</strong> {user.id}
|
||||||
<li className="mb-2">
|
</p>
|
||||||
Get started by editing{" "}
|
<p className="text-lg">
|
||||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
<strong>PubKey:</strong> {user.pubkey}
|
||||||
app/page.tsx
|
</p>
|
||||||
</code>
|
<button className='bg-red-500 px-5 py-2 rounded' onClick={signout}> Signout </button>
|
||||||
.
|
|
||||||
</li>
|
</div>
|
||||||
<li>Save and see your changes instantly.</li>
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className="dark:invert"
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={20}
|
|
||||||
height={20}
|
|
||||||
/>
|
|
||||||
Deploy now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Read our docs
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/file.svg"
|
|
||||||
alt="File icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Learn
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/window.svg"
|
|
||||||
alt="Window icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Examples
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
|
||||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
aria-hidden
|
|
||||||
src="/globe.svg"
|
|
||||||
alt="Globe icon"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Go to nextjs.org →
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default HomePage;
|
||||||
|
|
|
||||||
1
app/shared.ts
Normal file
1
app/shared.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export const SOLOGIN_API = "http://vps.playpoolstudios.com:20017/";
|
||||||
117
app/signin/page.tsx
Normal file
117
app/signin/page.tsx
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
'use client';
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { app, auth } from '@/app/firebase/config';
|
||||||
|
import { signInWithEmailAndPassword, getAuth } from 'firebase/auth';
|
||||||
|
import GoogleSignInButton from '../firebase/components/GoogleSignInButton';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { LoginSologin } from '../sologin';
|
||||||
|
|
||||||
|
const SignIn = () => {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try {
|
||||||
|
const userCredential = await signInWithEmailAndPassword(auth, email, password);
|
||||||
|
console.log('User signed in:', userCredential.user);
|
||||||
|
await LoginSologin(userCredential.user);
|
||||||
|
onLoginSuccess();
|
||||||
|
setEmail('');
|
||||||
|
setPassword('');
|
||||||
|
setError(null); // Clear errors on successful login
|
||||||
|
} catch (err: any) {
|
||||||
|
// Set the error message based on Firebase error codes
|
||||||
|
switch (err.code) {
|
||||||
|
case 'auth/user-not-found':
|
||||||
|
setError('User not found. Please check your email and try again.');
|
||||||
|
break;
|
||||||
|
case 'auth/invalid-credential':
|
||||||
|
setError('Incorrect password. Please try again.');
|
||||||
|
break;
|
||||||
|
case 'auth/network-request-failed':
|
||||||
|
setError('Unable to connect. Please check your internet connection.');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
setError('An unexpected error occurred. Please try again.');
|
||||||
|
console.error(err.code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const onLoginSuccess = ()=>{
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
|
||||||
|
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full sm:w-96">
|
||||||
|
<h2 className="text-3xl font-bold text-white mb-6">Sign In</h2>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="email">
|
||||||
|
Email Address
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="password">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full py-3 bg-blue-600 text-white rounded-md hover:bg-blue-500 transition duration-300"
|
||||||
|
>
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<p className="mt-4 text-center text-sm text-gray-400">
|
||||||
|
Don't have an account?{' '}
|
||||||
|
<a href="/signup" className="text-blue-400 hover:underline">
|
||||||
|
Sign Up
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<GoogleSignInButton onLoginSuccess={onLoginSuccess}></GoogleSignInButton>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Error Dialog */}
|
||||||
|
{error && (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 className="text-xl font-bold text-white mb-4">Error</h3>
|
||||||
|
<p className="text-gray-300">{error}</p>
|
||||||
|
<button
|
||||||
|
className=" mt-4 px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-500"
|
||||||
|
onClick={() => setError(null)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignIn;
|
||||||
122
app/signup/page.tsx
Normal file
122
app/signup/page.tsx
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
'use client'
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import {app, auth} from '@/app/firebase/config';
|
||||||
|
import { createUserWithEmailAndPassword, getAuth } from 'firebase/auth';
|
||||||
|
import { getApp } from 'firebase/app';
|
||||||
|
import { SOLOGIN_API } from '../shared';
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
import { RegisterSologin } from '../sologin';
|
||||||
|
|
||||||
|
const SignUp = () => {
|
||||||
|
const [email, setEmail] = useState('');
|
||||||
|
const [password, setPassword] = useState('');
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const onLoginSuccess = ()=>{
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
try{
|
||||||
|
createUserWithEmailAndPassword(auth, email, password)
|
||||||
|
.then(async(userCredential) => {
|
||||||
|
// Signed up
|
||||||
|
const user = userCredential.user;
|
||||||
|
await RegisterSologin(userCredential.user);
|
||||||
|
console.log(user);
|
||||||
|
onLoginSuccess();
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const errorCode = error.code;
|
||||||
|
|
||||||
|
switch(errorCode){
|
||||||
|
case 'auth/email-already-in-use':
|
||||||
|
setError("This email address is already signed up. Forgot password?")
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
setError("Something went wrong");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
setEmail('');
|
||||||
|
setPassword('');
|
||||||
|
}catch(e){
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
|
||||||
|
<div className="bg-gray-800 p-8 rounded-lg shadow-lg w-full sm:w-96">
|
||||||
|
<h2 className="text-3xl font-bold text-white mb-6">Sign Up</h2>
|
||||||
|
<form onSubmit={handleSubmit}>
|
||||||
|
<div className="mb-4">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="email">
|
||||||
|
Email Address
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="mb-6">
|
||||||
|
<label className="block text-sm text-gray-300 mb-2" htmlFor="password">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
className="w-full p-3 bg-gray-700 text-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="w-full py-3 bg-blue-600 text-white rounded-md hover:bg-blue-500 transition duration-300"
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<p className="mt-4 text-center text-sm text-gray-400">
|
||||||
|
Already have an account?{' '}
|
||||||
|
<a href="/signin" className="text-blue-400 hover:underline">
|
||||||
|
Log In
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{error && (
|
||||||
|
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||||
|
<div className="bg-gray-800 p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 className="text-xl font-bold text-white mb-4">Error</h3>
|
||||||
|
<p className="text-gray-300">{error}</p>
|
||||||
|
<button
|
||||||
|
className=" mt-4 px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-500"
|
||||||
|
onClick={() => setError(null)}
|
||||||
|
>
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SignUp;
|
||||||
18
app/sologin.ts
Normal file
18
app/sologin.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { User } from "firebase/auth"
|
||||||
|
import { SOLOGIN_API } from "./shared";
|
||||||
|
|
||||||
|
export const LoginSologin=async(currentUser:User)=>{
|
||||||
|
const tokenId = await currentUser.getIdToken();
|
||||||
|
const sologinLoginRes = await fetch(`${SOLOGIN_API}login?tokenId=${tokenId}`);
|
||||||
|
|
||||||
|
const resText = await sologinLoginRes.text();
|
||||||
|
console.log(resText);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RegisterSologin= async(currentUser:User)=>{
|
||||||
|
const tokenId = await currentUser.getIdToken();
|
||||||
|
const sologinRegisterRes = await fetch(`${SOLOGIN_API}register?tokenId=${tokenId}`);
|
||||||
|
|
||||||
|
const resText = await sologinRegisterRes.text();
|
||||||
|
console.log(resText);
|
||||||
|
}
|
||||||
24
firebase-debug.log
Normal file
24
firebase-debug.log
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
[debug] [2024-11-19T17:18:05.272Z] ----------------------------------------------------------------------
|
||||||
|
[debug] [2024-11-19T17:18:05.274Z] Command: C:\Program Files\nodejs\node.exe C:\Users\warlock\AppData\Roaming\npm\node_modules\firebase-tools\lib\bin\firebase.js init
|
||||||
|
[debug] [2024-11-19T17:18:05.274Z] CLI Version: 13.25.0
|
||||||
|
[debug] [2024-11-19T17:18:05.275Z] Platform: win32
|
||||||
|
[debug] [2024-11-19T17:18:05.275Z] Node Version: v20.16.0
|
||||||
|
[debug] [2024-11-19T17:18:05.275Z] Time: Tue Nov 19 2024 22:48:05 GMT+0530 (India Standard Time)
|
||||||
|
[debug] [2024-11-19T17:18:05.275Z] ----------------------------------------------------------------------
|
||||||
|
[debug]
|
||||||
|
[debug] [2024-11-19T17:18:05.277Z] >>> [apiv2][query] GET https://firebase-public.firebaseio.com/cli.json [none]
|
||||||
|
[debug] [2024-11-19T17:18:05.321Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
||||||
|
[debug] [2024-11-19T17:18:05.321Z] > authorizing via signed-in user (sewmina7@gmail.com)
|
||||||
|
[info]
|
||||||
|
######## #### ######## ######## ######## ### ###### ########
|
||||||
|
## ## ## ## ## ## ## ## ## ## ##
|
||||||
|
###### ## ######## ###### ######## ######### ###### ######
|
||||||
|
## ## ## ## ## ## ## ## ## ## ##
|
||||||
|
## #### ## ## ######## ######## ## ## ###### ########
|
||||||
|
|
||||||
|
You're about to initialize a Firebase project in this directory:
|
||||||
|
|
||||||
|
I:\NodeJS\mhunt_auth_dash_sologin
|
||||||
|
|
||||||
|
[debug] [2024-11-19T17:18:05.852Z] <<< [apiv2][status] GET https://firebase-public.firebaseio.com/cli.json 200
|
||||||
|
[debug] [2024-11-19T17:18:05.853Z] <<< [apiv2][body] GET https://firebase-public.firebaseio.com/cli.json {"cloudBuildErrorAfter":1594252800000,"cloudBuildWarnAfter":1590019200000,"defaultNode10After":1594252800000,"minVersion":"3.0.5","node8DeploysDisabledAfter":1613390400000,"node8RuntimeDisabledAfter":1615809600000,"node8WarnAfter":1600128000000}
|
||||||
|
|
@ -1,7 +1,19 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig = {
|
||||||
/* config options here */
|
async headers() {
|
||||||
};
|
return [
|
||||||
|
{
|
||||||
|
// matching all API routes
|
||||||
|
source: "/api/:path*",
|
||||||
|
headers: [
|
||||||
|
{ key: "Access-Control-Allow-Credentials", value: "true" },
|
||||||
|
{ key: "Access-Control-Allow-Origin", value: "vps.playpoolstudios.com" }, // replace this your actual origin
|
||||||
|
{ key: "Access-Control-Allow-Methods", value: "GET,DELETE,PATCH,POST,PUT" },
|
||||||
|
{ key: "Access-Control-Allow-Headers", value: "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
export default nextConfig;
|
export default nextConfig;
|
||||||
|
|
|
||||||
1152
package-lock.json
generated
1152
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
|
|
@ -9,18 +9,20 @@
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"firebase": "^11.0.2",
|
||||||
|
"next": "15.0.3",
|
||||||
"react": "19.0.0-rc-66855b96-20241106",
|
"react": "19.0.0-rc-66855b96-20241106",
|
||||||
"react-dom": "19.0.0-rc-66855b96-20241106",
|
"react-dom": "19.0.0-rc-66855b96-20241106"
|
||||||
"next": "15.0.3"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5",
|
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^18",
|
"@types/react": "^18",
|
||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"postcss": "^8",
|
"autoprefixer": "^10.4.20",
|
||||||
"tailwindcss": "^3.4.1",
|
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-config-next": "15.0.3"
|
"eslint-config-next": "15.0.3",
|
||||||
|
"postcss": "^8.4.49",
|
||||||
|
"tailwindcss": "^3.4.15",
|
||||||
|
"typescript": "^5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
||||||
15
tailwind.config.js
Normal file
15
tailwind.config.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: [
|
||||||
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
|
||||||
|
// Or if using `src` directory:
|
||||||
|
"./src/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
|
],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user