diff --git a/.env.example b/.env.example index 3208c010..c3b6ee30 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,9 @@ SEAFILE_PUBLIC_SHARE_TOKEN=your_seafile_public_share_token WIBU_UPLOAD_DIR=uploads WIBU_DOWNLOAD_DIR=./download +# WhatsApp Server Configuration +WA_SERVER_TOKEN=your_whatsapp_server_token + # Application Configuration # IMPORTANT: For staging/production, set this to your actual domain # Local development: NEXT_PUBLIC_BASE_URL=http://localhost:3000 diff --git a/src/app/api/auth/_lib/sendCodeOtp.ts b/src/app/api/auth/_lib/sendCodeOtp.ts new file mode 100644 index 00000000..96adf9aa --- /dev/null +++ b/src/app/api/auth/_lib/sendCodeOtp.ts @@ -0,0 +1,32 @@ +// app/api/auth/_lib/sendCodeOtp.ts + +const sendCodeOtp = async ({ + nomor, + codeOtp, + newMessage, +}: { + nomor: string; + codeOtp?: string | number; + newMessage?: string; +}) => { + const msg = + newMessage || + `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 enCode = msg; + + const res = await fetch(`https://otp.wibudev.com/api/wa/send-text`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${process.env.WA_SERVER_TOKEN}`, + }, + body: JSON.stringify({ + number: nomor, + text: enCode, + }), + }); + + return res; +}; + +export { sendCodeOtp }; diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts index 87d7deb4..63f5bac1 100644 --- a/src/app/api/auth/login/route.ts +++ b/src/app/api/auth/login/route.ts @@ -2,16 +2,10 @@ import prisma from "@/lib/prisma"; import { NextResponse } from "next/server"; import { randomOTP } from "../_lib/randomOTP"; +import { sendCodeOtp } from "../_lib/sendCodeOtp"; import { cookies } from "next/headers"; export async function POST(req: Request) { - if (req.method !== "POST") { - return NextResponse.json( - { success: false, message: "Method Not Allowed" }, - { status: 405 } - ); - } - try { const { nomor } = await req.json(); @@ -35,26 +29,33 @@ export async function POST(req: Request) { 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)}`; - - console.log("🔍 Debug WA URL:", waUrl); - try { - const res = await fetch(waUrl); - if (!res.ok) { - console.error(`⚠️ WA Service HTTP Error: ${res.status} ${res.statusText}. Continuing since OTP is logged.`); - console.log(`💡 Use this OTP to login: ${codeOtp}`); + const waResponse = await sendCodeOtp({ + nomor, + codeOtp, + }); + + if (!waResponse.ok) { + console.error( + `⚠️ WA Service HTTP Error: ${waResponse.status} ${waResponse.statusText}. Continuing since OTP is logged.` + ); + console.log(`💡 Use this OTP to login: ${codeOtp}`); } else { - const sendWa = await res.json(); + const sendWa = await waResponse.json(); console.log("📱 WA Response:", sendWa); + if (sendWa.status !== "success") { console.error("⚠️ WA Service Logic Error:", sendWa); } } } catch (waError: unknown) { - const errorMessage = waError instanceof Error ? waError.message : String(waError); - console.error("⚠️ WA Connection Exception. Continuing since OTP is logged.", errorMessage); + const errorMessage = + waError instanceof Error ? waError.message : String(waError); + + console.error( + "⚠️ WA Connection Exception. Continuing since OTP is logged.", + errorMessage + ); } const createOtpId = await prisma.kodeOtp.create({ @@ -62,12 +63,12 @@ export async function POST(req: Request) { }); const cookieStore = await cookies(); - cookieStore.set('auth_flow', 'login', { + cookieStore.set("auth_flow", "login", { httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'lax', - maxAge: 60 * 5, // 5 menit - path: '/' + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + maxAge: 60 * 5, + path: "/", }); return NextResponse.json({ @@ -85,6 +86,7 @@ export async function POST(req: Request) { } } catch (error) { console.error("❌ Error Login:", error); + return NextResponse.json( { success: false, message: "Terjadi kesalahan saat login" }, { status: 500 } diff --git a/src/app/darmasaba/_com/main-page/apbdes/lib/grafikRealisasi.tsx b/src/app/darmasaba/_com/main-page/apbdes/lib/grafikRealisasi.tsx index 6b769e67..1b8ae448 100644 --- a/src/app/darmasaba/_com/main-page/apbdes/lib/grafikRealisasi.tsx +++ b/src/app/darmasaba/_com/main-page/apbdes/lib/grafikRealisasi.tsx @@ -1,4 +1,4 @@ -import { Paper, Title, Progress, Stack, Text, Group, Box } from '@mantine/core' +import { Paper, Title, Progress, Stack, Text, Group, Box, type MantineColor } from '@mantine/core' import { IconArrowUpRight, IconArrowDownRight } from '@tabler/icons-react' import { APBDes, APBDesItem } from '../types/apbdes' @@ -106,7 +106,7 @@ function Summary({ title, data, icon }: SummaryProps) {