Perbaikan create route
Perbaikan create route
Section titled “Perbaikan create route”Validasi, hash asinkron, select tanpa password.
Berkas: alat/hari-3/Praktik 9/perbaikan/create-route.js
// =====================================================================// Praktik 9 - versi perbaikan create/route.js//// Perubahan dari versi modul://// 1. requireAuth() dijalankan SEBELUM request.json(), supaya beban// parsing body tidak bisa dipicu tanpa token.//// 2. Body divalidasi dan dinormalkan lebih dahulu. Versi modul memakai// data.role yang bisa undefined; di sini role punya nilai bawaan dan// hanya nilai yang dikenal yang diterima.//// 3. Daftar role yang boleh dibuat diperbaiki. Versi modul menulis// ["editor", "admin"] padahal komentar di roles.js menyebut viewer,// admin, super_admin. Sekarang daftarnya diambil dari satu sumber di// roles.js supaya tidak ada dua daftar yang saling bertentangan.//// 4. Kolom yang salah tulis (nama dua kali) dan pemakaian hashSync// diperbaiki. bcrypt.hashSync memblokir event loop Node selama proses// hashing; versi async tidak.//// 5. Respons tidak lagi mengembalikan objek user apa adanya. Prisma create// mengembalikan SELURUH kolom termasuk password berisi hash bcrypt.//// 6. Pesan galat database tidak lagi diteruskan mentah ke klien.//// Letakkan berkas ini di: src/app/api/users/create/route.js// =====================================================================
import { NextResponse } from "next/server";import bcrypt from "bcryptjs";import { randomUUID } from "node:crypto";import { requireAuth } from "../../../../../lib/auth/verifyBearerToken";import { db } from "../../../../../lib/db";import { ROLE_DAPAT_DIBUAT } from "../../../../../lib/auth/roles";
const EMAIL_POLA = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Panjang minimum password yang bisa diterima akun baru. Modul tidak// menetapkan apa pun, sehingga "123" bisa menjadi password admin.const PASSWORD_MIN = 8;
export async function POST(request) { // 1. Otorisasi lebih dahulu — jangan sentuh body sebelum token sah. const { error, status } = requireAuth(request, "super_admin"); if (error) { return NextResponse.json({ message: error }, { status }); }
let data; try { data = await request.json(); } catch { return NextResponse.json({ message: "Body harus berupa JSON" }, { status: 400 }); }
const nama = typeof data.nama === "string" ? data.nama.trim() : ""; const email = typeof data.email === "string" ? data.email.trim().toLowerCase() : ""; const password = typeof data.password === "string" ? data.password : ""; const role = data.role ?? ROLE_DAPAT_DIBUAT[0]; const isActive = data.is_active === true;
if (!nama || !email || !password) { return NextResponse.json( { message: "Nama, email, dan password wajib diisi" }, { status: 400 }, ); } if (!EMAIL_POLA.test(email)) { return NextResponse.json({ message: "Format email tidak valid" }, { status: 400 }); } if (password.length < PASSWORD_MIN) { return NextResponse.json( { message: `Password minimal ${PASSWORD_MIN} karakter` }, { status: 400 }, ); } if (!ROLE_DAPAT_DIBUAT.includes(role)) { return NextResponse.json( { message: `Role tidak valid. Pilihan: ${ROLE_DAPAT_DIBUAT.join(", ")}` }, { status: 400 }, ); }
try { // Cek email lebih dahulu supaya pesannya jelas. Tetap ada UNIQUE // constraint di database sebagai penjaga terakhir kalau ada dua // permintaan datang bersamaan. const sudahAda = await db.users.findUnique({ where: { email }, select: { user_id: true }, }); if (sudahAda) { return NextResponse.json({ message: "Email sudah terdaftar" }, { status: 409 }); }
const passwordHash = await bcrypt.hash(password, 12);
const penggunaBaru = await db.users.create({ data: { user_id: randomUUID(), nama, email, password: passwordHash, role, is_active: isActive, }, // Hanya kolom ini yang keluar dari database. select: { user_id: true, nama: true, email: true, role: true, is_active: true, created_at: true, }, });
return NextResponse.json( { message: "Berhasil membuat user baru", data: penggunaBaru }, { status: 201 }, ); } catch (err) { // Galat database dicatat di server, tidak dikirim ke klien. console.error("[users/create]", err); return NextResponse.json({ message: "Terjadi kesalahan pada server" }, { status: 500 }); }}