From b751f031cd10f8802d511679ba22daf1a1e99166 Mon Sep 17 00:00:00 2001 From: nico Date: Wed, 1 Apr 2026 17:04:25 +0800 Subject: [PATCH] fix(auth/swagger): make WA failure non-fatal and include /api prefix in docs --- src/app/api/[[...slugs]]/route.ts | 267 ++++++++++---------- src/app/api/auth/login/route.ts | 33 +-- src/app/api/auth/register/route.ts | 17 +- src/app/api/auth/resend/route.ts | 20 +- src/app/api/auth/send-otp-register/route.ts | 17 +- 5 files changed, 182 insertions(+), 172 deletions(-) diff --git a/src/app/api/[[...slugs]]/route.ts b/src/app/api/[[...slugs]]/route.ts index 16b0f923..8e7149e2 100644 --- a/src/app/api/[[...slugs]]/route.ts +++ b/src/app/api/[[...slugs]]/route.ts @@ -81,7 +81,7 @@ const Utils = new Elysia({ if (!process.env.WIBU_UPLOAD_DIR) throw new Error("WIBU_UPLOAD_DIR is not defined"); -const ApiServer = new Elysia() +const ApiServer = new Elysia({ prefix: "/api" }) .use( staticPlugin({ assets: UPLOAD_DIR, @@ -89,6 +89,17 @@ const ApiServer = new Elysia() }), ) .use(cors(corsConfig)) + .use( + swagger({ + path: "/docs", + documentation: { + info: { + title: "Desa Darmasaba API Documentation", + version: "1.0.0", + }, + }, + }), + ) .onError(({ code }) => { if (code === "NOT_FOUND") { return { @@ -97,142 +108,128 @@ const ApiServer = new Elysia() }; } }) - .group("/api", (app) => - app - .use( - swagger({ - path: "/docs", - documentation: { - info: { - title: "Desa Darmasaba API Documentation", - version: "1.0.0", - }, - }, + .use(Utils) + .use(FileStorage) + .use(LandingPage) + .use(PPID) + .use(Desa) + .use(Kesehatan) + .use(Keamanan) + .use(Ekonomi) + .use(Inovasi) + .use(Lingkungan) + .use(Pendidikan) + .use(User) + .use(Role) + .use(Search) + .get("/layanan", layanan) + .get("/potensi", getPotensi) + .get( + "/img/:name", + ({ params, query }) => { + return img({ + name: params.name, + UPLOAD_DIR_IMAGE, + ROOT, + size: query.size, + }); + }, + { + params: t.Object({ + name: t.String(), + }), + query: t.Optional( + t.Object({ + size: t.Optional(t.Number()), }), - ) - .use(Utils) - .use(FileStorage) - .use(LandingPage) - .use(PPID) - .use(Desa) - .use(Kesehatan) - .use(Keamanan) - .use(Ekonomi) - .use(Inovasi) - .use(Lingkungan) - .use(Pendidikan) - .use(User) - .use(Role) - .use(Search) - .get("/layanan", layanan) - .get("/potensi", getPotensi) - .get( - "/img/:name", - ({ params, query }) => { - return img({ - name: params.name, - UPLOAD_DIR_IMAGE, - ROOT, - size: query.size, - }); - }, - { - params: t.Object({ - name: t.String(), - }), - query: t.Optional( - t.Object({ - size: t.Optional(t.Number()), - }), - ), - }, - ) - .delete( - "/img/:name", - ({ params }) => { - return imgDel({ - name: params.name, - UPLOAD_DIR_IMAGE, - }); - }, - { - params: t.Object({ - name: t.String(), - }), - }, - ) - .get( - "/imgs", - ({ query }) => { - return imgs({ - search: query.search, - page: query.page, - count: query.count, - UPLOAD_DIR_IMAGE, - }); - }, - { - query: t.Optional( - t.Object({ - page: t.Number({ default: 1 }), - count: t.Number({ default: 10 }), - search: t.String({ default: "" }), - }), - ), - }, - ) - .post( - "/upl-img", - ({ body }) => { - console.log(body.title); - return uplImg({ files: body.files, UPLOAD_DIR_IMAGE }); - }, - { - body: t.Object({ - title: t.String(), - files: t.Files({ multiple: true }), - }), - }, - ) - .post( - "/upl-img-single", - ({ body }) => { - return uplImgSingle({ - fileName: body.name, - file: body.file, - UPLOAD_DIR_IMAGE, - }); - }, - { - body: t.Object({ - name: t.String(), - file: t.File(), - }), - }, - ) - .post( - "/upl-csv-single", - ({ body }) => { - return uplCsvSingle({ fileName: body.name, file: body.file }); - }, - { - body: t.Object({ - name: t.String(), - file: t.File(), - }), - }, - ) - .post( - "/upl-csv", - ({ body }) => { - return uplCsv({ files: body.files }); - }, - { - body: t.Object({ - files: t.Files(), - }), - }, ), + }, + ) + .delete( + "/img/:name", + ({ params }) => { + return imgDel({ + name: params.name, + UPLOAD_DIR_IMAGE, + }); + }, + { + params: t.Object({ + name: t.String(), + }), + }, + ) + .get( + "/imgs", + ({ query }) => { + return imgs({ + search: query.search, + page: query.page, + count: query.count, + UPLOAD_DIR_IMAGE, + }); + }, + { + query: t.Optional( + t.Object({ + page: t.Number({ default: 1 }), + count: t.Number({ default: 10 }), + search: t.String({ default: "" }), + }), + ), + }, + ) + .post( + "/upl-img", + ({ body }) => { + console.log(body.title); + return uplImg({ files: body.files, UPLOAD_DIR_IMAGE }); + }, + { + body: t.Object({ + title: t.String(), + files: t.Files({ multiple: true }), + }), + }, + ) + .post( + "/upl-img-single", + ({ body }) => { + return uplImgSingle({ + fileName: body.name, + file: body.file, + UPLOAD_DIR_IMAGE, + }); + }, + { + body: t.Object({ + name: t.String(), + file: t.File(), + }), + }, + ) + .post( + "/upl-csv-single", + ({ body }) => { + return uplCsvSingle({ fileName: body.name, file: body.file }); + }, + { + body: t.Object({ + name: t.String(), + file: t.File(), + }), + }, + ) + .post( + "/upl-csv", + ({ body }) => { + return uplCsv({ files: body.files }); + }, + { + body: t.Object({ + files: t.Files(), + }), + }, ); export const GET = ApiServer.handle; diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index e7b25da4..1b9baccf 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -33,6 +33,8 @@ export async function POST(req: Request) { const codeOtp = randomOTP(); const otpNumber = Number(codeOtp); + console.log(`🔑 DEBUG OTP [${nomor}]: ${codeOtp}`); + const waMessage = `Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya.\n\n>> Kode OTP anda: ${codeOtp}.`; const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`; @@ -40,26 +42,19 @@ export async function POST(req: Request) { try { const res = await fetch(waUrl); - const sendWa = await res.json(); - console.log("📱 WA Response:", sendWa); - - if (sendWa.status !== "success") { - console.error("❌ WA Service Error:", sendWa); - return NextResponse.json( - { - success: false, - message: "Gagal mengirim OTP via WhatsApp", - debug: sendWa - }, - { status: 400 } - ); + if (!res.ok) { + const errorText = await res.text(); + console.error(`⚠️ WA Service HTTP Error: ${res.status} ${res.statusText}. Continuing since OTP is logged.`); + console.log(`💡 Use this OTP to login: ${codeOtp}`); + } else { + const sendWa = await res.json(); + console.log("📱 WA Response:", sendWa); + if (sendWa.status !== "success") { + console.error("⚠️ WA Service Logic Error:", sendWa); + } } - } catch (waError) { - console.error("❌ Fetch WA Error:", waError); - return NextResponse.json( - { success: false, message: "Terjadi kesalahan saat mengirim WA" }, - { status: 500 } - ); + } catch (waError: any) { + console.error("⚠️ WA Connection Exception. Continuing since OTP is logged.", waError.message); } const createOtpId = await prisma.kodeOtp.create({ diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts index 0ffac336..83ec5bb6 100644 --- a/src/app/api/auth/register/route.ts +++ b/src/app/api/auth/register/route.ts @@ -22,14 +22,21 @@ export async function POST(req: Request) { // ✅ Generate dan kirim OTP const codeOtp = randomOTP(); const otpNumber = Number(codeOtp); + console.log(`🔑 DEBUG REGISTER OTP [${nomor}]: ${codeOtp}`); const waMessage = `Website Desa Darmasaba - Kode verifikasi Anda: ${codeOtp}`; const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`; - const waRes = await fetch(waUrl); - const waData = await waRes.json(); - - if (waData.status !== "success") { - return NextResponse.json({ success: false, message: 'Gagal mengirim OTP via WhatsApp' }, { status: 400 }); + + try { + const waRes = await fetch(waUrl); + if (!waRes.ok) { + console.warn(`⚠️ WA Service HTTP Error (Register): ${waRes.status} ${waRes.statusText}. Continuing since OTP is logged.`); + } else { + const waData = await waRes.json(); + console.log("📱 WA Response (Register):", waData); + } + } catch (waError: any) { + console.warn("⚠️ WA Connection Exception (Register). Continuing since OTP is logged.", waError.message); } // ✅ Simpan OTP ke database diff --git a/src/app/api/auth/resend/route.ts b/src/app/api/auth/resend/route.ts index 5824057c..5d06728c 100644 --- a/src/app/api/auth/resend/route.ts +++ b/src/app/api/auth/resend/route.ts @@ -17,18 +17,22 @@ export async function POST(req: Request) { const codeOtp = randomOTP(); const otpNumber = Number(codeOtp); + console.log(`🔑 DEBUG RESEND OTP [${nomor}]: ${codeOtp}`); // Kirim OTP via WhatsApp const waMessage = `Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya.\n\n>> Kode OTP anda: ${codeOtp}.`; const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=${encodeURIComponent(waMessage)}`; - const waRes = await fetch(waUrl); - const waData = await waRes.json(); - - if (waData.status !== "success") { - return NextResponse.json( - { success: false, message: "Gagal mengirim OTP via WhatsApp" }, - { status: 400 } - ); + + try { + const waRes = await fetch(waUrl); + if (!waRes.ok) { + console.warn(`⚠️ WA Service HTTP Error (Resend): ${waRes.status} ${waRes.statusText}. Continuing since OTP is logged.`); + } else { + const waData = await waRes.json(); + console.log("📱 WA Response (Resend):", waData); + } + } catch (waError: any) { + console.warn("⚠️ WA Connection Exception (Resend). Continuing since OTP is logged.", waError.message); } // Simpan OTP ke database diff --git a/src/app/api/auth/send-otp-register/route.ts b/src/app/api/auth/send-otp-register/route.ts index 501ea316..d944b15e 100644 --- a/src/app/api/auth/send-otp-register/route.ts +++ b/src/app/api/auth/send-otp-register/route.ts @@ -21,14 +21,21 @@ export async function POST(req: Request) { // Generate OTP const codeOtp = randomOTP(); const otpNumber = Number(codeOtp); + console.log(`🔑 DEBUG SEND-OTP-REGISTER [${nomor}]: ${codeOtp}`); // Kirim WA const waUrl = `https://wa.wibudev.com/code?nom=${encodeURIComponent(nomor)}&text=Website Desa Darmasaba - Kode verifikasi Anda: ${codeOtp}`; - const res = await fetch(waUrl); - const sendWa = await res.json(); - - if (sendWa.status !== "success") { - return NextResponse.json({ success: false, message: 'Gagal mengirim OTP' }, { status: 400 }); + + try { + const res = await fetch(waUrl); + if (!res.ok) { + console.warn(`⚠️ WA Service HTTP Error (SendOTPRegister): ${res.status} ${res.statusText}. Continuing since OTP is logged.`); + } else { + const sendWa = await res.json(); + console.log("📱 WA Response (SendOTPRegister):", sendWa); + } + } catch (waError: any) { + console.warn("⚠️ WA Connection Exception (SendOTPRegister). Continuing since OTP is logged.", waError.message); } // Simpan OTP