mirror of
https://github.com/beilunyang/moemail.git
synced 2026-08-12 00:44:02 +08:00
feat: Support finding and promoting roles by username
This commit is contained in:
@@ -1,43 +1,49 @@
|
||||
import { createDb } from "@/lib/db"
|
||||
import { userRoles, users } from "@/lib/schema"
|
||||
import { users } from "@/lib/schema"
|
||||
import { eq } from "drizzle-orm"
|
||||
|
||||
export const runtime = "edge"
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const url = new URL(request.url)
|
||||
const email = url.searchParams.get('email')
|
||||
export async function POST(request: Request) {
|
||||
try {
|
||||
const json = await request.json()
|
||||
const { searchText } = json as { searchText: string }
|
||||
|
||||
if (!email) {
|
||||
if (!searchText) {
|
||||
return Response.json({ error: "请提供用户名或邮箱地址" }, { status: 400 })
|
||||
}
|
||||
|
||||
const db = createDb()
|
||||
|
||||
const user = await db.query.users.findFirst({
|
||||
where: searchText.includes('@') ? eq(users.email, searchText) : eq(users.username, searchText),
|
||||
with: {
|
||||
userRoles: {
|
||||
with: {
|
||||
role: true
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
return Response.json({ error: "未找到用户" }, { status: 404 })
|
||||
}
|
||||
|
||||
return Response.json({
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
role: user.userRoles[0]?.role.name
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error("Failed to find user:", error)
|
||||
return Response.json(
|
||||
{ error: "邮箱地址不能为空" },
|
||||
{ status: 400 }
|
||||
{ error: "查询用户失败" },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
const db = createDb()
|
||||
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.email, email),
|
||||
})
|
||||
|
||||
if (!user) {
|
||||
return Response.json({ user: null })
|
||||
}
|
||||
|
||||
const userRole = await db.query.userRoles.findFirst({
|
||||
where: eq(userRoles.userId, user.id),
|
||||
with: {
|
||||
role: true
|
||||
}
|
||||
})
|
||||
|
||||
return Response.json({
|
||||
user: {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
role: userRole?.role.name
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -58,7 +58,9 @@ export function ProfileCard({ user }: ProfileCardProps) {
|
||||
}
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground truncate mt-1">
|
||||
{user.email}
|
||||
{
|
||||
user.email ? user.email : `用户名: ${user.username}`
|
||||
}
|
||||
</p>
|
||||
{user.roles && (
|
||||
<div className="flex gap-2 mt-2">
|
||||
|
||||
@@ -8,20 +8,34 @@ import { useToast } from "@/components/ui/use-toast"
|
||||
import { ROLES } from "@/lib/permissions"
|
||||
|
||||
export function PromotePanel() {
|
||||
const [email, setEmail] = useState("")
|
||||
const [searchText, setSearchText] = useState("")
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [action, setAction] = useState<"promote" | "demote">("promote")
|
||||
const { toast } = useToast()
|
||||
|
||||
const handleAction = async () => {
|
||||
if (!email) return
|
||||
if (!searchText) return
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await fetch(`/api/roles/users?email=${encodeURIComponent(email)}`)
|
||||
const data = await res.json() as { user?: { id: string; name?: string; email: string; role?: string }; error?: string }
|
||||
const res = await fetch("/api/roles/users", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ searchText })
|
||||
})
|
||||
const data = await res.json() as {
|
||||
user?: {
|
||||
id: string
|
||||
name?: string
|
||||
username?: string
|
||||
email: string
|
||||
role?: string
|
||||
}
|
||||
error?: string
|
||||
}
|
||||
|
||||
if (!res.ok) throw new Error(data.error || "未知错误")
|
||||
|
||||
if (!res.ok) throw new Error(data.error || '未知错误')
|
||||
if (!data.user) {
|
||||
toast({
|
||||
title: "未找到用户",
|
||||
@@ -61,9 +75,9 @@ export function PromotePanel() {
|
||||
|
||||
toast({
|
||||
title: action === "promote" ? "册封成功" : "贬为平民",
|
||||
description: `已将 ${data.user.email} ${action === "promote" ? "册封为骑士" : "贬为平民"}`
|
||||
description: `已将 ${data.user.email || data.user.username} ${action === "promote" ? "册封为骑士" : "贬为平民"}`
|
||||
})
|
||||
setEmail("")
|
||||
setSearchText("")
|
||||
|
||||
} catch (error) {
|
||||
toast({
|
||||
@@ -86,9 +100,9 @@ export function PromotePanel() {
|
||||
<div className="flex flex-col sm:flex-row gap-2">
|
||||
<div className="flex-1">
|
||||
<Input
|
||||
placeholder="输入用户邮箱"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="输入用户名或邮箱"
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
disabled={loading}
|
||||
/>
|
||||
</div>
|
||||
@@ -98,7 +112,7 @@ export function PromotePanel() {
|
||||
setAction("promote")
|
||||
handleAction()
|
||||
}}
|
||||
disabled={!email || loading}
|
||||
disabled={!searchText || loading}
|
||||
className="flex-1 sm:flex-initial gap-2"
|
||||
>
|
||||
{loading && action === "promote" ? (
|
||||
@@ -113,7 +127,7 @@ export function PromotePanel() {
|
||||
setAction("demote")
|
||||
handleAction()
|
||||
}}
|
||||
disabled={!email || loading}
|
||||
disabled={!searchText || loading}
|
||||
variant="destructive"
|
||||
className="flex-1 sm:flex-initial gap-2"
|
||||
>
|
||||
|
||||
@@ -85,10 +85,6 @@ export const roles = sqliteTable("role", {
|
||||
updatedAt: integer("updated_at", { mode: "timestamp" }).$defaultFn(() => new Date()),
|
||||
});
|
||||
|
||||
export const rolesRelations = relations(roles, ({ many }) => ({
|
||||
userRoles: many(userRoles),
|
||||
}));
|
||||
|
||||
export const userRoles = sqliteTable("user_role", {
|
||||
userId: text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }),
|
||||
roleId: text("role_id").notNull().references(() => roles.id, { onDelete: "cascade" }),
|
||||
@@ -106,4 +102,12 @@ export const userRolesRelations = relations(userRoles, ({ one }) => ({
|
||||
fields: [userRoles.roleId],
|
||||
references: [roles.id],
|
||||
}),
|
||||
}));
|
||||
}));
|
||||
|
||||
export const usersRelations = relations(users, ({ many }) => ({
|
||||
userRoles: many(userRoles),
|
||||
}));
|
||||
|
||||
export const rolesRelations = relations(roles, ({ many }) => ({
|
||||
userRoles: many(userRoles),
|
||||
}));
|
||||
Reference in New Issue
Block a user