Compare commits

...

26 Commits

Author SHA1 Message Date
9afd741d4f Merge pull request 'Fix Admin API Mobile' (#58) from mobile-api/18-feb-26 into staging
Reviewed-on: #58
2026-02-18 17:33:11 +08:00
1c227a2850 Fix Admin API Mobile
API – Admin Donation
- src/app/api/mobile/admin/donation/[id]/disbursement/route.ts
- src/app/api/mobile/admin/donation/[id]/donatur/route.ts
- src/app/api/mobile/admin/donation/route.ts

API – Master Data (Admin)
- src/app/api/mobile/admin/master/donation/route.ts
- src/app/api/mobile/admin/master/type-of-event/route.ts

API – Admin Voting
- src/app/api/mobile/admin/voting/route.ts

Docs
- PROMPT-AI.md
- QWEN.md

Deleted
- CHANGELOG_BRANCH.md

### No Issue
2026-02-18 17:22:54 +08:00
817919f8f7 Merge pull request '### Fitur: Penambahan Pagination pada Endpoint Admin Mobile' (#57) from mobile-api/14-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/57
2026-02-14 16:25:39 +08:00
5bdb998d2e ### Fitur: Penambahan Pagination pada Endpoint Admin Mobile
#### Deskripsi Umum
Telah dilakukan penambahan fitur pagination pada beberapa endpoint admin mobile untuk meningkatkan kinerja dan pengalaman pengguna saat mengakses data dalam jumlah besar.

#### File yang Diubah

1. **src/app/api/mobile/admin/job/route.ts**
   - Ditambahkan parameter  dari
   - Diterapkan logika pagination dengan  (default 10) dan
   - Query  telah dimodifikasi untuk mendukung pagination

2. **src/app/api/mobile/admin/event/route.ts**
   - Diperbaiki definisi variabel  untuk memastikan tipe data yang konsisten
   - Ditambahkan default value 1 untuk parameter
   - Perhitungan  disesuaikan agar lebih efisien

3. **src/app/api/mobile/admin/event/[id]/participants/route.ts**
   - Ditambahkan parameter  dari
   - Diterapkan logika pagination dengan  (default 10) dan
   - Query  telah dimodifikasi untuk mendukung pagination

#### Tujuan Perubahan
- Meningkatkan kinerja aplikasi saat mengambil data dalam jumlah besar
- Memungkinkan pengguna untuk mengakses data secara bertahap melalui halaman-halaman
- Mengurangi beban server saat mengambil data dalam jumlah besar
- Memberikan pengalaman pengguna yang lebih baik saat mengakses data admin

#### Cara Penggunaan
Untuk menggunakan fitur pagination, cukup tambahkan parameter  pada query string saat melakukan permintaan ke endpoint yang telah dimodifikasi. Contoh:

Default jumlah data per halaman adalah 10 item.

### No Issue
2026-02-14 15:36:09 +08:00
90031e23ef Merge pull request 'Fix Api Mobile' (#56) from mobile-api/13-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/56
2026-02-13 17:41:31 +08:00
b585aa3024 Fix Api Mobile
API – Admin Master Data
- src/app/api/mobile/admin/master/bank/route.ts
- src/app/api/mobile/admin/master/business-field/route.ts
- src/app/api/mobile/admin/master/business-field/[id]/route.ts

Docs
- PROMPT-AI.md

### No Issue
2026-02-13 17:40:25 +08:00
596ebd2ff4 Merge pull request 'mobile-api/12-feb-26' (#55) from mobile-api/12-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/55
2026-02-12 17:49:00 +08:00
a8f9d2ac0d Fix API Mobile Admin
API – Admin User (Mobile)
- src/app/api/mobile/admin/user/route.ts

Docs
- PROMPT-AI.md

### No Issue
2026-02-12 17:42:06 +08:00
d43f3762a3 Fixed Bug Server
## Summary
This branch contains several bug fixes and performance improvements, primarily focusing on:
- Database connection management
- MQTT client stability
- Logging optimization
- API enhancements

## Detailed Changes

### Fixed Issues
1. **Database Connection Management**
   - Removed  from user-validate API route to prevent connection pool exhaustion
   - Added proper connection handling in global Prisma setup
   - Reduced logging verbosity in production environments

2. **MQTT Client Improvements**
   - Enhanced MQTT client initialization with proper error handling
   - Added reconnection logic with configurable intervals
   - Implemented cleanup functions to prevent memory leaks
   - Added separate initialization logic for server and client-side code

3. **Logging Optimization**
   - Removed excessive logging in middleware that was causing high CPU usage
   - Configured appropriate log levels for development and production

4. **Component Stability**
   - Added safety checks in text editor component to prevent MQTT operations on the server side
   - Improved MQTT publishing logic with client availability checks

### New Files
-  - Utility functions for safe database operations

### Modified Files
1.
   - Removed problematic  call

2.
   - Configured different logging levels for dev/prod
   - Removed process listeners that were causing disconnections
   - Exported prisma instance separately

3.
   - Removed excessive logging statements

4.
   - Enhanced initialization with error handling
   - Added reconnection and timeout configurations

5.
   - Added proper cleanup functions
   - Improved connection handling

6.
   - Added MQTT client availability checks
   - Prevented server-side MQTT operations

### Performance Improvements
- Reduced database connection overhead
- Optimized MQTT connection handling
- Eliminated unnecessary logging in production
- Better memory management with proper cleanup functions

### No Issue
2026-02-12 16:29:03 +08:00
aa700523ca Merge pull request 'feat: Implementasi pagination pada endpoint mobile donation' (#54) from mobile-api/10-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/54
2026-02-10 17:36:02 +08:00
236ab4d4a4 Merge pull request 'Mobile API' (#53) from mobile-api/9-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/53
2026-02-09 17:38:52 +08:00
eaa7692359 Merge pull request 'Fix API mobile Investment' (#52) from mobile-api/6-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/52
2026-02-06 17:39:36 +08:00
d51ce346e6 Merge pull request 'Fix API mobile' (#51) from mobile-api/5-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/51
2026-02-05 17:36:40 +08:00
91f4bb6c9e Merge pull request 'mobile-api/4-jan-26' (#50) from mobile-api/4-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/50
2026-02-05 10:11:03 +08:00
1fe0001994 Merge pull request 'Fix API Job untuk loaddata:' (#49) from mobile-api/2-feb-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/49
2026-02-02 17:11:32 +08:00
b82a283731 Merge pull request 'mobile-api for load data' (#48) from mobile-api/30-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/48
2026-01-30 17:20:09 +08:00
6d7d0fd07e Merge pull request 'mobile-notification done' (#46) from mobile-notification/27-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/46
2026-01-27 17:00:26 +08:00
bc80bb3441 Merge pull request 'Notification Donasi & EULA on login' (#45) from mobile-notification/23-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/45
2026-01-23 17:06:56 +08:00
8ab94b9c86 Merge pull request 'mobile-notification invesment' (#44) from mobile-notification/21-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/44
2026-01-21 15:41:18 +08:00
6e37b18e42 Merge pull request 'mobile-notification report comment' (#43) from mobile-notification/19-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/43
2026-01-19 17:54:21 +08:00
a6db03d0b4 Merge pull request 'mobile-notification event dan voting' (#42) from mobile-notification/15-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/42
2026-01-15 17:41:58 +08:00
c550a4e922 Merge pull request 'mobile-notification try to push to apple and android preview' (#41) from mobile-notification/12-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/41
2026-01-15 17:41:13 +08:00
2431a3fa3e Merge pull request 'Mobile notification & EULA route' (#40) from mobile-notification/9-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/40
2026-01-09 17:47:40 +08:00
1ed0da8c7d Merge pull request 'Fix API mobile notifikasi untuk job' (#39) from mobile-notification/7-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/39
2026-01-08 15:26:19 +08:00
e15a5d796d Merge pull request 'mobile notification' (#38) from mobile-notification/6-jan-26 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/38
2026-01-06 17:53:14 +08:00
836ebfaef0 Merge pull request 'mobile notification API' (#37) from mobile-notification/5-jan-25 into staging
Reviewed-on: http://wibugit.wibudev.com/wibu/hipmi/pulls/37
2026-01-05 14:06:58 +08:00
23 changed files with 299 additions and 97 deletions

39
CHANGELOG_COMMIT.md Normal file
View File

@@ -0,0 +1,39 @@
## Catatan Perubahan untuk Commit
### Fitur: Penambahan Pagination pada Endpoint Admin Mobile
#### Deskripsi Umum
Telah dilakukan penambahan fitur pagination pada beberapa endpoint admin mobile untuk meningkatkan kinerja dan pengalaman pengguna saat mengakses data dalam jumlah besar.
#### File yang Diubah
1. **src/app/api/mobile/admin/job/route.ts**
- Ditambahkan parameter `page` dari `searchParams`
- Diterapkan logika pagination dengan `takeData` (default 10) dan `skipData`
- Query `prisma.job.findMany` telah dimodifikasi untuk mendukung pagination
2. **src/app/api/mobile/admin/event/route.ts**
- Diperbaiki definisi variabel `page` untuk memastikan tipe data yang konsisten
- Ditambahkan default value 1 untuk parameter `page`
- Perhitungan `skipData` disesuaikan agar lebih efisien
3. **src/app/api/mobile/admin/event/[id]/participants/route.ts**
- Ditambahkan parameter `page` dari `searchParams`
- Diterapkan logika pagination dengan `takeData` (default 10) dan `skipData`
- Query `prisma.event_Peserta.findMany` telah dimodifikasi untuk mendukung pagination
#### Tujuan Perubahan
- Meningkatkan kinerja aplikasi saat mengambil data dalam jumlah besar
- Memungkinkan pengguna untuk mengakses data secara bertahap melalui halaman-halaman
- Mengurangi beban server saat mengambil data dalam jumlah besar
- Memberikan pengalaman pengguna yang lebih baik saat mengakses data admin
#### Cara Penggunaan
Untuk menggunakan fitur pagination, cukup tambahkan parameter `page` pada query string saat melakukan permintaan ke endpoint yang telah dimodifikasi. Contoh:
```
GET /api/mobile/admin/job?page=2
GET /api/mobile/admin/event?page=3
GET /api/mobile/admin/event/{id}/participants?page=1
```
Default jumlah data per halaman adalah 10 item.

View File

@@ -1,5 +1,5 @@
File utama: src/app/api/mobile/donation/[id]/donatur/route.ts File utama: src/app/api/mobile/admin/donation/[id]/donatur/route.ts
Terapkan pagination pada file "File utama" pada method GET Terapkan pagination pada file "File utama" pada method GET
Analisa juga file "File utama", jika belum memiliki page dari seachParams maka terapkan. Juga pastikan take dan skip sudah sesuai dengan pagination. Buat default nya menjadi 10 untuk take data Analisa juga file "File utama", jika belum memiliki page dari seachParams maka terapkan. Juga pastikan take dan skip sudah sesuai dengan pagination. Buat default nya menjadi 10 untuk take data

View File

@@ -198,4 +198,4 @@ References: #issue-number
### Data Protection ### Data Protection
- Encrypted tokens - Encrypted tokens
- Secure API routes - Secure API routes
- Proper CORS configuration - Proper CORS configuration

View File

@@ -10,6 +10,7 @@ import {
NotificationMobileTitleType, NotificationMobileTitleType,
} from "../../../../../../../../types/type-mobile-notification"; } from "../../../../../../../../types/type-mobile-notification";
import { routeUserMobile } from "@/lib/mobile/route-page-mobile"; import { routeUserMobile } from "@/lib/mobile/route-page-mobile";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { POST, GET }; export { POST, GET };
@@ -154,7 +155,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const category = searchParams.get("category"); const category = searchParams.get("category");
const page = searchParams.get("page"); const page = searchParams.get("page");
const takeData = 10; const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = Number(page) * takeData - takeData; const skipData = Number(page) * takeData - takeData;
console.log("[CATEGORY]", category); console.log("[CATEGORY]", category);
@@ -174,6 +175,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
id: true, id: true,
createdAt: true, createdAt: true,
nominalCair: true, nominalCair: true,
title: true,
}, },
}); });
} else if (category === "get-one") { } else if (category === "get-one") {

View File

@@ -1,6 +1,7 @@
import _ from "lodash"; import _ from "lodash";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET }; export { GET };
@@ -9,7 +10,7 @@ async function GET(req: Request, { params }: { params: { id: string } }) {
const { searchParams } = new URL(req.url); const { searchParams } = new URL(req.url);
const page = searchParams.get("page"); const page = searchParams.get("page");
const status = searchParams.get("status"); const status = searchParams.get("status");
const takeData = 10; const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = Number(page) * takeData - takeData; const skipData = Number(page) * takeData - takeData;
const fixStatus = _.startCase(status || ""); const fixStatus = _.startCase(status || "");

View File

@@ -1,6 +1,7 @@
import _ from "lodash"; import _ from "lodash";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET }; export { GET };
@@ -9,11 +10,10 @@ async function GET(request: Request) {
const category = searchParams.get("category"); const category = searchParams.get("category");
const page = searchParams.get("page"); const page = searchParams.get("page");
const search = searchParams.get("search"); const search = searchParams.get("search");
const takeData = 10; const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = Number(page) * takeData - takeData; const skipData = Number(page) * takeData - takeData;
console.log("[CATEGORY]", category);
let fixData; let fixData;
try { try {
if (category === "dashboard") { if (category === "dashboard") {
const publish = await prisma.donasi.count({ const publish = await prisma.donasi.count({
@@ -48,7 +48,7 @@ async function GET(request: Request) {
where: { where: {
active: true, active: true,
}, },
} },
); );
const categoryDonation = countCategoryDonation.length; const categoryDonation = countCategoryDonation.length;
@@ -68,7 +68,6 @@ async function GET(request: Request) {
}, },
}); });
console.log("[STATUS]", checkStatus);
if (!checkStatus) { if (!checkStatus) {
return NextResponse.json( return NextResponse.json(
@@ -77,7 +76,7 @@ async function GET(request: Request) {
message: "Failed to get data donation", message: "Failed to get data donation",
reason: "Status not found", reason: "Status not found",
}, },
{ status: 500 } { status: 500 },
); );
} }
@@ -100,6 +99,12 @@ async function GET(request: Request) {
select: { select: {
id: true, id: true,
title: true, title: true,
target: true,
DonasiMaster_Durasi: {
select: {
name: true,
},
},
Author: { Author: {
select: { select: {
id: true, id: true,
@@ -109,7 +114,6 @@ async function GET(request: Request) {
}, },
}); });
console.log("[LIST]", fixData);
} }
return NextResponse.json( return NextResponse.json(
@@ -118,7 +122,7 @@ async function GET(request: Request) {
message: `Success get data donation ${category}`, message: `Success get data donation ${category}`,
data: fixData, data: fixData,
}, },
{ status: 200 } { status: 200 },
); );
} catch (error) { } catch (error) {
console.error("Error get data donation:", error); console.error("Error get data donation:", error);
@@ -128,7 +132,7 @@ async function GET(request: Request) {
message: "Failed to get data donation", message: "Failed to get data donation",
reason: (error as Error).message, reason: (error as Error).message,
}, },
{ status: 500 } { status: 500 },
); );
} }
} }

View File

@@ -1,9 +1,15 @@
import { NextResponse } from "next/server"; import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { NextResponse } from "next/server";
export { GET }; export { GET };
async function GET(request: Request, { params }: { params: { id: string } }) { async function GET(request: Request, { params }: { params: { id: string } }) {
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
try { try {
const { id } = params; const { id } = params;
@@ -12,6 +18,7 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
eventId: id, eventId: id,
}, },
select: { select: {
id: true,
eventId: true, eventId: true,
userId: true, userId: true,
isPresent: true, isPresent: true,
@@ -35,6 +42,8 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
}, },
}, },
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
return NextResponse.json( return NextResponse.json(

View File

@@ -1,7 +1,8 @@
import _ from "lodash";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { NextResponse } from "next/server"; import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import _ from "lodash";
import moment from "moment"; import moment from "moment";
import { NextResponse } from "next/server";
export { GET }; export { GET };
@@ -11,13 +12,12 @@ async function GET(request: Request) {
const fixStatus = _.startCase(category || ""); const fixStatus = _.startCase(category || "");
const search = searchParams.get("search"); const search = searchParams.get("search");
const page = searchParams.get("page"); const page = Number(searchParams.get("page")) || 1;
const takeData = 10; const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = Number(page) * takeData - takeData; const skipData = page * takeData - takeData;
let fixData; let fixData;
console.log("[CATEGORY]", category);
// console.log("[FIX STATUS]", fixStatus);
try { try {
if (category === "dashboard") { if (category === "dashboard") {
@@ -71,7 +71,6 @@ async function GET(request: Request) {
typeOfEvent, typeOfEvent,
}; };
} else if (category === "history") { } else if (category === "history") {
console.log("[HISTORY HERE]");
const data = await prisma.event.findMany({ const data = await prisma.event.findMany({
take: page ? takeData : undefined, take: page ? takeData : undefined,
@@ -151,21 +150,22 @@ async function GET(request: Request) {
}, },
}, },
select: { select: {
id: true, id: true,
title: true, title: true,
tanggal: true, tanggal: true,
Author: { tanggalSelesai: true,
select: { Author: {
id: true, select: {
username: true, id: true,
Profile: { username: true,
select: { Profile: {
name: true, select: {
}, name: true,
}, },
}, },
}, },
}, },
},
}); });
fixData = data; fixData = data;
@@ -177,7 +177,7 @@ async function GET(request: Request) {
message: `Success get data event ${category}`, message: `Success get data event ${category}`,
data: fixData, data: fixData,
}, },
{ status: 200 } { status: 200 },
); );
} catch (error) { } catch (error) {
console.log(`[ERROR GET DATA EVENT: ${category}]`, error); console.log(`[ERROR GET DATA EVENT: ${category}]`, error);
@@ -187,7 +187,7 @@ async function GET(request: Request) {
message: `Error get data event ${category}`, message: `Error get data event ${category}`,
reason: (error as Error).message, reason: (error as Error).message,
}, },
{ status: 500 } { status: 500 },
); );
} }
} }

View File

@@ -1,6 +1,7 @@
import _ from "lodash"; import _ from "lodash";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET }; export { GET };
@@ -8,6 +9,9 @@ async function GET(request: Request, { params }: { params: { name: string } }) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const category = searchParams.get("category"); const category = searchParams.get("category");
const search = searchParams.get("search"); const search = searchParams.get("search");
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
let fixData; let fixData;
try { try {
@@ -66,6 +70,8 @@ async function GET(request: Request, { params }: { params: { name: string } }) {
title: true, title: true,
Author: true, Author: true,
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
} }

View File

@@ -1,14 +1,22 @@
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
export { GET, POST }; export { GET, POST };
async function GET() { async function GET(request: Request) {
try { try {
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
const data = await prisma.masterBank.findMany({ const data = await prisma.masterBank.findMany({
orderBy: { orderBy: {
updatedAt: "desc", updatedAt: "desc",
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
return NextResponse.json( return NextResponse.json(

View File

@@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import { NextResponse } from "next/server";
export { GET, PUT }; export { GET, PUT };
@@ -11,6 +12,10 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
const category = searchParams.get("category"); const category = searchParams.get("category");
const subBidangId = searchParams.get("subBidangId"); const subBidangId = searchParams.get("subBidangId");
const page = Number(searchParams.get("page")) || 1;
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
if (category === "all") { if (category === "all") {
const bidang = await prisma.masterBidangBisnis.findUnique({ const bidang = await prisma.masterBidangBisnis.findUnique({
where: { where: {
@@ -45,6 +50,16 @@ async function GET(request: Request, { params }: { params: { id: string } }) {
}, },
}); });
fixData = subBidang;
} else if (category === "only-sub-bidang") {
const subBidang = await prisma.masterSubBidangBisnis.findMany({
where: {
masterBidangBisnisId: id,
},
take: takeData,
skip: skipData,
});
fixData = subBidang; fixData = subBidang;
} }
@@ -71,9 +86,6 @@ async function PUT(request: Request, { params }: { params: { id: string } }) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const category = searchParams.get("category"); const category = searchParams.get("category");
console.log("category", category);
console.log("data", data);
try { try {
if (category === "bidang") { if (category === "bidang") {
const updateData = await prisma.masterBidangBisnis.update({ const updateData = await prisma.masterBidangBisnis.update({

View File

@@ -2,15 +2,24 @@ import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import _ from "lodash"; import _ from "lodash";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET, POST }; export { GET, POST };
async function GET(request: Request) { async function GET(request: Request) {
try { try {
const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
const data = await prisma.masterBidangBisnis.findMany({ const data = await prisma.masterBidangBisnis.findMany({
orderBy: { orderBy: {
createdAt: "asc", createdAt: "asc",
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
return NextResponse.json({ return NextResponse.json({

View File

@@ -1,10 +1,14 @@
import { NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET, POST }; export { GET, POST };
async function GET(request: Request) { async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
// const category = searchParams.get("category"); // const category = searchParams.get("category");
let fixData; let fixData;
@@ -13,6 +17,8 @@ async function GET(request: Request) {
orderBy: { orderBy: {
createdAt: "asc", createdAt: "asc",
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
// if (category === "category") { // if (category === "category") {

View File

@@ -1,14 +1,22 @@
import { NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET, POST }; export { GET, POST };
async function GET(request: Request) { async function GET(request: NextRequest) {
try { try {
const searchParams = request.nextUrl.searchParams;
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
const data = await prisma.eventMaster_TipeAcara.findMany({ const data = await prisma.eventMaster_TipeAcara.findMany({
orderBy: { orderBy: {
updatedAt: "desc", updatedAt: "desc",
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
return NextResponse.json({ return NextResponse.json({

View File

@@ -1,5 +1,6 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
import { NextResponse } from "next/server";
export { GET }; export { GET };
@@ -7,10 +8,16 @@ async function GET(request: Request) {
const { searchParams } = new URL(request.url); const { searchParams } = new URL(request.url);
const search = searchParams.get("search"); const search = searchParams.get("search");
const category = searchParams.get("category"); const category = searchParams.get("category");
const page = Number(searchParams.get("page"));
const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = page * takeData - takeData;
console.log("SEARCH", search);
console.log("PAGE", page);
let fixData; let fixData;
try { try {
if(category === "only-user"){ if (category === "only-user") {
fixData = await prisma.user.findMany({ fixData = await prisma.user.findMany({
orderBy: { orderBy: {
updatedAt: "desc", updatedAt: "desc",
@@ -22,8 +29,10 @@ async function GET(request: Request) {
mode: "insensitive", mode: "insensitive",
}, },
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
} else if(category === "only-admin"){ } else if (category === "only-admin") {
fixData = await prisma.user.findMany({ fixData = await prisma.user.findMany({
orderBy: { orderBy: {
updatedAt: "desc", updatedAt: "desc",
@@ -35,8 +44,10 @@ async function GET(request: Request) {
mode: "insensitive", mode: "insensitive",
}, },
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
} else if (category === "all-role"){ } else if (category === "all-role") {
fixData = await prisma.user.findMany({ fixData = await prisma.user.findMany({
orderBy: { orderBy: {
updatedAt: "desc", updatedAt: "desc",
@@ -48,13 +59,15 @@ async function GET(request: Request) {
}, },
{ {
masterUserRoleId: "2", masterUserRoleId: "2",
} },
], ],
username: { username: {
contains: search || "", contains: search || "",
mode: "insensitive", mode: "insensitive",
}, },
}, },
take: page ? takeData : undefined,
skip: page ? skipData : undefined,
}); });
} }
@@ -65,13 +78,11 @@ async function GET(request: Request) {
data: fixData, data: fixData,
}); });
} catch (error) { } catch (error) {
return NextResponse.json( return NextResponse.json({
{ status: 500,
status: 500, success: false,
success: false, message: "Error get data user access",
message: "Error get data user access", reason: (error as Error).message,
reason: (error as Error).message, });
},
);
} }
} }

View File

@@ -2,6 +2,7 @@ import _ from "lodash";
import moment from "moment"; import moment from "moment";
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { prisma } from "@/lib"; import { prisma } from "@/lib";
import { PAGINATION_DEFAULT_TAKE } from "@/lib/constans-value/constansValue";
export { GET }; export { GET };
@@ -12,7 +13,7 @@ async function GET(request: Request) {
const search = searchParams.get("search"); const search = searchParams.get("search");
const page = searchParams.get("page"); const page = searchParams.get("page");
const takeData = 10; const takeData = PAGINATION_DEFAULT_TAKE;
const skipData = Number(page) * takeData - takeData; const skipData = Number(page) * takeData - takeData;
let fixData; let fixData;

View File

@@ -84,7 +84,7 @@ export async function GET(req: Request) {
}, },
{ status: 500 } { status: 500 }
); );
} finally {
await prisma.$disconnect();
} }
// Removed prisma.$disconnect() from here to prevent connection pool exhaustion
// Prisma connections are handled globally and shouldn't be disconnected on each request
} }

View File

@@ -28,6 +28,7 @@ import { useRouter } from "next/navigation";
import { IconMoodSmileFilled } from "@tabler/icons-react"; import { IconMoodSmileFilled } from "@tabler/icons-react";
import { listStiker } from "../../lib/stiker"; import { listStiker } from "../../lib/stiker";
import { UIGlobal_Modal } from "../../ui"; import { UIGlobal_Modal } from "../../ui";
import mqtt_client from "@/util/mqtt_client";
const ReactQuill = dynamic( const ReactQuill = dynamic(
async () => { async () => {
@@ -248,10 +249,12 @@ function ButtonAction({ value, lengthData }: ButtonActionProps) {
ComponentGlobal_NotifikasiBerhasil(create.message); ComponentGlobal_NotifikasiBerhasil(create.message);
router.back(); router.back();
mqtt_client.publish( if (typeof window !== 'undefined' && mqtt_client) {
"Forum_create_new", mqtt_client.publish(
JSON.stringify({ isNewPost: true, count: 1 }) "Forum_create_new",
); JSON.stringify({ isNewPost: true, count: 1 })
);
}
} else { } else {
ComponentGlobal_NotifikasiGagal(create.message); ComponentGlobal_NotifikasiGagal(create.message);
} }

View File

@@ -9,30 +9,21 @@ declare global {
let prisma: PrismaClient; let prisma: PrismaClient;
if (process.env.NODE_ENV === "production") { if (process.env.NODE_ENV === "production") {
prisma = new PrismaClient(); prisma = new PrismaClient({
// Reduce logging in production to improve performance
log: ['error', 'warn'],
});
} else { } else {
if (!global.prisma) { if (!global.prisma) {
global.prisma = new PrismaClient(); global.prisma = new PrismaClient({
log: ['error', 'warn', 'info', 'query'], // More verbose logging in development
});
} }
prisma = global.prisma; prisma = global.prisma;
} }
// Tambahkan listener hanya jika belum ditambahkan sebelumnya // Tambahkan listener hanya jika belum ditambahkan sebelumnya
if (!global.prismaListenersAdded) { if (!global.prismaListenersAdded) {
// Handle uncaught errors
process.on("uncaughtException", async (error) => {
console.error("Uncaught Exception:", error);
await prisma.$disconnect();
process.exit(1);
});
// Handle unhandled promise rejections
process.on("unhandledRejection", async (error) => {
console.error("Unhandled Rejection:", error);
await prisma.$disconnect();
process.exit(1);
});
// Handle graceful shutdown // Handle graceful shutdown
process.on("SIGINT", async () => { process.on("SIGINT", async () => {
console.log("Received SIGINT signal. Closing database connections..."); console.log("Received SIGINT signal. Closing database connections...");
@@ -51,3 +42,4 @@ if (!global.prismaListenersAdded) {
} }
export default prisma; export default prisma;
export { prisma };

24
src/lib/prismaUtils.ts Normal file
View File

@@ -0,0 +1,24 @@
import { prisma } from './prisma';
/**
* Utility function to safely execute Prisma operations
* This prevents improper disconnection of the Prisma client
* which was causing high CPU usage and connection pool issues
*/
export async function executeDbOperation<T>(
operation: () => Promise<T>,
errorMessage: string = "Database operation failed"
): Promise<{ success: boolean; data?: T; error?: string }> {
try {
const data = await operation();
return { success: true, data };
} catch (error) {
console.error(errorMessage, error);
return { success: false, error: (error as Error).message };
}
// Note: We intentionally do NOT call prisma.$disconnect() here
// Prisma manages connection pooling automatically and disconnecting
// on each request causes performance issues
}
export { prisma };

View File

@@ -66,9 +66,10 @@ export const middleware = async (req: NextRequest) => {
const { pathname } = req.nextUrl; const { pathname } = req.nextUrl;
const apiBaseUrl = new URL(req.url).origin || process.env.NEXT_PUBLIC_API_URL; const apiBaseUrl = new URL(req.url).origin || process.env.NEXT_PUBLIC_API_URL;
const dbUrl = process.env.DATABASE_URL; // Removed excessive logging that was causing high CPU usage
console.log("DATABASE_URL >>", dbUrl); // const dbUrl = process.env.DATABASE_URL;
console.log("URL Access >>", req.url); // console.log("DATABASE_URL >>", dbUrl);
// console.log("URL Access >>", req.url);
// Handle CORS preflight // Handle CORS preflight
const corsResponse = handleCors(req); const corsResponse = handleCors(req);

View File

@@ -4,7 +4,66 @@ declare global {
var mqtt_client: mqtt.MqttClient; var mqtt_client: mqtt.MqttClient;
} }
const mqtt_client = // Initialize MQTT client with proper error handling and reconnection settings
globalThis.mqtt_client || mqtt.connect("wss://io.wibudev.com"); let mqtt_client: mqtt.MqttClient;
if (typeof window === 'undefined') {
// Server-side code
mqtt_client = globalThis.mqtt_client || (() => {
const client = mqtt.connect("wss://io.wibudev.com", {
reconnectPeriod: 5000, // Reconnect every 5 seconds
connectTimeout: 30 * 1000, // 30 second timeout
// Clean session to avoid message queue buildup
clean: true,
// Reduce unnecessary pings
keepalive: 60
});
// Prevent multiple initializations
globalThis.mqtt_client = client;
// Add error handling
client.on('error', (error) => {
console.error('MQTT Connection Error:', error);
});
client.on('reconnect', () => {
console.log('MQTT Reconnecting...');
});
client.on('close', () => {
console.log('MQTT Connection Closed');
});
return client;
})();
} else {
// Client-side code - initialize only once
if (!(globalThis as any).mqtt_client) {
(globalThis as any).mqtt_client = mqtt.connect("wss://io.wibudev.com", {
reconnectPeriod: 5000, // Reconnect every 5 seconds
connectTimeout: 30 * 1000, // 30 second timeout
// Clean session to avoid message queue buildup
clean: true,
// Reduce unnecessary pings
keepalive: 60
});
// Add error handling
(globalThis as any).mqtt_client.on('error', (error: any) => {
console.error('MQTT Connection Error:', error);
});
(globalThis as any).mqtt_client.on('reconnect', () => {
console.log('MQTT Reconnecting...');
});
(globalThis as any).mqtt_client.on('close', () => {
console.log('MQTT Connection Closed');
});
}
mqtt_client = (globalThis as any).mqtt_client;
}
export default mqtt_client; export default mqtt_client;

View File

@@ -3,20 +3,27 @@
import { useEffect } from "react"; import { useEffect } from "react";
import mqtt_client from "./mqtt_client"; import mqtt_client from "./mqtt_client";
export default function MqttLoader() { export default function MqttLoader() {
useEffect(() => { useEffect(() => {
mqtt_client.on("connect", () => { // Only set up connection handlers once
console.log("connected"); const handleConnect = () => {
}); console.log("MQTT connected");
};
const handleError = (error: any) => {
console.error("MQTT Error:", error);
};
// Subscribe to events
mqtt_client.on("connect", handleConnect);
mqtt_client.on("error", handleError);
// Cleanup function to unsubscribe when component unmounts
return () => {
mqtt_client.off("connect", handleConnect);
mqtt_client.off("error", handleError);
};
}, []); }, []);
return null; return null;
// <>
// <Stack>
// <Button onClick={onClick}>Tekan</Button>
// <Button onClick={onClick2}>Tekan 2</Button>
// </Stack>
// </>
// );
} }