用户资料/重置密码/用户管理
This commit is contained in:
1 parent
5c1f640a2b
commit
813407de30
14 files changed
+591
-2
No files matched your search
@@ -0,0 +1,29 @@
|
||||
import { requireAdminAuth } from '../../../../utils/access'
|
||||
import { createApiError } from '../../../../utils/api-error'
|
||||
import { clearUserPassword, findUserById } from '../../../../utils/db'
|
||||
import { logActivity } from '../../../../utils/activity-log'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const operator = await requireAdminAuth(event)
|
||||
|
||||
const id = Number(getRouterParam(event, 'id'))
|
||||
const target = Number.isInteger(id) ? findUserById(id) : null
|
||||
if (!target) {
|
||||
createApiError(event, 'INVALID_REQUEST', '用户不存在', 404)
|
||||
}
|
||||
if (target.id === operator.id) {
|
||||
createApiError(event, 'INVALID_REQUEST', '不能清除自己的密码', 400)
|
||||
}
|
||||
|
||||
clearUserPassword(target.id)
|
||||
|
||||
logActivity(event, {
|
||||
action: 'settings',
|
||||
key: `users/${target.id}/clear-password`,
|
||||
originalName: target.username,
|
||||
userId: operator.id,
|
||||
source: 'admin'
|
||||
})
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
@@ -0,0 +1,7 @@
|
||||
import { requireAdminAuth } from '../../../utils/access'
|
||||
import { listAdminUsers } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
await requireAdminAuth(event)
|
||||
return { users: listAdminUsers() }
|
||||
})
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
rowToAuthUser,
|
||||
verifyPassword
|
||||
} from '../../utils/auth'
|
||||
import { findUserByUsername } from '../../utils/db'
|
||||
import { findUserByUsername, isPasswordResetRequired } from '../../utils/db'
|
||||
import { getAdminSecret } from '../../utils/env'
|
||||
import { verifyLoginVerification, type VerificationBody } from '../../utils/login-verification'
|
||||
import { logActivity } from '../../utils/activity-log'
|
||||
@@ -56,6 +56,12 @@ export default defineEventHandler(async (event) => {
|
||||
}
|
||||
|
||||
const user = findUserByUsername(username)
|
||||
|
||||
// 管理员已清除密码:下次登录时直接进入设置新密码流程
|
||||
if (user && isPasswordResetRequired(user.id)) {
|
||||
return { success: false, requiresPasswordReset: true, username: user.username }
|
||||
}
|
||||
|
||||
if (!user || !verifyPassword(password, user.password_hash)) {
|
||||
logWarn('login failed: invalid credentials', { ip, username })
|
||||
createApiError(event, 'UNAUTHORIZED', '用户名或密码错误', 401)
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createApiError } from '../../utils/api-error'
|
||||
import { createSession, hashPassword } from '../../utils/auth'
|
||||
import {
|
||||
findUserByUsername,
|
||||
isPasswordResetRequired,
|
||||
setUserPassword
|
||||
} from '../../utils/db'
|
||||
import { logInfo } from '../../utils/logger'
|
||||
|
||||
interface ResetBody {
|
||||
username?: string
|
||||
password?: string
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody<ResetBody>(event).catch((): ResetBody => ({}))
|
||||
const username = body.username?.trim() ?? ''
|
||||
const password = body.password ?? ''
|
||||
|
||||
if (!username || password.length < 6) {
|
||||
createApiError(event, 'INVALID_REQUEST', '请输入用户名和至少 6 位的新密码', 400)
|
||||
}
|
||||
|
||||
const user = findUserByUsername(username)
|
||||
if (!user || !isPasswordResetRequired(user.id)) {
|
||||
createApiError(event, 'UNAUTHORIZED', '该账号无需重置密码', 401)
|
||||
}
|
||||
|
||||
setUserPassword(user.id, await hashPassword(password))
|
||||
await createSession(event, user.id)
|
||||
logInfo('password reset completed', { username })
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
@@ -0,0 +1,23 @@
|
||||
import { requireUserAuth } from '../../utils/access'
|
||||
import { createApiError } from '../../utils/api-error'
|
||||
import { findUserByUsername, updateUsername } from '../../utils/db'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const user = await requireUserAuth(event)
|
||||
const body = await readBody<{ username?: unknown }>(event).catch(
|
||||
(): { username?: unknown } => ({})
|
||||
)
|
||||
|
||||
const username = typeof body.username === 'string' ? body.username.trim() : ''
|
||||
if (!/^[a-zA-Z0-9_.-]{2,32}$/.test(username)) {
|
||||
createApiError(event, 'INVALID_REQUEST', '用户名需为 2–32 位字母、数字、下划线、点或短横线', 400)
|
||||
}
|
||||
|
||||
const existing = findUserByUsername(username)
|
||||
if (existing && existing.id !== user.id) {
|
||||
createApiError(event, 'CONFLICT', '用户名已被占用', 409)
|
||||
}
|
||||
|
||||
updateUsername(user.id, username)
|
||||
return { success: true, user: { ...user, username } }
|
||||
})
|
||||
Reference in new issue
Block a user