diff --git a/bun.lockb b/bun.lockb index da3ca6e..11774f4 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7888c66..4adb9e8 100644 --- a/package.json +++ b/package.json @@ -39,8 +39,10 @@ "@tiptap/pm": "^2.11.7", "@tiptap/react": "^2.4.0", "@tiptap/starter-kit": "^2.4.0", + "@types/bun": "^1.2.17", "@types/busboy": "^1.5.4", "@types/lodash": "^4.17.6", + "@types/minimist": "^1.2.5", "@types/multer": "^1.4.12", "@types/web-push": "^3.6.3", "busboy": "^1.6.0", @@ -56,6 +58,7 @@ "iron-session": "^8.0.2", "jose": "^5.9.2", "lodash": "^4.17.21", + "minimist": "^1.2.8", "moment": "^2.30.1", "multer": "^1.4.5-lts.2", "next": "14.2.4", @@ -71,6 +74,7 @@ "rrule": "^2.8.1", "sharp": "^0.33.5", "supabase": "^1.192.5", + "tsx": "^4.20.3", "web-push": "^3.6.7", "wibu-cli": "^1.0.91", "wibu-pkg": "^1.0.63", diff --git a/src/app/api/fcm/route.ts b/src/app/api/fcm/route.ts deleted file mode 100644 index 6723548..0000000 --- a/src/app/api/fcm/route.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NextResponse } from "next/server"; -import { sendFCM } from "../../../../xfcm/send"; - -export const GET = () => { - const token = "cRz96GHKTRaQaRJ35e8Hxa:APA91bEUSxE0VPbqKSzseQ_zGhbYsDofMexKykRw7o_3z2aPM9YFmZbeA2enrmb3qjdZ2g4-QQtiNHAyaZqAT1ITOrwo9jVJlShTeABmEFYP5GLEUZ3dlLc"; - sendFCM(token, "Test dari Local", "Ini hanya percobaan notifikasi dari script."); - return NextResponse.json({ success: true, message: "Sukses" }, { status: 200 }); -} \ No newline at end of file diff --git a/src/app/api/mobile/fcm/[[...slug]]/route.ts b/src/app/api/mobile/fcm/[[...slug]]/route.ts new file mode 100644 index 0000000..13abc6a --- /dev/null +++ b/src/app/api/mobile/fcm/[[...slug]]/route.ts @@ -0,0 +1,21 @@ +import { sendFCM } from "@/lib/firebase/fcm"; +import elysia from "elysia"; + + +const ApiV2 = new elysia({ + prefix: "/api/mobile/fcm" +}) + .get("/", async () => { + const token = [ + 'c89yuexsS_uc1tOErVPu5a:APA91bEb6tEKXAfReZjFVJ2mMyOzoW_RXryLSnSJTpbIVV3G0L_DCNkLuRvJ02Ip-Erz88QCQBAt-C2SN8eCRxu3-v1sBzXzKPtDv-huXpkjXsyrkifqvUo', + 'cRz96GHKTRaQaRJ35e8Hxa:APA91bEUSxE0VPbqKSzseQ_zGhbYsDofMexKykRw7o_3z2aPM9YFmZbeA2enrmb3qjdZ2g4-QQtiNHAyaZqAT1ITOrwo9jVJlShTeABmEFYP5GLEUZ3dlLc' + ] + + sendFCM(token, "Test dari Production STG", "Ini hanya percobaan notifikasi dari script."); + return { + data: "success elysia api" + }; + }) + + +export const GET = ApiV2.handle; \ No newline at end of file diff --git a/src/app/api/mobile/home/notification/route.ts b/src/app/api/mobile/home/notification/route.ts index 31e7116..445c078 100644 --- a/src/app/api/mobile/home/notification/route.ts +++ b/src/app/api/mobile/home/notification/route.ts @@ -1,6 +1,6 @@ import { prisma } from "@/module/_global"; -import { funGetUserByCookies } from "@/module/auth"; -import { createLogUser } from "@/module/user"; +import { funGetUserById } from "@/module/auth"; +import { createLogUserMobile } from "@/module/user"; import _ from "lodash"; import moment from "moment"; import "moment/locale/id"; @@ -9,21 +9,22 @@ import { NextResponse } from "next/server"; // GET ALL NOTIFIKASI export async function GET(request: Request) { try { - const user = await funGetUserByCookies(); - if (user.id == undefined) { - return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); - } - const { searchParams } = new URL(request.url); + const user = searchParams.get('user'); const page = searchParams.get('page'); const dataSkip = Number(page) * 10 - 10; + const userMobile = await funGetUserById({ id: String(user) }); + if (userMobile.id == "null" || userMobile.id == undefined || userMobile.id == "") { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 }); + } + const announcements = await prisma.notifications.findMany({ skip: dataSkip, take: 10, where: { isActive: true, - idUserTo: user.id + idUserTo: userMobile.id }, orderBy: [ { @@ -53,11 +54,13 @@ export async function GET(request: Request) { // UPDATE READ NOTIFIKASI export async function PUT(request: Request, context: { params: { id: string } }) { try { - const user = await funGetUserByCookies() - if (user.id == undefined) { - return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + const { id, user } = await request.json(); + const userMobile = await funGetUserById({ id: String(user) }); + + if (userMobile.id == "null" || userMobile.id == undefined || userMobile.id == "") { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 }); } - const { id } = await request.json(); + const data = await prisma.notifications.count({ where: { id: id, @@ -70,7 +73,7 @@ export async function PUT(request: Request, context: { params: { id: string } }) success: false, message: "Gagal mendapatkan data, data tidak ditemukan", }, - { status: 404 } + { status: 200 } ); } @@ -84,13 +87,13 @@ export async function PUT(request: Request, context: { params: { id: string } }) }); // create log user - const log = await createLogUser({ act: 'UPDATE', desc: 'User membaca notifikasi', table: 'notifications', data: id }) + const log = await createLogUserMobile({ act: 'UPDATE', desc: 'User membaca notifikasi', table: 'notifications', data: id, user: userMobile.id }) - return NextResponse.json({ success: true, message: "Berhasil mendapatkan notifikasi", }, { status: 200 }); + return NextResponse.json({ success: true, message: "Berhasil membaca notifikasi", }, { status: 200 }); } catch (error) { console.error(error); - return NextResponse.json({ success: false, message: "Gagal mendapatkan notifikasi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); + return NextResponse.json({ success: false, message: "Gagal membaca notifikasi, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); } } @@ -98,14 +101,16 @@ export async function PUT(request: Request, context: { params: { id: string } }) // UPDATE READ ALL NOTIFICATION export async function POST(request: Request) { try { - const user = await funGetUserByCookies() - if (user.id == undefined) { - return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 401 }); + const { user } = await request.json(); + const userMobile = await funGetUserById({ id: String(user) }); + + if (userMobile.id == "null" || userMobile.id == undefined || userMobile.id == "") { + return NextResponse.json({ success: false, message: "Anda harus login untuk mengakses ini" }, { status: 200 }); } const upd = await prisma.notifications.updateMany({ where: { - idUserTo: user.id, + idUserTo: userMobile.id, isRead: false }, data: { @@ -114,7 +119,7 @@ export async function POST(request: Request) { }) // create log user - const log = await createLogUser({ act: 'UPDATE', desc: 'User menandai semua notifikasi', table: 'notifications', data: '' }) + const log = await createLogUserMobile({ act: 'UPDATE', desc: 'User menandai semua notifikasi', table: 'notifications', data: '', user: userMobile.id }) return NextResponse.json({ success: true, message: "Berhasil mengupdate notifikasi", }, { status: 200 }); diff --git a/src/app/api/mobile/route.ts b/src/app/api/mobile/route.ts deleted file mode 100644 index 124dca8..0000000 --- a/src/app/api/mobile/route.ts +++ /dev/null @@ -1,75 +0,0 @@ -import prisma from '@/lib/prisma'; -import admin from 'firebase-admin'; -import { NextResponse } from "next/server"; - -export async function GET(request: Request) { - try { - if (!admin.apps.length) { - const serviceAccount = require("../../../../service_key.json"); - admin.initializeApp({ - credential: admin.credential.cert(serviceAccount), - }); - } - - const token = [ - // 'fPfBeTn4R4KsHhDw-8Edj0:APA91bGBjguuZsMhisJua_Wa3m7z7vBCE08vjyDBScmN0eIRgpfINlDx4SI6-upn-rr6tTAcoPxeQkxxbEsohcUbbV5DjUyIG4xR6wuvKOJMp3Mr4rNWFv8', - // 'c89yuexsS_uc1tOErVPu5a:APA91bEb6tEKXAfReZjFVJ2mMyOzoW_RXryLSnSJTpbIVV3G0L_DCNkLuRvJ02Ip-Erz88QCQBAt-C2SN8eCRxu3-v1sBzXzKPtDv-huXpkjXsyrkifqvUo', - 'cRz96GHKTRaQaRJ35e8Hxa:APA91bEUSxE0VPbqKSzseQ_zGhbYsDofMexKykRw7o_3z2aPM9YFmZbeA2enrmb3qjdZ2g4-QQtiNHAyaZqAT1ITOrwo9jVJlShTeABmEFYP5GLEUZ3dlLc' - ] - - - const message = { - notification: { - title: 'New Update :)', - body: 'Check out the latest features in our app.', - }, - data: { - category: 'value1', - content: 'value2', - }, - }; - - admin.messaging().sendEachForMulticast({ - tokens: token, - ...message, - }) - .then(async (response) => { - // Handle successful sends and failed tokens - if (response.failureCount > 0) { - const failedTokens: any[] = []; - response.responses.forEach((resp, idx) => { - if (!resp.success) { - failedTokens.push(token[idx]); - } - }); - const deleteToken = await prisma.tokenDeviceUser.deleteMany({ - where: { - token: { - in: failedTokens - } - } - }); - } - }) - .catch((error) => { - console.error('Error sending message:', error); - }); - - // const message = { - // notification: { - // title: 'Dari API Localhost Coba', - // body: 'Hello World Coba', - // }, - - // token: 'c89yuexsS_uc1tOErVPu5a:APA91bEb6tEKXAfReZjFVJ2mMyOzoW_RXryLSnSJTpbIVV3G0L_DCNkLuRvJ02Ip-Erz88QCQBAt-C2SN8eCRxu3-v1sBzXzKPtDv-huXpkjXsyrkifqvUo', - - // }; - - // const response = await admin.messaging().send(message); - - return NextResponse.json({ success: true, message: `Sukses ${admin.apps.length}` }, { status: 200 }); - } catch (error) { - console.error(error); - return NextResponse.json({ success: false, message: "Gagal", reason: (error as Error).message, }, { status: 500 }); - } -} \ No newline at end of file diff --git a/src/app/api/mobile/user/[id]/route.ts b/src/app/api/mobile/user/[id]/route.ts index 29dada5..0fffb77 100644 --- a/src/app/api/mobile/user/[id]/route.ts +++ b/src/app/api/mobile/user/[id]/route.ts @@ -42,6 +42,11 @@ export async function GET(request: Request, context: { params: { id: string } }) id: true }, }, + Village:{ + select:{ + name:true + } + } }, }); @@ -51,10 +56,11 @@ export async function GET(request: Request, context: { params: { id: string } }) const idUserRole = users?.UserRole.id const phone = users?.phone.substr(2) const role = users?.UserRole.name + const village = users?.Village.name - const result = { ...userData, group, position, idUserRole, phone, role }; + const result = { ...userData, group, position, idUserRole, phone, role, village }; - const omitData = _.omit(result, ["Group", "Position", "UserRole"]); + const omitData = _.omit(result, ["Group", "Position", "UserRole", "Village"]); diff --git a/src/app/api/version-app/route.ts b/src/app/api/version-app/route.ts index bc31d4e..b224b1e 100644 --- a/src/app/api/version-app/route.ts +++ b/src/app/api/version-app/route.ts @@ -2,7 +2,7 @@ import { NextResponse } from "next/server"; export async function GET(request: Request) { try { - return NextResponse.json({ success: true, version: "1.5.0", tahap: "beta", update: "- update api mobile; - firebase admin" }, { status: 200 }); + return NextResponse.json({ success: true, version: "1.5.1", tahap: "beta", update: "-percobaan firebase admin fcm" }, { status: 200 }); } catch (error) { console.error(error); return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 }); diff --git a/src/lib/firebase/fcm.ts b/src/lib/firebase/fcm.ts new file mode 100644 index 0000000..1e8baf1 --- /dev/null +++ b/src/lib/firebase/fcm.ts @@ -0,0 +1,66 @@ +import admin from "firebase-admin"; + +// Inisialisasi admin (hanya sekali) +if (!admin.apps.length) { + admin.initializeApp({ + credential: admin.credential.cert({ + projectId: process.env.GOOGLE_PROJECT_ID, + privateKey: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'), + clientEmail: process.env.GOOGLE_CLIENT_EMAIL, + }), + }); +} + +/** + * Kirim push notifikasi ke FCM token + */ +export async function sendFCM(token: string[], title: string, body: string) { + const message = { + notification: { + title, + body, + }, + data: { + id: 'cm1eqwkwj00067j2bzejyh9u7', + category: 'division', + content: 'cm1eqwkvu00017j2b8qnisp9g', + }, + apns: { + payload: { + aps: { + contentAvailable: true, + alert: { + title, + body, + }, + }, + }, + }, + token, + }; + + try { + const result = await admin.messaging().sendEachForMulticast({ + tokens: token, + ...message, + }) + console.log("✅ FCM berhasil dikirim:", result); + if (result.failureCount > 0) { + const failedTokens: any[] = []; + result.responses.forEach((resp, idx) => { + if (!resp.success) { + failedTokens.push(token[idx]); + } + }); + // const deleteToken = await prisma.tokenDeviceUser.deleteMany({ + // where: { + // token: { + // in: failedTokens + // } + // } + // }); + } + } catch (err) { + console.error("❌ Gagal mengirim FCM:", err); + } +} \ No newline at end of file diff --git a/xfcm/admin.ts b/xfcm/admin.ts deleted file mode 100644 index 35b185a..0000000 --- a/xfcm/admin.ts +++ /dev/null @@ -1,13 +0,0 @@ -import admin from "firebase-admin"; - -if (!admin.apps.length) { - admin.initializeApp({ - credential: admin.credential.cert({ - projectId: process.env.GOOGLE_PROJECT_ID, - privateKey: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'), - clientEmail: process.env.GOOGLE_CLIENT_EMAIL, - }), - }); -} - -export default admin; diff --git a/xfcm/send.ts b/xfcm/send.ts deleted file mode 100644 index 6377745..0000000 --- a/xfcm/send.ts +++ /dev/null @@ -1,38 +0,0 @@ -import admin from "firebase-admin"; - -// Inisialisasi admin (hanya sekali) -if (!admin.apps.length) { - admin.initializeApp({ - credential: admin.credential.cert({ - projectId: process.env.GOOGLE_PROJECT_ID, - privateKey: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, '\n'), - clientEmail: process.env.GOOGLE_CLIENT_EMAIL, - }), - }); -} - -/** - * Kirim push notifikasi ke FCM token - */ -export async function sendFCM(token: string, title: string, body: string) { - const message = { - token, - notification: { - title, - body, - }, - data: { - tested: "true" - } - }; - - try { - const result = await admin.messaging().send(message); - console.log("✅ FCM berhasil dikirim:", result); - } catch (err) { - console.error("❌ Gagal mengirim FCM:", err); - } -} - -// const token = "cRz96GHKTRaQaRJ35e8Hxa:APA91bEUSxE0VPbqKSzseQ_zGhbYsDofMexKykRw7o_3z2aPM9YFmZbeA2enrmb3qjdZ2g4-QQtiNHAyaZqAT1ITOrwo9jVJlShTeABmEFYP5GLEUZ3dlLc"; -// sendFCM(token, "Test dari Local", "Ini hanya percobaan notifikasi dari script.");