Fix UI Admin Menu Kesehatan, Login Admin, OTP

This commit is contained in:
2025-09-08 14:02:21 +08:00
parent 8817b937b1
commit 797713ef49
80 changed files with 7648 additions and 4924 deletions

View File

@@ -0,0 +1,50 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { jwtVerify } from "jose";
export async function decrypt({
token,
encodedKey,
}: {
token: string;
encodedKey: string;
}): Promise<Record<string, any> | null> {
if (!token || !encodedKey) {
console.error("Missing required parameters:", {
hasToken: !!token,
hasEncodedKey: !!encodedKey,
});
return null;
}
try {
const enc = new TextEncoder().encode(encodedKey);
const { payload } = await jwtVerify(token, enc, {
algorithms: ["HS256"],
});
if (!payload || !payload.user) {
console.error("Invalid payload structure:", {
hasPayload: !!payload,
hasUser: payload ? !!payload.user : false,
});
return null;
}
// Logging untuk debug
// console.log("Decrypt successful:", {
// payloadExists: !!payload,
// userExists: !!payload.user,
// tokenPreview: token.substring(0, 10) + "...",
// });
return payload.user as Record<string, any>;
} catch (error) {
console.error("Token verification failed:", {
error,
tokenLength: token?.length,
errorName: error instanceof Error ? error.name : "Unknown error",
errorMessage: error instanceof Error ? error.message : String(error),
});
return null;
}
}

View File

@@ -0,0 +1,26 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { SignJWT } from "jose";
export async function encrypt({
user,
exp = "7 year",
encodedKey,
}: {
user: Record<string, any>;
exp?: string;
encodedKey: string;
}): Promise<string | null> {
try {
const enc = new TextEncoder().encode(encodedKey);
return new SignJWT({ user })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime(exp)
.sign(enc);
} catch (error) {
console.error("Gagal mengenkripsi", error);
return null;
}
}
// wibu:0.2.82

View File

@@ -0,0 +1,13 @@
"use server";
import prisma from "@/lib/prisma";
export async function auth_getCodeOtpByNumber({kodeId}: {kodeId: string}) {
const data = await prisma.kodeOtp.findFirst({
where: {
id: kodeId,
},
});
return data;
}

View File

@@ -0,0 +1,4 @@
export function randomOTP() {
const random = Math.floor(Math.random() * (9000 - 1000 )) + 1000
return random;
}

View File

@@ -0,0 +1,36 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { cookies } from "next/headers";
import { encrypt } from "./encrypt";
export async function sessionCreate({
sessionKey,
exp = "7 year",
encodedKey,
user,
}: {
sessionKey: string;
exp?: string;
encodedKey: string;
user: Record<string, unknown>;
}) {
const token = await encrypt({
exp,
encodedKey,
user,
});
const cookie: any = {
key: sessionKey,
value: token,
options: {
httpOnly: true,
sameSite: "lax",
path: "/",
},
};
(await cookies()).set(cookie.key, cookie.value, { ...cookie.options });
return token;
}
// wibu:0.2.82

View File

@@ -0,0 +1,63 @@
import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
import { randomOTP } from "../_lib/randomOTP";
export async function POST(req: Request) {
if (req.method !== "POST") {
return NextResponse.json(
{ success: false, message: "Method Not Allowed" },
{ status: 405 }
);
}
try {
const codeOtp = randomOTP();
const body = await req.json();
const { nomor } = body;
const res = await fetch(
`https://wa.wibudev.com/code?nom=${nomor}&text=Website Desa Darmasaba - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPADA SIAPAPUN, termasuk anggota ataupun Admin lainnya.
\n
>> Kode OTP anda: ${codeOtp}.
`
);
const sendWa = await res.json();
if (sendWa.status !== "success")
return NextResponse.json(
{ success: false, message: "Nomor Whatsapp Tidak Aktif" },
{ status: 400 }
);
const createOtpId = await prisma.kodeOtp.create({
data: {
nomor: nomor,
otp: codeOtp,
},
});
if (!createOtpId)
return NextResponse.json(
{ success: false, message: "Gagal mengirim kode OTP" },
{ status: 400 }
);
return NextResponse.json(
{
success: true,
message: "Kode verifikasi terkirim",
kodeId: createOtpId.id,
},
{ status: 200 }
);
} catch (error) {
console.log("Error Login", error);
return NextResponse.json(
{ success: false, message: "Terjadi masalah saat login" , reason: error as Error },
{ status: 500 }
);
} finally {
await prisma.$disconnect();
}
}

View File

@@ -0,0 +1,62 @@
import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
export async function POST(req: Request) {
if (req.method !== "POST") {
return NextResponse.json(
{ success: false, message: "Method Not Allowed" },
{ status: 405 }
);
}
try {
const { data } = await req.json();
const cekUsername = await prisma.user.findUnique({
where: {
username: data.username,
nomor: data.nomor,
},
});
if (cekUsername)
return NextResponse.json({
success: false,
message: "Username sudah digunakan",
});
const createUser = await prisma.user.create({
data: {
username: data.username,
nomor: data.nomor,
},
});
if (!createUser)
return NextResponse.json(
{ success: false, message: "Gagal Registrasi" },
{ status: 500 }
);
return NextResponse.json(
{
success: true,
message: "Registrasi Berhasil, Anda Sedang Login",
// data: createUser,
},
{ status: 201 }
);
} catch (error) {
console.error("Error registrasi:", error);
return NextResponse.json(
{
success: false,
message: "Maaf, Terjadi Keselahan",
reason: (error as Error).message,
},
{ status: 500 }
);
} finally {
await prisma.$disconnect();
}
}