childcafe/src/app/api/reserve/route.ts
2025-10-11 17:31:24 +09:00

109 lines
3.3 KiB
TypeScript

// src/app/api/reserve/route.ts
import { NextResponse, type NextRequest } from "next/server";
import { prisma } from "@/lib/prisma";
import { reserveServerSchema } from "@/lib/validators/reserveServerValidator";
import { randomUUID } from "crypto";
import { checkReserveLimit } from "@/lib/validators/reserveLimitValidator";
import { sendMail } from "@/lib/mail";
import { createReserveMail, createAdminNotifyMail } from "./mailTemplate";
export async function POST(req: NextRequest) {
try {
const body = await req.json();
const data = reserveServerSchema.parse(body); // 型は Zod から推論
// --- 1. 予約制限のチェック(既存ロジック維持)---
const limitResult = await checkReserveLimit({
reserve_num: data.reserve_num,
reserve_time: data.reserve_time,
});
if (!limitResult.ok) {
return NextResponse.json(
{ success: false, error: limitResult.message },
{ status: 409 }
);
}
// --- 2. 重複チェック(既存ロジック維持)---
const exists = await prisma.reserveCustomer.findFirst({
where: {
reserve_num: data.reserve_num,
cust_email: data.cust_email,
cancel_flag: 0,
},
});
if (exists) {
return NextResponse.json(
{
success: false,
error: "既に予約されています。\nキャンセルをご希望の場合はLINEにてメールアドレスとキャンセル希望の旨をメッセージください",
},
{ status: 409 }
);
}
// --- 3. ワンタイム thanks_token 発行(既存ロジック維持)---
const token = randomUUID();
// await prisma.reserveCustomer.create({
// data: {
// reserve_num: data.reserve_num,
// cust_name: data.cust_name,
// cust_parent: data.cust_parent,
// cust_child: data.cust_child,
// reserve_time: data.reserve_time,
// cust_email: data.cust_email,
// thanks_token: token, // ← 既存通り thanks_token に保存
// },
// });
const setting = await prisma.reserveSetting.findUnique({
where: { autonum: data.reserve_num },
select: { diner_date: true },
});
data.thanks_token = token;
const reserve = await prisma.reserveCustomer.create({ data });
// --- メール本文生成 ---
const mail = createReserveMail({ ...reserve, ...data, event_date: setting?.diner_date });
// --- メール送信 ---
try {
await sendMail({
from: `みんなのBettakuごはん子ども食堂 <bettaku@basecafe.jp>`,
to: data.cust_email,
subject: mail.subject,
html: mail.text.replace(/\n/g, "<br>"), // ✅ HTML形式対応
text: mail.text,
});
} catch (mailError) {
console.error("❌ メール送信エラー:", mailError);
// メール送信に失敗しても予約登録は成功として扱う
}
// 管理者宛
const adminMail = createAdminNotifyMail({
...reserve,
...data,
event_date: setting?.diner_date,
});
await sendMail({
from: `bc-sys <corp@basecafe.jp>`,
to: process.env.MAIL_ADMIN ?? "",
subject: adminMail.subject,
html: adminMail.text.replace(/\n/g, "<br>"),
text: adminMail.text,
});
// --- 4. 返り値形式(既存仕様を厳守)---
return NextResponse.json({ success: true, token });
} catch (error: unknown) {
console.error("reserve POST error:", error);
return NextResponse.json(
{ success: false, error: "予約処理に失敗しました" },
{ status: 500 }
);
}
}