Skip to content

Commit 91ad5a2

Browse files
author
pengyu
committed
complete sign in in landing page
1 parent 9771a56 commit 91ad5a2

File tree

5 files changed

+193
-126
lines changed

5 files changed

+193
-126
lines changed

backend/src/auth/auth.service.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -119,18 +119,18 @@ export class AuthService {
119119
}
120120
}
121121
async logout(token: string): Promise<boolean> {
122-
Logger.log('logout token', token);
123122
try {
124123
await this.jwtService.verifyAsync(token);
124+
const refreshToken = await this.refreshTokenRepository.findOne({ where: { token } });
125+
126+
if (refreshToken) {
127+
await this.refreshTokenRepository.remove(refreshToken);
128+
}
129+
130+
return true;
125131
} catch (error) {
126132
return false;
127133
}
128-
129-
if (!(await this.jwtCacheService.isTokenStored(token))) {
130-
return false;
131-
}
132-
this.jwtCacheService.removeToken(token);
133-
return true;
134134
}
135135

136136
async assignMenusToRole(roleId: string, menuIds: string[]): Promise<Role> {

frontend/src/app/layout.tsx

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
1-
import type { Metadata, Viewport } from 'next';
2-
import { Inter } from 'next/font/google';
3-
import './globals.css';
4-
import { BaseProviders } from './providers/BaseProvider';
5-
import { ThemeProvider } from './providers/ThemeProvider';
6-
import ApolloWrapper from './providers/ApolloProvider';
1+
import type { Metadata, Viewport } from "next";
2+
import { Inter } from "next/font/google";
3+
import "./globals.css";
4+
import { BaseProviders } from "./providers/BaseProvider";
5+
import { ThemeProvider } from "./providers/ThemeProvider";
6+
import ApolloWrapper from "./providers/ApolloProvider";
7+
import { AuthProvider } from "@/contexts/AuthContext"; // ✅ Import AuthProvider
78

8-
const inter = Inter({ subsets: ['latin'] });
9+
const inter = Inter({ subsets: ["latin"] });
910

1011
export const metadata: Metadata = {
11-
title: 'Codefox',
12-
description: 'The best dev project generator',
12+
title: "Codefox",
13+
description: "The best dev project generator",
1314
};
1415

1516
export const viewport: Viewport = {
16-
width: 'device-width',
17+
width: "device-width",
1718
initialScale: 1,
1819
maximumScale: 1,
1920
userScalable: false,
@@ -27,8 +28,10 @@ export default function RootLayout({
2728
return (
2829
<html lang="en" suppressHydrationWarning>
2930
<body className={inter.className}>
30-
<ApolloWrapper>
31-
<ThemeProvider>{children}</ThemeProvider>
31+
<ApolloWrapper> {/* ✅ Ensure Apollo is ready first */}
32+
<AuthProvider> {/* ✅ Then wrap AuthProvider inside */}
33+
<ThemeProvider>{children}</ThemeProvider>
34+
</AuthProvider>
3235
</ApolloWrapper>
3336
</body>
3437
</html>
+27-8
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,41 @@
1-
'use client';
2-
3-
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
1+
"use client";
2+
import { ApolloClient, InMemoryCache, ApolloProvider, createHttpLink } from '@apollo/client';
3+
import { setContext } from '@apollo/client/link/context';
44
import { useEffect, useState } from 'react';
5+
import { useAuth } from '@/contexts/AuthContext'; // ✅ Import AuthContext
6+
7+
const httpLink = createHttpLink({
8+
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL || 'http://localhost:4000/graphql',
9+
});
510

611
export default function ApolloWrapper({ children }: { children: React.ReactNode }) {
12+
const { token, refreshAccessToken } = useAuth(); // ✅ Access AuthContext
713
const [client, setClient] = useState<ApolloClient<any> | null>(null);
814

15+
const authLink = setContext(async (_, { headers }) => {
16+
let currentToken = localStorage.getItem('accessToken');
17+
18+
if (!currentToken) {
19+
currentToken = await refreshAccessToken(); // ✅ Refresh token if missing
20+
}
21+
22+
return {
23+
headers: {
24+
...headers,
25+
Authorization: currentToken ? `Bearer ${currentToken}` : "",
26+
},
27+
};
28+
});
29+
930
useEffect(() => {
1031
const client = new ApolloClient({
11-
uri: process.env.NEXT_PUBLIC_GRAPHQL_URL || 'http://localhost:4000/graphql',
32+
link: authLink.concat(httpLink),
1233
cache: new InMemoryCache(),
1334
});
1435
setClient(client);
15-
}, []);
36+
}, [token]); // ✅ Rebuild client when token updates
1637

17-
if (!client) {
18-
return null;
19-
}
38+
if (!client) return null;
2039

2140
return <ApolloProvider client={client}>{children}</ApolloProvider>;
2241
}

frontend/src/components/SignInModal.tsx

+75-99
Original file line numberDiff line numberDiff line change
@@ -26,43 +26,27 @@ export function SignInModal({
2626
const router = useRouter();
2727
const [email, setEmail] = useState('');
2828
const [password, setPassword] = useState('');
29-
const [showPassword, setShowPassword] = useState(false);
29+
const [errorMessage, setErrorMessage] = useState<string | null>(null); // ✅ State for error message
3030

3131
const [loginUser, { loading }] = useMutation(LOGIN_USER, {
3232
onCompleted: (data) => {
3333
if (data?.login) {
34-
// Store tokens
3534
localStorage.setItem('accessToken', data.login.accessToken);
3635
localStorage.setItem('refreshToken', data.login.refreshToken);
37-
38-
// Show success message
3936
toast.success('Login successful!');
40-
41-
// Close the modal
37+
setErrorMessage(null); // ✅ Clear error on success
4238
onClose();
43-
44-
45-
router.push('/main/chat');
39+
router.push('/x');
4640
}
4741
},
4842
onError: (error) => {
49-
toast.error(error.message);
43+
setErrorMessage('Incorrect email or password. Please try again.'); // ✅ Show error message
5044
},
5145
});
5246

53-
const handleEmailSubmit = (e: React.FormEvent) => {
54-
e.preventDefault();
55-
setPassword(''); // Clear password when moving to password step
56-
setShowPassword(true);
57-
};
58-
59-
const handleBackToEmail = () => {
60-
setPassword(''); // Clear password when going back
61-
setShowPassword(false);
62-
};
63-
6447
const handleSubmit = async (e: React.FormEvent) => {
6548
e.preventDefault();
49+
setErrorMessage(null); // ✅ Clear error when attempting login again
6650
try {
6751
await loginUser({
6852
variables: {
@@ -85,96 +69,88 @@ export function SignInModal({
8569
<TextureCardHeader className="flex flex-col gap-1 items-center justify-center p-4">
8670
<TextureCardTitle>Welcome back</TextureCardTitle>
8771
<p className="text-center text-neutral-600 dark:text-neutral-400">
88-
{showPassword
89-
? 'Enter your password'
90-
: 'Sign in to your account'}
72+
Sign in to your account
9173
</p>
9274
</TextureCardHeader>
9375
<TextureSeparator />
9476
<TextureCardContent>
95-
{!showPassword ? (
96-
<>
97-
<form onSubmit={handleEmailSubmit} className="space-y-4">
98-
<div>
99-
<Label htmlFor="email">Email</Label>
100-
<Input
101-
id="email"
102-
type="email"
103-
value={email}
104-
onChange={(e) => setEmail(e.target.value)}
105-
required
106-
className="w-full px-4 py-2 rounded-md border"
107-
/>
108-
</div>
109-
<Button type="submit" className="w-full">
110-
Continue
111-
</Button>
112-
</form>
77+
<form onSubmit={handleSubmit} className="space-y-4">
78+
<div>
79+
<Label htmlFor="email">Email</Label>
80+
<Input
81+
id="email"
82+
type="email"
83+
value={email}
84+
onChange={(e) => {
85+
setEmail(e.target.value);
86+
setErrorMessage(null); // ✅ Clear error when user types
87+
}}
88+
required
89+
className="w-full px-4 py-2 rounded-md border"
90+
/>
91+
</div>
92+
<div>
93+
<Label htmlFor="password">Password</Label>
94+
<Input
95+
id="password"
96+
type="password"
97+
value={password}
98+
onChange={(e) => {
99+
setPassword(e.target.value);
100+
setErrorMessage(null); // ✅ Clear error when user types
101+
}}
102+
required
103+
className="w-full px-4 py-2 rounded-md border"
104+
/>
105+
</div>
106+
107+
{/* ✅ Show error message if login fails */}
108+
{errorMessage && (
109+
<div className="text-red-500 text-sm text-center">{errorMessage}</div>
110+
)}
113111

114-
<div className="mt-6">
115-
<div className="relative">
116-
<div className="absolute inset-0 flex items-center">
117-
<span className="w-full border-t" />
118-
</div>
119-
<div className="relative flex justify-center text-xs uppercase">
120-
<span className="bg-white dark:bg-zinc-900 px-2 text-muted-foreground">
121-
Or continue with
122-
</span>
123-
</div>
124-
</div>
112+
<Button type="submit" className="w-full" disabled={loading}>
113+
{loading ? 'Signing in...' : 'Sign in'}
114+
</Button>
115+
</form>
125116

126-
<div className="mt-4 flex flex-col gap-4">
127-
<Button
128-
variant="outline"
129-
className="flex items-center gap-2 w-full"
130-
>
131-
<img
132-
src="/images/google.png"
133-
alt="Google"
134-
className="w-5 h-5"
135-
/>
136-
<span>Google</span>
137-
</Button>
138-
<Button
139-
variant="outline"
140-
className="flex items-center gap-2 w-full"
141-
>
142-
<img
143-
src="/images/github.png"
144-
alt="GitHub"
145-
className="w-5 h-5"
146-
/>
147-
<span>GitHub</span>
148-
</Button>
149-
</div>
117+
<div className="mt-6">
118+
<div className="relative">
119+
<div className="absolute inset-0 flex items-center">
120+
<span className="w-full border-t" />
150121
</div>
151-
</>
152-
) : (
153-
<form onSubmit={handleSubmit} className="space-y-4">
154-
<div>
155-
<Label htmlFor="password">Password</Label>
156-
<Input
157-
id="password"
158-
type="password"
159-
value={password}
160-
onChange={(e) => setPassword(e.target.value)}
161-
required
162-
className="w-full px-4 py-2 rounded-md border"
163-
/>
122+
<div className="relative flex justify-center text-xs uppercase">
123+
<span className="bg-white dark:bg-zinc-900 px-2 text-muted-foreground">
124+
Or continue with
125+
</span>
164126
</div>
165-
<Button type="submit" className="w-full">
166-
Sign in
127+
</div>
128+
129+
<div className="mt-4 flex flex-col gap-4">
130+
<Button
131+
variant="outline"
132+
className="flex items-center gap-2 w-full"
133+
>
134+
<img
135+
src="/images/google.png"
136+
alt="Google"
137+
className="w-5 h-5"
138+
/>
139+
<span>Google</span>
167140
</Button>
168141
<Button
169-
type="button"
170-
variant="link"
171-
onClick={handleBackToEmail}
172-
className="w-full"
142+
variant="outline"
143+
className="flex items-center gap-2 w-full"
173144
>
174-
Back to email
145+
<img
146+
src="/images/github.png"
147+
alt="GitHub"
148+
className="w-5 h-5"
149+
/>
150+
<span>GitHub</span>
175151
</Button>
176-
</form>
177-
)}
152+
</div>
153+
</div>
178154
</TextureCardContent>
179155
</div>
180156
</BackgroundGradient>

0 commit comments

Comments
 (0)