Merge pull request 'upd: api monitoring' (#29) from amalia/06-apr-26 into join
Reviewed-on: #29
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Village" ADD COLUMN "isDummy" BOOLEAN NOT NULL DEFAULT false;
|
||||||
@@ -51,6 +51,7 @@ model Village {
|
|||||||
name String
|
name String
|
||||||
desc String @db.Text
|
desc String @db.Text
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
|
isDummy Boolean @default(false)
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
Group Group[]
|
Group Group[]
|
||||||
|
|||||||
233
src/app/api/monitoring/[[...slug]]/route.ts
Normal file
233
src/app/api/monitoring/[[...slug]]/route.ts
Normal 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;
|
||||||
@@ -2,7 +2,7 @@ import { NextResponse } from "next/server";
|
|||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
try {
|
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) {
|
} catch (error) {
|
||||||
console.error(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 });
|
return NextResponse.json({ success: false, version: "Gagal mendapatkan version, coba lagi nanti (error: 500)", reason: (error as Error).message, }, { status: 500 });
|
||||||
|
|||||||
Reference in New Issue
Block a user