mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
feat: add mise config for web
This commit is contained in:
+4
-1
@@ -1,5 +1,8 @@
|
||||
[task_config]
|
||||
dir = "{{cwd}}"
|
||||
|
||||
[tasks.start]
|
||||
alias = "dev"
|
||||
description = "Start server"
|
||||
run = "go run main.go"
|
||||
run = "go run cmd/agent/main.go"
|
||||
depends = ["//:go-install"]
|
||||
|
||||
@@ -29,6 +29,7 @@ description = "Start development environment"
|
||||
depends = [
|
||||
"//agent:dev",
|
||||
"//cmd/agent:start",
|
||||
"//packages/web:dev",
|
||||
]
|
||||
|
||||
[tasks.setup]
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
import { db } from '@memoh/db'
|
||||
import { users, settings } from '@memoh/db/schema'
|
||||
import { eq } from 'drizzle-orm'
|
||||
|
||||
/**
|
||||
* 验证用户凭据
|
||||
* 优先检查是否为 ROOT 用户,否则查询数据库
|
||||
*/
|
||||
export const validateUser = async (username: string, password: string) => {
|
||||
// 检查是否为 ROOT 用户
|
||||
const rootUser = process.env.ROOT_USER
|
||||
const rootPassword = process.env.ROOT_USER_PASSWORD
|
||||
|
||||
let userId: string | null = null
|
||||
|
||||
if (rootUser && rootPassword && username === rootUser) {
|
||||
if (password === rootPassword) {
|
||||
// 检查 root 用户是否存在于数据库中
|
||||
const [existingUser] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, rootUser))
|
||||
|
||||
userId = existingUser?.id
|
||||
|
||||
if (!existingUser) {
|
||||
// 为 root 用户创建数据库记录
|
||||
// 使用占位符密码哈希,因为实际密码在环境变量中
|
||||
|
||||
const [newUser] = await db
|
||||
.insert(users)
|
||||
.values({
|
||||
username: rootUser,
|
||||
passwordHash: 'ENV_BASED_AUTH', // 占位符,实际使用环境变量验证
|
||||
role: 'admin',
|
||||
displayName: 'Root User',
|
||||
email: null,
|
||||
avatarUrl: null,
|
||||
isActive: true,
|
||||
})
|
||||
.onConflictDoNothing() // 避免并发创建导致的冲突
|
||||
.returning({
|
||||
id: users.id,
|
||||
})
|
||||
|
||||
userId = newUser.id
|
||||
}
|
||||
|
||||
// 检查 root 用户的 settings 是否存在,不存在则创建
|
||||
const [existingSettings] = await db
|
||||
.select()
|
||||
.from(settings)
|
||||
.where(eq(settings.userId, userId))
|
||||
|
||||
if (!existingSettings) {
|
||||
// 为 root 用户创建默认 settings
|
||||
await db
|
||||
.insert(settings)
|
||||
.values({
|
||||
userId: userId,
|
||||
defaultChatModel: null,
|
||||
defaultEmbeddingModel: null,
|
||||
defaultSummaryModel: null,
|
||||
maxContextLoadTime: 60,
|
||||
language: 'Same as user input',
|
||||
})
|
||||
.onConflictDoNothing() // 避免并发创建导致的冲突
|
||||
}
|
||||
|
||||
// 返回 ROOT 用户信息
|
||||
return {
|
||||
id: userId,
|
||||
username: rootUser,
|
||||
role: 'admin' as const,
|
||||
displayName: 'Root User',
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
// 查询数据库中的用户(使用 username 而不是 id)
|
||||
const [user] = await db
|
||||
.select()
|
||||
.from(users)
|
||||
.where(eq(users.username, username))
|
||||
|
||||
if (!user) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 验证密码 (这里使用简单的 Bun.password.verify)
|
||||
const isValid = await Bun.password.verify(password, user.passwordHash)
|
||||
|
||||
if (!isValid) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 检查账户是否激活
|
||||
if (!user.isActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
// 更新最后登录时间
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
lastLoginAt: new Date(),
|
||||
})
|
||||
.where(eq(users.id, user.id))
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
role: user.role,
|
||||
displayName: user.displayName || user.username,
|
||||
email: user.email,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
import Elysia from 'elysia'
|
||||
import { adminMiddleware } from '../../middlewares'
|
||||
import {
|
||||
GetUserByIdModel,
|
||||
CreateUserModel,
|
||||
UpdateUserModel,
|
||||
DeleteUserModel,
|
||||
UpdatePasswordModel,
|
||||
} from './model'
|
||||
import {
|
||||
getUsers,
|
||||
getUserById,
|
||||
createUser,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
updateUserPassword,
|
||||
} from './service'
|
||||
|
||||
export const userModule = new Elysia({
|
||||
prefix: '/user',
|
||||
})
|
||||
// 使用管理员中间件保护所有路由
|
||||
.use(adminMiddleware)
|
||||
// Get all users
|
||||
.get('/', async ({ query }) => {
|
||||
try {
|
||||
const page = parseInt(query.page as string) || 1
|
||||
const limit = parseInt(query.limit as string) || 10
|
||||
const sortBy = query.sortBy as string || 'createdAt'
|
||||
const sortOrder = (query.sortOrder as string) || 'desc'
|
||||
|
||||
const result = await getUsers({
|
||||
page,
|
||||
limit,
|
||||
sortBy,
|
||||
sortOrder: sortOrder as 'asc' | 'desc',
|
||||
})
|
||||
|
||||
return {
|
||||
success: true,
|
||||
...result,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to fetch users',
|
||||
}
|
||||
}
|
||||
})
|
||||
// Get user by ID
|
||||
.get('/:id', async ({ params, set }) => {
|
||||
try {
|
||||
const { id } = params
|
||||
const user = await getUserById(id)
|
||||
|
||||
if (!user) {
|
||||
set.status = 404
|
||||
return {
|
||||
success: false,
|
||||
error: 'User not found',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: user,
|
||||
}
|
||||
} catch (error) {
|
||||
set.status = 500
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to fetch user',
|
||||
}
|
||||
}
|
||||
}, GetUserByIdModel)
|
||||
// Create new user
|
||||
.post('/', async ({ body, set }) => {
|
||||
try {
|
||||
const newUser = await createUser(body)
|
||||
set.status = 201
|
||||
return {
|
||||
success: true,
|
||||
data: newUser,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && (
|
||||
error.message.includes('already exists')
|
||||
)) {
|
||||
set.status = 409
|
||||
} else {
|
||||
set.status = 500
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to create user',
|
||||
}
|
||||
}
|
||||
}, CreateUserModel)
|
||||
// Update user
|
||||
.put('/:id', async ({ params, body, set }) => {
|
||||
try {
|
||||
const { id } = params
|
||||
const updatedUser = await updateUser(id, body)
|
||||
|
||||
if (!updatedUser) {
|
||||
set.status = 404
|
||||
return {
|
||||
success: false,
|
||||
error: 'User not found',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: updatedUser,
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.includes('already exists')) {
|
||||
set.status = 409
|
||||
} else {
|
||||
set.status = 500
|
||||
}
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to update user',
|
||||
}
|
||||
}
|
||||
}, UpdateUserModel)
|
||||
// Delete user
|
||||
.delete('/:id', async ({ params, set }) => {
|
||||
try {
|
||||
const { id } = params
|
||||
const deletedUser = await deleteUser(id)
|
||||
|
||||
if (!deletedUser) {
|
||||
set.status = 404
|
||||
return {
|
||||
success: false,
|
||||
error: 'User not found',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: deletedUser,
|
||||
}
|
||||
} catch (error) {
|
||||
set.status = 500
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to delete user',
|
||||
}
|
||||
}
|
||||
}, DeleteUserModel)
|
||||
// Update user password
|
||||
.patch('/:id/password', async ({ params, body, set }) => {
|
||||
try {
|
||||
const { id } = params
|
||||
const updatedUser = await updateUserPassword(id, body.password)
|
||||
|
||||
if (!updatedUser) {
|
||||
set.status = 404
|
||||
return {
|
||||
success: false,
|
||||
error: 'User not found',
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: updatedUser,
|
||||
message: 'Password updated successfully',
|
||||
}
|
||||
} catch (error) {
|
||||
set.status = 500
|
||||
return {
|
||||
success: false,
|
||||
error: error instanceof Error ? error.message : 'Failed to update password',
|
||||
}
|
||||
}
|
||||
}, UpdatePasswordModel)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[tasks.dev]
|
||||
alias = "dev"
|
||||
description = "Start web development server"
|
||||
run = "pnpm dev"
|
||||
depends = ["//:pnpm-install"]
|
||||
|
||||
[tasks.build]
|
||||
description = "Build web"
|
||||
run = "pnpm build"
|
||||
depends = ["//:pnpm-install"]
|
||||
|
||||
[tasks.start]
|
||||
description = "Start web"
|
||||
run = "pnpm start"
|
||||
Reference in New Issue
Block a user