feat: tambah endpoint inactive-users dan lengkapi field response-nya

This commit is contained in:
2026-05-28 14:32:49 +08:00
parent e5891f0da3
commit 1df1d10c91

View File

@@ -1572,6 +1572,116 @@ const MonitoringServer = new Elysia({ prefix: "/api/monitoring" })
}
)
.get("/inactive-users", async ({ query, set }) => {
const VALID_DAYS = [7, 14, 30];
const days = VALID_DAYS.includes(Number(query.days)) ? Number(query.days) : 7;
const pageNum = Number(query.page ?? 1) || 1;
const take = 15;
const { idVillage } = query;
try {
const users = await prisma.user.findMany({
where: {
idUserRole: { not: 'developer' },
...(idVillage && { idVillage }),
},
select: {
id: true,
name: true,
nik: true,
email: true,
phone: true,
gender: true,
isActive: true,
isWithoutOTP: true,
isApprover: true,
idUserRole: true,
idVillage: true,
idGroup: true,
idPosition: true,
UserRole: { select: { name: true } },
Village: { select: { id: true, name: true } },
Group: { select: { name: true } },
Position: { select: { name: true } },
UserLog: {
orderBy: { createdAt: 'desc' },
take: 1,
select: { createdAt: true },
},
},
});
const threshold = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
const inactive = users
.map((u) => ({
id: u.id,
name: u.name,
nik: u.nik,
email: u.email,
phone: u.phone,
gender: u.gender,
isActive: u.isActive,
isWithoutOTP: u.isWithoutOTP,
isApprover: u.isApprover,
role: u.UserRole?.name ?? null,
idUserRole: u.idUserRole,
village: u.Village?.name ?? null,
idVillage: u.Village?.id ?? null,
group: u.Group?.name ?? null,
position: u.Position?.name ?? null,
idGroup: u.idGroup,
idPosition: u.idPosition,
lastActivity: u.UserLog[0]?.createdAt ?? null,
daysSince: u.UserLog[0]?.createdAt
? Math.floor((Date.now() - u.UserLog[0].createdAt.getTime()) / (1000 * 60 * 60 * 24))
: null,
}))
.filter((u) => u.lastActivity === null || u.lastActivity < threshold)
.sort((a, b) => {
if (a.lastActivity === null) return -1;
if (b.lastActivity === null) return 1;
return a.lastActivity.getTime() - b.lastActivity.getTime();
});
const total = inactive.length;
const paginated = inactive.slice((pageNum - 1) * take, pageNum * take);
return {
success: true,
message: "Berhasil mendapatkan data",
data: {
users: paginated,
total,
totalPage: Math.ceil(total / take),
currentPage: pageNum,
days,
},
};
} catch (error) {
console.error("[inactive-users] error:", error);
set.status = 500;
return {
success: false,
message: "Terjadi kesalahan pada server",
data: null,
};
}
},
{
query: t.Object({
days: t.Optional(t.String({ description: "Threshold hari tidak aktif: 7, 14, atau 30 (default: 7)" })),
idVillage: t.Optional(t.String({ description: "Filter berdasarkan ID desa" })),
page: t.Optional(t.String({ description: "Halaman (default: 1)" })),
}),
detail: {
summary: "Inactive Users",
description: "Mendapatkan daftar user yang tidak ada aktivitas dalam X hari terakhir, atau belum pernah ada aktivitas sama sekali.",
tags: ["user"],
},
}
)
// ─── API KEY MANAGEMENT ──────────────────────────────────────────────────
.get("/api-keys", async ({ set }) => {