upd: api monitoring #29

Merged
amaliadwiy merged 1 commits from amalia/06-apr-26 into join 2026-04-06 17:35:19 +08:00
4 changed files with 237 additions and 1 deletions

View File

@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "Village" ADD COLUMN "isDummy" BOOLEAN NOT NULL DEFAULT false;

View File

@@ -51,6 +51,7 @@ model Village {
name String
desc String @db.Text
isActive Boolean @default(true)
isDummy Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
Group Group[]

View File

@@ -0,0 +1,233 @@
import { prisma } from "@/module/_global";
import cors from "@elysiajs/cors";
import { swagger } from "@elysiajs/swagger";
import Elysia from "elysia";
import _ from "lodash";
import moment from "moment";
import "moment/locale/id";
// Gabungkan semua ke dalam satu instance server yang dipasang di /api/monitoring
const MonitoringServer = new Elysia({ prefix: "/api/monitoring" })
.use(cors({
origin: "*",
methods: ["GET", "POST", "OPTIONS"],
}))
.use(swagger({
path: "/docs", // Karena prefix instance adalah /api/monitoring, maka ini akan diakses di /api/monitoring/docs
documentation: {
info: {
title: "Des Plus - Monitoring API",
version: "1.0.0",
description: "API Khusus untuk kebutuhan Dashboard Monitoring",
}
}
}))
.get(
"/grid-overview",
async ({ query, set }) => {
try {
const version = await prisma.setting.findMany({
select: {
id: true,
name: true,
value: true
}
});
const result_version = Object.fromEntries(version.map(item => [item.id, item.value]));
const activity_today = await prisma.userLog.count({
where: {
createdAt: {
gte: moment().subtract(1, 'days').toDate(),
lte: moment().toDate(),
}
}
})
const activity_yesterday = await prisma.userLog.count({
where: {
createdAt: {
gte: moment().subtract(2, 'days').toDate(),
lte: moment().subtract(1, 'days').toDate(),
}
}
})
const activity_increase = (activity_today - activity_yesterday);
const percentage_increase = (activity_increase / activity_yesterday) * 100
const total_village = await prisma.village.findMany({
where: {
isDummy: false
}
})
const total_village_active = total_village.filter((item) => item.isActive).length
const total_village_inactive = total_village.filter((item) => !item.isActive).length
return {
success: true,
message: "Berhasil mendapatkan data",
data: {
version: result_version,
activity: {
today: activity_today,
increase: _.isNaN(percentage_increase) ? 0 : percentage_increase.toFixed(2),
},
village: {
active: total_village_active,
inactive: total_village_inactive,
},
},
};
} catch (error) {
console.error("[overview] grid-overview error:", error);
set.status = 500;
return {
success: false,
message: "Terjadi kesalahan pada server",
data: null,
};
}
},
{
detail: {
summary: "Grid Overview",
description: "Menu Overview - Mendapatkan daftar versi aplikasi.",
tags: ["overview"],
},
}
)
.get(
"/daily-activity",
async ({ query, set }) => {
try {
// const data = await prisma.userLog.findMany({
// where: {
// User: {
// Village: {
// isDummy: false
// }
// },
// createdAt: {
// gte: moment().subtract(7, 'days').toDate(),
// lte: moment().toDate(),
// }
// },
// select: {
// createdAt: true,
// }
// })
const data = await prisma.$queryRaw`
SELECT
DATE(ul."createdAt") AS tanggal,
COUNT(*) AS total
FROM "UserLog" ul
JOIN "User" u ON ul."idUser" = u."id"
JOIN "Village" v ON u."idVillage" = v."id"
WHERE v."isDummy" = false
AND ul."createdAt" >= NOW() - INTERVAL '7 days'
GROUP BY tanggal
ORDER BY tanggal;` as any[];
const result = [];
// ubah data ke map biar gampang lookup
const map = data.reduce((acc: any, item: any) => {
const key = moment(item.tanggal).format('YYYY-MM-DD');
acc[key] = Number(item.total);
return acc;
}, {});
// generate 7 hari terakhir
for (let i = 6; i >= 0; i--) {
const date = moment().subtract(i, 'days');
const key = date.format('YYYY-MM-DD');
result.push({
date: date.format('DD MMM'),
logs: map[key] || 0
});
}
return {
success: true,
message: "Berhasil mendapatkan data",
data: result,
};
} catch (error) {
console.error("[overview] daily-activity error:", error);
set.status = 500;
return {
success: false,
message: "Terjadi kesalahan pada server",
data: null,
};
}
},
{
detail: {
summary: "Daily Activity",
description: "Menu Overview - Mendapatkan data grafik aktivitas harian semua desa.",
tags: ["overview"],
},
}
)
.get(
"/comparison-activity",
async ({ query, set }) => {
try {
const data = await prisma.$queryRaw`
SELECT
v."name",
COUNT(ul."id") AS total_logs
FROM "UserLog" ul
JOIN "User" u ON ul."idUser" = u."id"
JOIN "Village" v ON u."idVillage" = v."id"
WHERE v."isDummy" = false
AND ul."createdAt" >= NOW() - INTERVAL '7 days'
GROUP BY v."id", v."name"
ORDER BY total_logs DESC;
` as any[];
const result = data.map(item => ({
village: item.name,
activity: Number(item.total_logs)
}));
return {
success: true,
message: "Berhasil mendapatkan data",
data: result,
};
} catch (error) {
console.error("[overview] comparison-activity error:", error);
set.status = 500;
return {
success: false,
message: "Terjadi kesalahan pada server",
data: null,
};
}
},
{
detail: {
summary: "Comparison Activity",
description: "Menu Overview - Mendapatkan data grafik perbandingan aktivitas desa selama 7 hari terakhir.",
tags: ["overview"],
},
}
)
;
export const GET = MonitoringServer.handle;
export const POST = MonitoringServer.handle;

View File

@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
export async function GET(request: Request) {
try {
return NextResponse.json({ success: true, version: "2.1.8", tahap: "beta", update: "-api untuk dashboard noc; -perbaikan otp" }, { status: 200 });
return NextResponse.json({ success: true, version: "2.1.9", tahap: "beta", update: "-api untuk dashboard monitoring" }, { 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 });