diff --git a/app/components/auth/Login.tsx b/app/components/auth/Login.tsx index 49ad241..72e960c 100644 --- a/app/components/auth/Login.tsx +++ b/app/components/auth/Login.tsx @@ -1,18 +1,24 @@ import React, { useState } from 'react'; import { useNavigate } from '@remix-run/react'; +import { validatePhoneNumber } from '~/utils/validation'; export function Login() { - const [email, setEmail] = useState(''); + const [phone, setPhone] = useState(''); const [password, setPassword] = useState(''); + const [phoneError, setPhoneError] = useState(''); const navigate = useNavigate(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!validatePhoneNumber(phone)) { + setPhoneError('请输入有效的手机号码'); + return; + } try { const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ email, password }), + body: JSON.stringify({ phone, password }), }); if (response.ok) { const data = (await response.json()) as { token: string }; @@ -27,22 +33,45 @@ export function Login() { }; return ( -
- setEmail(e.target.value)} - placeholder="邮箱" - required - /> - setPassword(e.target.value)} - placeholder="密码" - required - /> - + +
+ + { + setPhone(e.target.value); + setPhoneError(''); + }} + required + className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" + /> + {phoneError &&

{phoneError}

} +
+
+ + setPassword(e.target.value)} + required + className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" + /> +
+
+ +
); } diff --git a/app/components/auth/Register.tsx b/app/components/auth/Register.tsx index 2c9046f..1ac731c 100644 --- a/app/components/auth/Register.tsx +++ b/app/components/auth/Register.tsx @@ -1,19 +1,29 @@ import React, { useState } from 'react'; import { useNavigate } from '@remix-run/react'; +import { validatePhoneNumber } from '~/utils/validation'; export function Register() { - const [username, setUsername] = useState(''); - const [email, setEmail] = useState(''); + const [phone, setPhone] = useState(''); const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [phoneError, setPhoneError] = useState(''); const navigate = useNavigate(); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); + if (!validatePhoneNumber(phone)) { + setPhoneError('请输入有效的手机号码'); + return; + } + if (password !== confirmPassword) { + alert('两次输入的密码不一致'); + return; + } try { const response = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, email, password }), + body: JSON.stringify({ phone, password }), }); if (response.ok) { navigate('/login'); @@ -26,29 +36,58 @@ export function Register() { }; return ( -
- setUsername(e.target.value)} - placeholder="用户名" - required - /> - setEmail(e.target.value)} - placeholder="邮箱" - required - /> - setPassword(e.target.value)} - placeholder="密码" - required - /> - + +
+ + { + setPhone(e.target.value); + setPhoneError(''); + }} + required + className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" + /> + {phoneError &&

{phoneError}

} +
+
+ + setPassword(e.target.value)} + required + className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" + /> +
+
+ + setConfirmPassword(e.target.value)} + required + className="mt-1 block w-full px-3 py-2 bg-bolt-elements-background-depth-1 border border-bolt-elements-borderColor rounded-md shadow-sm focus:outline-none focus:ring-bolt-elements-button-primary-background focus:border-bolt-elements-button-primary-background" + /> +
+
+ +
); } diff --git a/app/components/header/Header.tsx b/app/components/header/Header.tsx index fa1e3a6..d3e2f6d 100644 --- a/app/components/header/Header.tsx +++ b/app/components/header/Header.tsx @@ -44,13 +44,13 @@ export function Header() { )} diff --git a/app/routes/api.auth.login.ts b/app/routes/api.auth.login.ts index 336c13d..20b3ddb 100644 --- a/app/routes/api.auth.login.ts +++ b/app/routes/api.auth.login.ts @@ -1,15 +1,25 @@ -import { json } from '@remix-run/cloudflare'; -import { type ActionFunction } from '@remix-run/node'; +import { json } from '@remix-run/node'; +import type { ActionFunction } from '@remix-run/node'; +import { validatePhoneNumber } from '~/utils/validation'; import { verifyLogin, createToken } from '~/utils/auth.server'; export const action: ActionFunction = async ({ request }) => { - const { email, password } = (await request.json()) as { email: string; password: string }; - - const user = await verifyLogin(email, password); - if (!user) { - return json({ success: false, error: 'Invalid credentials' }, { status: 400 }); + const { phone, password } = await request.json() as { phone: string, password: string }; + + if (!validatePhoneNumber(phone)) { + return json({ error: '无效的手机号码' }, { status: 400 }); + } + + try { + const user = await verifyLogin(phone, password); + if (!user) { + return json({ error: '手机号或密码不正确' }, { status: 401 }); + } + + const token = createToken(user._id.toString()); + return json({ token }); + } catch (error) { + console.error('Login error:', error); + return json({ error: '登录失败,请稍后再试' }, { status: 500 }); } - - const token = createToken(user._id.toString()); - return json({ success: true, token }); }; diff --git a/app/routes/api.auth.register.ts b/app/routes/api.auth.register.ts index f82aa3e..868baf9 100644 --- a/app/routes/api.auth.register.ts +++ b/app/routes/api.auth.register.ts @@ -1,20 +1,31 @@ -import { type ActionFunction, json } from '@remix-run/cloudflare'; +import { json } from '@remix-run/node'; +import type { ActionFunction } from '@remix-run/node'; +import { validatePhoneNumber } from '~/utils/validation'; import { db } from '~/utils/db.server'; import { hashPassword } from '~/utils/auth.server'; export const action: ActionFunction = async ({ request }) => { - const { username, email, password } = (await request.json()) as { username: string; email: string; password: string }; - + const { phone, password } = await request.json() as { phone: string, password: string }; + + if (!validatePhoneNumber(phone)) { + return json({ error: '无效的手机号码' }, { status: 400 }); + } + try { + const existingUser = await db('users').where({ phone }).first(); + if (existingUser) { + return json({ error: '该手机号已被注册' }, { status: 400 }); + } + const hashedPassword = await hashPassword(password); const [userId] = await db('users').insert({ - username, - email, + phone, password: hashedPassword }); + return json({ success: true, userId }); } catch (error) { console.error('Registration error:', error); - return json({ success: false, error: 'Registration failed' }, { status: 400 }); + return json({ error: '注册失败,请稍后再试' }, { status: 500 }); } }; diff --git a/app/utils/validation.ts b/app/utils/validation.ts new file mode 100644 index 0000000..fbd2705 --- /dev/null +++ b/app/utils/validation.ts @@ -0,0 +1,6 @@ +export function validatePhoneNumber(phone: string): boolean { + // 这里使用一个简单的中国大陆手机号码验证规则 + // 你可能需要根据具体需求调整这个正则表达式 + const phoneRegex = /^1[3-9]\d{9}$/; + return phoneRegex.test(phone); +}