Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions src/app/api/change-password/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,32 @@ export async function POST(request: NextRequest) {

try {
const body = await request.json();
const { newPassword } = body;

// 获取认证信息
const { newPassword, oldPassword } = body;
const authInfo = getAuthInfoFromCookie(request);
if (!authInfo || !authInfo.username) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}

// 验证新密码
if (!newPassword || typeof newPassword !== 'string') {
return NextResponse.json({ error: '新密码不得为空' }, { status: 400 });
if (!oldPassword || typeof oldPassword !== 'string') {
return NextResponse.json({ error: '旧密码不得为空' }, { status: 400 });
}
if (!newPassword || typeof newPassword !== 'string' || newPassword.trim().length < 6) {
return NextResponse.json({ error: '新密码长度至少为6位' }, { status: 400 });
}
if (oldPassword === newPassword) {
return NextResponse.json({ error: '新密码不能与旧密码相同' }, { status: 400 });
}

const username = authInfo.username;

// 不允许站长修改密码(站长用户名等于 process.env.USERNAME)
if (username === process.env.USERNAME) {
return NextResponse.json(
{ error: '站长不能通过此接口修改密码' },
{ status: 403 }
);
}

// 修改密码
const valid = await db.verifyUser(username, oldPassword);
if (!valid) {
return NextResponse.json({ error: '旧密码错误' }, { status: 401 });
}
await db.changePassword(username, newPassword);

return NextResponse.json({ ok: true });
Expand Down
50 changes: 29 additions & 21 deletions src/components/UserMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export const UserMenu: React.FC = () => {
{ value: 'custom', label: '自定义代理' },
];

// 修改密码相关状态
const [oldPassword, setOldPassword] = useState('');
const [newPassword, setNewPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [passwordLoading, setPasswordLoading] = useState(false);
Expand Down Expand Up @@ -277,53 +277,55 @@ export const UserMenu: React.FC = () => {
const handleChangePassword = () => {
setIsOpen(false);
setIsChangePasswordOpen(true);
setOldPassword('');
setNewPassword('');
setConfirmPassword('');
setPasswordError('');
};

const handleCloseChangePassword = () => {
setIsChangePasswordOpen(false);
setOldPassword('');
setNewPassword('');
setConfirmPassword('');
setPasswordError('');
};

const handleSubmitChangePassword = async () => {
setPasswordError('');

// 验证密码
if (!newPassword) {
setPasswordError('新密码不得为空');
if (!oldPassword) {
setPasswordError('旧密码不得为空');
return;
}
if (!newPassword || newPassword.trim().length < 6) {
setPasswordError('新密码长度至少为6位');
return;
}
if (oldPassword === newPassword) {
setPasswordError('新密码不能与旧密码相同');
return;
}

if (newPassword !== confirmPassword) {
setPasswordError('两次输入的密码不一致');
return;
}

setPasswordLoading(true);

try {
const response = await fetch('/api/change-password', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
oldPassword,
newPassword,
}),
});

const data = await response.json();

if (!response.ok) {
setPasswordError(data.error || '修改密码失败');
return;
}

// 修改成功,关闭弹窗并登出
setIsChangePasswordOpen(false);
await handleLogout();
} catch (error) {
Expand Down Expand Up @@ -1021,24 +1023,33 @@ export const UserMenu: React.FC = () => {
</button>
</div>

{/* 表单 */}
<div className='space-y-4'>
{/* 新密码输入 */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
旧密码
</label>
<input
type='password'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-colors bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400'
placeholder='请输入旧密码'
value={oldPassword}
onChange={(e) => setOldPassword(e.target.value)}
disabled={passwordLoading}
/>
</div>
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
新密码
</label>
<input
type='password'
className='w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent transition-colors bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 placeholder-gray-500 dark:placeholder-gray-400'
placeholder='请输入新密码'
placeholder='请输入新密码,至少6位'
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
disabled={passwordLoading}
/>
</div>

{/* 确认密码输入 */}
<div>
<label className='block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2'>
确认密码
Expand All @@ -1053,15 +1064,12 @@ export const UserMenu: React.FC = () => {
/>
</div>

{/* 错误信息 */}
{passwordError && (
<div className='text-red-500 text-sm bg-red-50 dark:bg-red-900/20 p-3 rounded-md border border-red-200 dark:border-red-800'>
{passwordError}
</div>
)}
</div>

{/* 操作按钮 */}
<div className='flex gap-3 mt-6 pt-4 border-t border-gray-200 dark:border-gray-700'>
<button
onClick={handleCloseChangePassword}
Expand All @@ -1073,7 +1081,7 @@ export const UserMenu: React.FC = () => {
<button
onClick={handleSubmitChangePassword}
className='flex-1 px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700 dark:bg-green-700 dark:hover:bg-green-600 rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed'
disabled={passwordLoading || !newPassword || !confirmPassword}
disabled={passwordLoading || !oldPassword || !newPassword || !confirmPassword}
>
{passwordLoading ? '修改中...' : '确认修改'}
</button>
Expand Down