Skip to content

Commit dab86d2

Browse files
committed
feat: login OK
1 parent 1559f31 commit dab86d2

33 files changed

+603
-92
lines changed
0 Bytes
Binary file not shown.
32 KB
Binary file not shown.
32.2 KB
Binary file not shown.

kommande-access-gui/src/app/api/auth/[...nextauth]/route.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,26 @@ const authOptions = {
2727
async authorize(credentials) {
2828
try {
2929
// Call your backend API to validate credentials
30-
const response = await fetch('https://your-api.com/auth/login', {
30+
const response = await fetch(`${process.env.NEXT_PUBLIC_AUTH_API_URL}/user/login`, {
3131
method: 'POST',
3232
headers: { 'Content-Type': 'application/json' },
3333
body: JSON.stringify({
3434
email: credentials.email,
3535
password: credentials.password,
3636
}),
37+
mode: 'cors',
38+
credentials: 'include',
3739
});
3840

41+
// 即使响应不是OK的,也尝试读取响应主体
3942
const userData = await response.json();
4043

4144
if (response.ok && userData) {
4245
return userData;
4346
}
47+
48+
// 如果响应不OK,记录错误但返回null
49+
console.error('Login failed with status:', response.status, userData);
4450
return null;
4551
} catch (error) {
4652
console.error('Login failed:', error);

kommande-access-gui/src/app/users/Dashboard/page.jsx

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,40 @@ export default function Dashboard() {
2929
if (!session?.user) return;
3030

3131
try {
32-
const response = await fetch('https://your-api.com/user?name=${encodeURIComponent(session.user.name)}`', {
32+
const response = await fetch(`${process.env.NEXT_PUBLIC_AUTH_API_URL}/user/user?userId=${encodeURIComponent(session.user.id)}`, {
3333
method: 'GET',
3434
headers: {
3535
'Content-Type': 'application/json',
3636
//Authorization: `Bearer ${session.user.token}`, // Use token from session
3737
},
38+
mode: 'cors',
39+
credentials: 'include',
3840
});
3941

4042
if (!response.ok) {
41-
throw new Error('Failed to fetch user data');
43+
const errorText = await response.text();
44+
let errorMessage = 'Failed to fetch user data';
45+
try {
46+
// 尝试解析错误JSON
47+
const errorData = JSON.parse(errorText);
48+
if (typeof errorData === 'object') {
49+
errorMessage = errorData.message ||
50+
errorData.title ||
51+
errorData.error ||
52+
'Failed to fetch user data';
53+
}
54+
} catch (e) {
55+
// 如果不是有效JSON,使用文本
56+
if (errorText) errorMessage = errorText;
57+
}
58+
throw new Error(errorMessage);
4259
}
4360

4461
const data = await response.json();
4562
setUserData(data);
4663
} catch (error) {
4764
console.error('Error fetching user data:', error);
48-
setError('Failed to load user information');
65+
setError(error.message || 'Failed to load user information');
4966
} finally {
5067
setLoading(false);
5168
}

kommande-access-gui/src/app/users/Login/page.jsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ export default function Login() {
5353
if (result.error) {
5454
setError('Invalid email or password. Please try again.');
5555
} else {
56+
// Update user context with session data
57+
userLoggedIn(result);
5658
router.push('/users/Dashboard');
5759
}
5860
} catch (error) {
@@ -230,7 +232,7 @@ export default function Login() {
230232
<p className="text-center text-gray-600 text-sm">
231233
Don&apos;t have an account?{' '}
232234
<Link
233-
href="/auth/register"
235+
href="/users/Register"
234236
className="text-primary hover:text-primary/80 font-semibold transition-colors"
235237
>
236238
Sign up
Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
'use client';
2+
3+
import { useState } from 'react';
4+
import Image from 'next/image';
5+
import Link from 'next/link';
6+
import { useRouter } from 'next/navigation';
7+
import { signIn } from 'next-auth/react';
8+
import { UserIcon, LockIcon, MailIcon, Loader2, PhoneIcon } from 'lucide-react';
9+
10+
/**
11+
* Register page component that handles new user registration
12+
*/
13+
export default function Register() {
14+
const router = useRouter();
15+
const [error, setError] = useState('');
16+
const [success, setSuccess] = useState('');
17+
const [isLoading, setIsLoading] = useState(false);
18+
const [formData, setFormData] = useState({
19+
name: '',
20+
email: '',
21+
password: '',
22+
confirmPassword: '',
23+
phoneNumber: '',
24+
});
25+
26+
// Handle input changes
27+
const handleInputChange = (e) => {
28+
const { name, value } = e.target;
29+
setFormData((prev) => ({
30+
...prev,
31+
[name]: value,
32+
}));
33+
setError('');
34+
};
35+
36+
// Handle form submission
37+
const handleRegister = async (e) => {
38+
e.preventDefault();
39+
setIsLoading(true);
40+
setError('');
41+
setSuccess('');
42+
43+
// Validate form inputs
44+
if (formData.password !== formData.confirmPassword) {
45+
setError('Passwords do not match.');
46+
setIsLoading(false);
47+
return;
48+
}
49+
50+
if (formData.password.length < 6) {
51+
setError('Password must be at least 6 characters long.');
52+
setIsLoading(false);
53+
return;
54+
}
55+
56+
if (!formData.email.match(/^[^@\s]+@[^@\s]+\.[^@\s]+$/)) {
57+
setError('Please enter a valid email address.');
58+
setIsLoading(false);
59+
return;
60+
}
61+
62+
if (!formData.name.trim()) {
63+
setError('Please enter your name.');
64+
setIsLoading(false);
65+
return;
66+
}
67+
68+
try {
69+
// 构建用户对象 - 确保与后端模型字段完全匹配
70+
const userObject = {
71+
type: 'BasicUser',
72+
Name: formData.name,
73+
Email: formData.email,
74+
PasswordHash: formData.password,
75+
PhoneNumber: formData.phoneNumber || undefined // 不发送null值,而是完全省略
76+
};
77+
78+
// 如果是BasicUser类型,添加Image属性
79+
userObject.Image = '/default-avatar.png';
80+
81+
console.log('Sending registration request with data:', JSON.stringify(userObject, null, 2));
82+
83+
const response = await fetch(`${process.env.NEXT_PUBLIC_AUTH_API_URL}/user/register`, {
84+
method: 'POST',
85+
headers: {
86+
'Content-Type': 'application/json',
87+
'Accept': 'application/json'
88+
},
89+
body: JSON.stringify(userObject),
90+
mode: 'cors',
91+
credentials: 'include',
92+
});
93+
94+
console.log('Registration response status:', response.status);
95+
96+
// 尝试读取响应,无论成功或失败
97+
let responseText;
98+
try {
99+
responseText = await response.text();
100+
console.log('Registration response body:', responseText);
101+
} catch (error) {
102+
console.error('Error reading response:', error);
103+
}
104+
105+
if (response.ok) {
106+
setSuccess('Registration successful! You can now log in.');
107+
// Redirect to login page after 2 seconds
108+
setTimeout(() => {
109+
router.push('/users/Login');
110+
}, 2000);
111+
} else {
112+
// 尝试解析JSON,如果失败则使用原始文本
113+
let errorMessage = 'Registration failed. Please try again.';
114+
if (responseText) {
115+
try {
116+
const errorData = JSON.parse(responseText);
117+
if (typeof errorData === 'string') {
118+
errorMessage = errorData;
119+
} else if (errorData && typeof errorData === 'object') {
120+
// 尝试提取各种可能的错误消息字段
121+
errorMessage = errorData.message ||
122+
errorData.title ||
123+
errorData.error ||
124+
errorData.errors?.join(', ') ||
125+
(errorData.errors && typeof errorData.errors === 'object' ?
126+
Object.values(errorData.errors).flat().join(', ') :
127+
JSON.stringify(errorData));
128+
}
129+
} catch (e) {
130+
// 如果不是有效的JSON,使用原始文本
131+
errorMessage = responseText;
132+
}
133+
}
134+
setError(errorMessage);
135+
}
136+
} catch (error) {
137+
console.error('Registration error:', error);
138+
setError('An error occurred during registration. Please try again.');
139+
} finally {
140+
setIsLoading(false);
141+
}
142+
};
143+
144+
return (
145+
<main className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-100 to-slate-200">
146+
<div className="w-full max-w-6xl mx-4 bg-white rounded-2xl shadow-xl overflow-hidden">
147+
<div className="grid md:grid-cols-2 gap-0">
148+
{/* Left - Image Section */}
149+
<div className="relative h-48 md:h-auto">
150+
<Image
151+
src="/imgLogin/login_1.jpg"
152+
alt="Registration background"
153+
fill
154+
priority
155+
className="object-cover"
156+
sizes="(max-width: 768px) 100vw, 50vw"
157+
/>
158+
<div className="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent" />
159+
</div>
160+
161+
{/* Right - Registration Form Section */}
162+
<div className="p-6 md:p-12 space-y-6">
163+
{/* Header */}
164+
<div className="text-center space-y-2">
165+
<h1 className="text-2xl md:text-3xl font-bold text-gray-900">
166+
Create an Account
167+
</h1>
168+
<p className="text-slate-600">
169+
Join us and enjoy our services
170+
</p>
171+
</div>
172+
173+
{/* Registration Form */}
174+
<form className="space-y-4" onSubmit={handleRegister}>
175+
<div className="space-y-4">
176+
{/* Name Input */}
177+
<div className="relative">
178+
<UserIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
179+
<input
180+
type="text"
181+
name="name"
182+
value={formData.name}
183+
onChange={handleInputChange}
184+
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
185+
placeholder="Full Name"
186+
disabled={isLoading}
187+
required
188+
/>
189+
</div>
190+
191+
{/* Email Input */}
192+
<div className="relative">
193+
<MailIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
194+
<input
195+
type="email"
196+
name="email"
197+
value={formData.email}
198+
onChange={handleInputChange}
199+
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
200+
placeholder="Email"
201+
disabled={isLoading}
202+
required
203+
/>
204+
</div>
205+
206+
{/* Phone Input (Optional) */}
207+
<div className="relative">
208+
<PhoneIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
209+
<input
210+
type="tel"
211+
name="phoneNumber"
212+
value={formData.phoneNumber}
213+
onChange={handleInputChange}
214+
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
215+
placeholder="Phone Number (Optional)"
216+
disabled={isLoading}
217+
/>
218+
</div>
219+
220+
{/* Password Input */}
221+
<div className="relative">
222+
<LockIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
223+
<input
224+
type="password"
225+
name="password"
226+
value={formData.password}
227+
onChange={handleInputChange}
228+
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
229+
placeholder="Password"
230+
disabled={isLoading}
231+
required
232+
minLength={6}
233+
/>
234+
</div>
235+
236+
{/* Confirm Password Input */}
237+
<div className="relative">
238+
<LockIcon className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-gray-400" />
239+
<input
240+
type="password"
241+
name="confirmPassword"
242+
value={formData.confirmPassword}
243+
onChange={handleInputChange}
244+
className="w-full pl-10 pr-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-primary focus:border-transparent placeholder:text-gray-500"
245+
placeholder="Confirm Password"
246+
disabled={isLoading}
247+
required
248+
minLength={6}
249+
/>
250+
</div>
251+
</div>
252+
253+
{/* Error Message */}
254+
{error && (
255+
<div className="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg text-sm">
256+
{typeof error === 'string' ? error : 'An error occurred during registration. Please try again.'}
257+
</div>
258+
)}
259+
260+
{/* Success Message */}
261+
{success && (
262+
<div className="bg-green-50 border border-green-200 text-green-600 px-4 py-3 rounded-lg text-sm">
263+
{success}
264+
</div>
265+
)}
266+
267+
{/* Register Button */}
268+
<button
269+
type="submit"
270+
disabled={isLoading}
271+
className="w-full bg-primary hover:bg-primary/90 text-white font-semibold py-2.5 rounded-lg transition focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2"
272+
>
273+
{isLoading ? (
274+
<>
275+
<Loader2 className="animate-spin h-5 w-5" />
276+
<span>Creating account...</span>
277+
</>
278+
) : (
279+
<span>Create Account</span>
280+
)}
281+
</button>
282+
</form>
283+
284+
{/* Sign In Link */}
285+
<p className="text-center text-gray-600 text-sm">
286+
Already have an account?{' '}
287+
<Link
288+
href="/users/Login"
289+
className="text-primary hover:text-primary/80 font-semibold transition-colors"
290+
>
291+
Sign in
292+
</Link>
293+
</p>
294+
</div>
295+
</div>
296+
</div>
297+
</main>
298+
);
299+
}

0 commit comments

Comments
 (0)