109 lines
3.3 KiB
TypeScript
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 }
|
|
);
|
|
}
|
|
}
|