feat: add mise config for web

This commit is contained in:
Acbox
2026-01-29 15:49:51 +08:00
parent bfea20482b
commit 54c9143a9c
5 changed files with 19 additions and 302 deletions
+4 -1
View File
@@ -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"]
+1
View File
@@ -29,6 +29,7 @@ description = "Start development environment"
depends = [
"//agent:dev",
"//cmd/agent:start",
"//packages/web:dev",
]
[tasks.setup]
-119
View File
@@ -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,
}
}
-182
View File
@@ -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)
+14
View File
@@ -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"