From c976e6beafcb440e28e8afd520918df39d4a6ddb Mon Sep 17 00:00:00 2001 From: bagasbanuna Date: Wed, 3 Dec 2025 14:58:03 +0800 Subject: [PATCH] Fix reject Apple : Add: src/app/api/auth/mobile-login/ src/app/api/auth/mobile-register/ src/app/api/auth/mobile-validasi/ Fix: modified: bun.lock modified: package.json modified: src/app/api/mobile/voting/route.ts ### No Issue --- bun.lock | 11 ++- package.json | 1 + src/app/api/auth/mobile-login/route.ts | 77 +++++++++++++++ src/app/api/auth/mobile-register/route.ts | 108 ++++++++++++++++++++++ src/app/api/auth/mobile-validasi/route.ts | 82 ++++++++++++++++ src/app/api/mobile/voting/route.ts | 20 +++- 6 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 src/app/api/auth/mobile-login/route.ts create mode 100644 src/app/api/auth/mobile-register/route.ts create mode 100644 src/app/api/auth/mobile-validasi/route.ts diff --git a/bun.lock b/bun.lock index 388be769..60a6c70f 100644 --- a/bun.lock +++ b/bun.lock @@ -41,6 +41,7 @@ "autoprefixer": "10.4.14", "bufferutil": "^4.0.8", "bun": "^1.1.38", + "caniuse-lite": "^1.0.30001757", "colors": "^1.4.0", "date-fns": "^4.1.0", "dayjs": "^1.11.10", @@ -1388,7 +1389,7 @@ "camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="], - "caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001757", "", {}, "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ=="], "canvas": ["canvas@3.1.0", "", { "dependencies": { "node-addon-api": "^7.0.0", "prebuild-install": "^7.1.1" } }, "sha512-tTj3CqqukVJ9NgSahykNwtGda7V33VLObwrHfzT0vqJXu7J4d4C/7kQQW3fOEGDfZZoILPut5H00gOjyttPGyg=="], @@ -3474,6 +3475,8 @@ "ast-types/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "autoprefixer/caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="], + "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], "better-opn/open": ["open@8.4.2", "", { "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", "is-wsl": "^2.2.0" } }, "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ=="], @@ -3482,6 +3485,8 @@ "blessed-contrib/strip-ansi": ["strip-ansi@3.0.1", "", { "dependencies": { "ansi-regex": "^2.0.0" } }, "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg=="], + "browserslist/caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="], + "cacache/glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], "caller-callsite/callsites": ["callsites@2.0.0", "", {}, "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ=="], @@ -3640,6 +3645,8 @@ "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "next/caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="], + "next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "next-dev/@mantine/hooks": ["@mantine/hooks@7.17.0", "", { "peerDependencies": { "react": "^18.x || ^19.x" } }, "sha512-vo3K49mLy1nJ8LQNb5KDbJgnX0xwt3Y8JOF3ythjB5LEFMptdLSSgulu64zj+QHtzvffFCsMb05DbTLLpVP/JQ=="], @@ -4074,6 +4081,8 @@ "wibu/next/@swc/helpers": ["@swc/helpers@0.5.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "tslib": "^2.4.0" } }, "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A=="], + "wibu/next/caniuse-lite": ["caniuse-lite@1.0.30001701", "", {}, "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw=="], + "wibu/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], "wibu/react-dom/scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], diff --git a/package.json b/package.json index ba2dfd6b..8521cc3c 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "autoprefixer": "10.4.14", "bufferutil": "^4.0.8", "bun": "^1.1.38", + "caniuse-lite": "^1.0.30001757", "colors": "^1.4.0", "date-fns": "^4.1.0", "dayjs": "^1.11.10", diff --git a/src/app/api/auth/mobile-login/route.ts b/src/app/api/auth/mobile-login/route.ts new file mode 100644 index 00000000..3d18f185 --- /dev/null +++ b/src/app/api/auth/mobile-login/route.ts @@ -0,0 +1,77 @@ +import { prisma } from "@/lib"; +import { randomOTP } from "@/app_modules/auth/fun/rondom_otp"; +import { NextResponse } from "next/server"; + +export async function POST(req: Request) { + try { + + const codeOtp = randomOTP(); + const body = await req.json(); + console.log("[Masuk API]", body); + const { nomor } = body; + + const user = await prisma.user.findUnique({ + where: { + nomor: nomor, + }, + }); + + console.log(["cek user", user]); + console.log(["cek nomor", nomor]); + + if (!user) + return NextResponse.json({ + success: false, + message: "User tidak ditemukan", + status: 404, + }); + + 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 } + ); + + const msg = `HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPAADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya.\n\n\n> Kode OTP anda: ${codeOtp}.`; + // const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; + const encodedMsg = encodeURIComponent(msg); + + const res = await fetch( + `https://wa.wibudev.com/code?nom=${nomor}&text=${encodedMsg}`, + { cache: "no-cache" } + ); + + const sendWa = await res.json(); + + if (sendWa.status !== "success") + return NextResponse.json( + { success: false, message: "Nomor Whatsapp Tidak Aktif" }, + { status: 400 } + ); + + return NextResponse.json( + { + success: true, + message: "Kode verifikasi terkirim", + kodeId: createOtpId.id, + }, + { status: 200 } + ); + } catch (error) { + return NextResponse.json( + { + success: false, + message: "Terjadi masalah saat login", + reason: error as Error, + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/auth/mobile-register/route.ts b/src/app/api/auth/mobile-register/route.ts new file mode 100644 index 00000000..332518f8 --- /dev/null +++ b/src/app/api/auth/mobile-register/route.ts @@ -0,0 +1,108 @@ +import { sessionCreate } from "@/app/(auth)/_lib/session_create"; +import { randomOTP } from "@/app_modules/auth/fun/rondom_otp"; +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 } + ); + } + + const { data } = await req.json(); + console.log("data >>", data); + const codeOtp = randomOTP(); + try { + const cekUsername = await prisma.user.findUnique({ + where: { + username: data.username, + }, + }); + + if (cekUsername) + return NextResponse.json({ + success: false, + message: "Username sudah digunakan", + }); + + // ✅ Validasi wajib setuju Terms + if (data.termsOfServiceAccepted !== true) { + return NextResponse.json({ + success: false, + message: "You must agree to the Terms of Service", + }); + } + + const createUser = await prisma.user.create({ + data: { + username: data.username, + nomor: data.nomor, + active: false, + termsOfServiceAccepted: data.termsOfServiceAccepted, + }, + }); + + if (!createUser) + return NextResponse.json( + { success: false, message: "Gagal Registrasi" }, + { status: 500 } + ); + + // const token = await sessionCreate({ + // sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, + // encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + // user: createUser as any, + // }); + + const createOtpId = await prisma.kodeOtp.create({ + data: { + nomor: data.nomor, + otp: codeOtp, + }, + }); + + if (!createOtpId) + return NextResponse.json( + { success: false, message: "Gagal mengirim kode OTP" }, + { status: 400 } + ); + + // const msg = `HIPMI - Kode ini bersifat RAHASIA dan JANGAN DI BAGIKAN KEPAADA SIAPAPUN, termasuk anggota ataupun pengurus HIPMI lainnya.\n\n\n> Kode OTP anda: ${codeOtp}.`; + const msg = `HIPMI%20-%20Kode%20ini%20bersifat%20RAHASIA%20dan%20JANGAN%20DI%20BAGIKAN%20KEPADA%20SIAPAPUN%2C%20termasuk%20anggota%20ataupun%20pengurus%20HIPMI%20lainnya.%20Kode%20OTP%20anda%3A%20${codeOtp}.`; + // // const encodedMsg = encodeURIComponent(msg); + + const res = await fetch( + `https://wa.wibudev.com/code?nom=${data.nomor}&text=${msg}`, + { cache: "no-cache" } + ); + + const sendWa = await res.json(); + + if (sendWa.status !== "success") + return NextResponse.json( + { success: false, message: "Nomor Whatsapp Tidak Aktif" }, + { status: 400 } + ); + + return NextResponse.json( + { + success: true, + message: "Registrasi Berhasil", + // token: token, + kodeId: createOtpId.id, + }, + { status: 201 } + ); + } catch (error) { + return NextResponse.json( + { + success: false, + message: "Maaf, Terjadi Keselahan", + reason: (error as Error).message, + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/auth/mobile-validasi/route.ts b/src/app/api/auth/mobile-validasi/route.ts new file mode 100644 index 00000000..f0d94e08 --- /dev/null +++ b/src/app/api/auth/mobile-validasi/route.ts @@ -0,0 +1,82 @@ +import { sessionCreate } from "@/app/(auth)/_lib/session_create"; +import prisma from "@/lib/prisma"; +import backendLogger from "@/util/backendLogger"; +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 { nomor } = await req.json(); + + const dataUser = await prisma.user.findUnique({ + where: { + nomor: nomor, + }, + select: { + id: true, + nomor: true, + username: true, + active: true, + masterUserRoleId: true, + termsOfServiceAccepted: true, + }, + }); + + if (dataUser == null) + return NextResponse.json( + { success: false, message: "Nomor Belum Terdaftar" }, + { status: 200 } + ); + + const token = await sessionCreate({ + sessionKey: process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, + encodedKey: process.env.NEXT_PUBLIC_BASE_TOKEN_KEY!, + user: dataUser as any, + }); + + if (!token) { + return NextResponse.json( + { success: false, message: "Gagal membuat session" }, + { status: 500 } + ); + } + // Buat response dengan token dalam cookie + const response = NextResponse.json( + { + success: true, + message: "Berhasil Login", + roleId: dataUser.masterUserRoleId, + active: dataUser.active, + termsOfServiceAccepted: dataUser.termsOfServiceAccepted, + token: token, + }, + { status: 200 } + ); + + // Set cookie dengan token yang sudah dipastikan tidak null + response.cookies.set(process.env.NEXT_PUBLIC_BASE_SESSION_KEY!, token, { + path: "/", + sameSite: "lax", + secure: process.env.NODE_ENV === "production", + maxAge: 30 * 24 * 60 * 60, // 30 hari dalam detik (1 bulan) + }); + + return response; + } catch (error) { + backendLogger.log("API Error or Server Error", error); + return NextResponse.json( + { + success: false, + message: "Maaf, Terjadi Keselahan", + reason: (error as Error).message, + }, + { status: 500 } + ); + } +} diff --git a/src/app/api/mobile/voting/route.ts b/src/app/api/mobile/voting/route.ts index e364a50a..e3231ad0 100644 --- a/src/app/api/mobile/voting/route.ts +++ b/src/app/api/mobile/voting/route.ts @@ -65,14 +65,23 @@ async function GET(request: Request) { const search = searchParams.get("search"); const category = searchParams.get("category"); const authorId = searchParams.get("authorId"); + const userLoginId = searchParams.get("userLoginId"); + + console.log("userLoginId >>", userLoginId); let fixData; try { if (category === "beranda") { - fixData = await prisma.voting.findMany({ + if (!userLoginId) { + return NextResponse.json( + { success: false, message: "User ID required" }, + { status: 400 } + ); + } + const data = await prisma.voting.findMany({ orderBy: { - awalVote: "asc" + awalVote: "asc", }, where: { voting_StatusId: "1", @@ -85,6 +94,13 @@ async function GET(request: Request) { contains: search || "", mode: "insensitive", }, + NOT: { + Voting_Kontributor: { + some: { + authorId: userLoginId, + }, + }, + }, }, include: { Voting_DaftarNamaVote: {