마에스트로 상세화면에 암호 초기화 버튼 추가 (암호 초기화: 123456)

This commit is contained in:
2026-07-05 13:48:17 +09:00
parent a15543859e
commit ff67e5cf5a
3 changed files with 107 additions and 2 deletions
@@ -2,7 +2,7 @@
import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { Save } from "lucide-react";
import { KeyRound, Save } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
@@ -41,6 +41,7 @@ export function MaestroEditForm({ maestro, playerCount }: MaestroEditFormProps)
const router = useRouter();
const [isPending, startTransition] = useTransition();
const [isSaving, setIsSaving] = useState(false);
const [isResettingPassword, setIsResettingPassword] = useState(false);
const [name, setName] = useState(maestro.name);
const [email, setEmail] = useState(maestro.email);
@@ -56,7 +57,7 @@ export function MaestroEditForm({ maestro, playerCount }: MaestroEditFormProps)
const [errorMessage, setErrorMessage] = useState("");
const [successMessage, setSuccessMessage] = useState("");
const isDisabled = isSaving || isPending;
const isDisabled = isSaving || isPending || isResettingPassword;
async function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault();
@@ -107,6 +108,42 @@ export function MaestroEditForm({ maestro, playerCount }: MaestroEditFormProps)
}
}
async function handleResetPassword() {
if (
!window.confirm(
`${maestro.name} 계정의 비밀번호를 123456으로 초기화 하시겠습니까?`
)
)
return;
setIsResettingPassword(true);
setErrorMessage("");
setSuccessMessage("");
try {
const response = await fetch(
`/api/maestros/${maestro.maestroID}/reset-password`,
{ method: "POST" }
);
const body = (await response.json().catch(() => null)) as {
message?: string;
} | null;
if (!response.ok) {
throw new Error(body?.message ?? "암호 초기화에 실패했습니다.");
}
setSuccessMessage("암호 초기화 완료");
} catch (error) {
setErrorMessage(
error instanceof Error ? error.message : "암호 초기화에 실패했습니다."
);
} finally {
setIsResettingPassword(false);
}
}
return (
<form onSubmit={(e) => void handleSubmit(e)} className="mt-4 space-y-5">
<div className="grid gap-x-8 gap-y-4 text-sm md:grid-cols-2 xl:grid-cols-3">
@@ -216,6 +253,16 @@ export function MaestroEditForm({ maestro, playerCount }: MaestroEditFormProps)
<Save aria-hidden="true" className="size-4" />
</Button>
<Button
disabled={isDisabled}
onClick={() => void handleResetPassword()}
size="sm"
type="button"
variant="outline"
>
<KeyRound aria-hidden="true" className="size-4" />
</Button>
{successMessage ? (
<p className="text-sm text-green-700 dark:text-green-400">
{successMessage}
@@ -0,0 +1,33 @@
import { NextResponse } from "next/server";
import { resetMaestroPassword } from "@/lib/maestros";
import { ApiError } from "@/lib/errors";
import { withApiHandler } from "@/lib/api-handler";
import { logger } from "@/lib/logger";
const CTX = "maestros/[id]/reset-password";
export const POST = withApiHandler<{ id: string }>(
CTX,
async (_request, { params, t0 }) => {
const { id } = await params;
const maestroID = Number(id);
if (!Number.isInteger(maestroID) || maestroID <= 0) {
throw new ApiError("Invalid maestro id", 400);
}
const found = await resetMaestroPassword(maestroID);
if (!found) {
throw new ApiError("Not found", 404);
}
logger.info(CTX, "password reset", {
id: maestroID,
duration: Date.now() - t0,
});
return NextResponse.json({ success: true });
}
);
+25
View File
@@ -255,6 +255,31 @@ export async function updateMaestro(
return { changed: true };
}
export async function resetMaestroPassword(
maestroID: number
): Promise<boolean> {
const maestro = await db.maestro.findUnique({
where: { MaestroID: maestroID },
select: { MaestroID: true },
});
if (!maestro) return false;
await db.$transaction(async (tx) => {
await tx.$executeRaw`UPDATE maestro SET Password = PASSWORD('123456') WHERE MaestroID = ${maestroID}`;
await tx.maestro_log.create({
data: {
Type: "reset_maestro_password",
MaestroID: maestroID,
LogDateTime: new Date(),
Remark: "admin reset to 123456",
},
});
});
return true;
}
const logPageSizeOptions = [10, 20, 50] as const;
type LogPageSize = (typeof logPageSizeOptions)[number];