fix(auth): improve OTP error handling and add health check endpoint

Option 2 - Improve Error Handling:
- Track WA success status and error messages in login route
- Return debug info (including OTP code) only in non-production
- Show descriptive message when WhatsApp fails to send
- Better error categorization (HTTP error vs logic error vs connection)

Option 3 - Health Check Endpoint:
- Create /api/health/otp endpoint for OTP service diagnostics
- Support test mode with query params: ?test=true&number=6281234567890
- Check token configuration (configured vs placeholder)
- Measure response time and validate service response
- Return comprehensive status for debugging OTP issues

Usage:
- Basic check: GET /api/health/otp
- Test send: GET /api/health/otp?test=true&number=6281234567890

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-04-06 16:27:29 +08:00
parent 3de412afe0
commit feb853d06e
2 changed files with 188 additions and 7 deletions

View File

@@ -29,6 +29,9 @@ export async function POST(req: Request) {
console.log(`🔑 DEBUG OTP [${nomor}]: ${codeOtp}`);
let waSuccess = false;
let waErrorMessage = "";
try {
const waResponse = await sendCodeOtp({
nomor,
@@ -36,25 +39,29 @@ export async function POST(req: Request) {
});
if (!waResponse.ok) {
console.error(
`⚠️ WA Service HTTP Error: ${waResponse.status} ${waResponse.statusText}. Continuing since OTP is logged.`
);
waErrorMessage = `WA Service HTTP Error: ${waResponse.status} ${waResponse.statusText}`;
console.error(`⚠️ ${waErrorMessage}. Continuing since OTP is logged.`);
console.log(`💡 Use this OTP to login: ${codeOtp}`);
} else {
const sendWa = await waResponse.json();
console.log("📱 WA Response:", sendWa);
if (sendWa.status !== "success") {
console.error("⚠️ WA Service Logic Error:", sendWa);
waErrorMessage = `WA Service Logic Error: ${JSON.stringify(sendWa)}`;
console.error("⚠️", waErrorMessage);
} else {
waSuccess = true;
}
}
} catch (waError: unknown) {
const errorMessage =
waError instanceof Error ? waError.message : String(waError);
waErrorMessage = `WA Connection Exception: ${errorMessage}`;
console.error(
"⚠️ WA Connection Exception. Continuing since OTP is logged.",
errorMessage
"⚠️",
waErrorMessage,
". Continuing since OTP is logged."
);
}
@@ -71,11 +78,25 @@ export async function POST(req: Request) {
path: "/",
});
// Include debug info for non-production environments when WA fails
const isNonProd = process.env.NODE_ENV !== "production";
const includeDebug = isNonProd && !waSuccess;
return NextResponse.json({
success: true,
message: "Kode verifikasi dikirim",
message: waSuccess
? "Kode verifikasi dikirim"
: "Kode verifikasi dibuat (WhatsApp gagal terkirim)",
kodeId: createOtpId.id,
isRegistered: true,
waSuccess,
...(includeDebug && {
debug: {
codeOtp,
waErrorMessage,
note: "Hanya muncul di development/staging. Hapus di production.",
},
}),
});
} else {
return NextResponse.json({

View File

@@ -0,0 +1,160 @@
// app/api/health/otp/route.ts
import { NextResponse } from "next/server";
import { randomOTP } from "@/app/api/auth/_lib/randomOTP";
import { sendCodeOtp } from "@/app/api/auth/_lib/sendCodeOtp";
/**
* Health check endpoint untuk OTP WhatsApp service
*
* GET /api/health/otp?test=true&number=6281234567890
*
* Query parameters:
* - test: Set "true" untuk kirim test OTP (optional)
* - number: Nomor tujuan test (required jika test=true)
*
* Response:
* - Status OTP service (available/unavailable)
* - Token configuration status
* - Test message results (jika test=true)
*/
export async function GET(req: Request) {
try {
const { searchParams } = new URL(req.url);
const isTest = searchParams.get("test") === "true";
const testNumber = searchParams.get("number");
// Check token configuration
const tokenConfigured = Boolean(process.env.WA_SERVER_TOKEN);
const isPlaceholder =
process.env.WA_SERVER_TOKEN === "your_whatsapp_server_token";
const healthCheck = {
service: "OTP WhatsApp",
status: "ok" as "ok" | "degraded" | "unavailable",
token: {
configured: tokenConfigured,
isPlaceholder,
},
timestamp: new Date().toISOString(),
};
// If test mode, actually try to send OTP
if (isTest) {
if (!testNumber) {
return NextResponse.json(
{
success: false,
message: "Nomor diperlukan untuk test mode",
error: "Tambahkan query parameter: ?number=6281234567890",
},
{ status: 400 }
);
}
const testOtp = randomOTP();
console.log(`🧪 OTP HEALTH CHECK - Testing with number: ${testNumber}`);
console.log(`🧪 Test OTP code: ${testOtp}`);
try {
const startTime = Date.now();
const waResponse = await sendCodeOtp({
nomor: testNumber,
codeOtp: testOtp,
});
const responseTime = Date.now() - startTime;
if (!waResponse.ok) {
healthCheck.status = "unavailable";
return NextResponse.json({
success: false,
message: "Gagal mengirim OTP test",
health: healthCheck,
test: {
number: testNumber,
httpStatus: waResponse.status,
statusText: waResponse.statusText,
responseTime: `${responseTime}ms`,
note: "Cek apakah WA_SERVER_TOKEN sudah benar di Portainer",
},
});
}
const responseData = await waResponse.json();
if (responseData.status !== "success") {
healthCheck.status = "degraded";
return NextResponse.json({
success: false,
message: "OTP test terkirim tapi service merespon error",
health: healthCheck,
test: {
number: testNumber,
httpStatus: waResponse.status,
responseTime: `${responseTime}ms`,
serviceResponse: responseData,
note: "Cek apakah WA_SERVER_TOKEN sudah benar di Portainer",
},
});
}
// Success
healthCheck.status = "ok";
return NextResponse.json({
success: true,
message: "OTP test berhasil dikirim",
health: healthCheck,
test: {
number: testNumber,
httpStatus: waResponse.status,
responseTime: `${responseTime}ms`,
serviceResponse: responseData,
otpCode: testOtp,
note: "Gunakan kode ini untuk verifikasi test",
},
});
} catch (error) {
healthCheck.status = "unavailable";
const errorMessage =
error instanceof Error ? error.message : String(error);
return NextResponse.json({
success: false,
message: "Exception saat mengirim OTP test",
health: healthCheck,
test: {
number: testNumber,
error: errorMessage,
note: "Cek network connectivity ke otp.wibudev.com",
},
});
}
}
// Basic health check without sending OTP
if (!tokenConfigured || isPlaceholder) {
healthCheck.status = "unavailable";
}
return NextResponse.json({
success: true,
message:
healthCheck.status === "ok"
? "OTP service tersedia"
: "OTP service tidak tersedia - cek WA_SERVER_TOKEN",
health: healthCheck,
usage: "Tambahkan ?test=true&number=6281234567890 untuk test kirim OTP",
});
} catch (error) {
console.error("❌ OTP Health Check Error:", error);
return NextResponse.json(
{
success: false,
message: "Health check gagal",
error: error instanceof Error ? error.message : String(error),
},
{ status: 500 }
);
}
}