633 lines
16 KiB
TypeScript
633 lines
16 KiB
TypeScript
import Elysia, { t } from "elysia"
|
|
import type { StatusPengaduan } from "generated/prisma"
|
|
import { getLastUpdated } from "../lib/get-last-updated"
|
|
import { generateNoPengaduan } from "../lib/no-pengaduan"
|
|
import { normalizePhoneNumber } from "../lib/normalizePhone"
|
|
import { prisma } from "../lib/prisma"
|
|
import { defaultConfigSF, uploadFile, uploadFileBase64 } from "../lib/seafile"
|
|
|
|
const PengaduanRoute = new Elysia({
|
|
prefix: "pengaduan",
|
|
tags: ["pengaduan"],
|
|
})
|
|
|
|
// --- KATEGORI PENGADUAN ---
|
|
.get("/category", async () => {
|
|
const data = await prisma.categoryPengaduan.findMany({
|
|
where: {
|
|
isActive: true
|
|
},
|
|
orderBy: {
|
|
name: "asc"
|
|
}
|
|
})
|
|
return data
|
|
}, {
|
|
detail: {
|
|
summary: "List Kategori Pengaduan",
|
|
description: `tool untuk mendapatkan list kategori pengaduan`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.post("/category/create", async ({ body }) => {
|
|
const { name } = body
|
|
|
|
await prisma.categoryPengaduan.create({
|
|
data: {
|
|
name,
|
|
}
|
|
})
|
|
|
|
return `
|
|
${JSON.stringify(body)}
|
|
|
|
kategori pengaduan sudah dibuat`
|
|
}, {
|
|
body: t.Object({
|
|
name: t.String({ minLength: 1, error: "name harus diisi" }),
|
|
}),
|
|
detail: {
|
|
summary: "buat kategori pengaduan",
|
|
description: `tool untuk membuat kategori pengaduan`
|
|
}
|
|
})
|
|
.post("/category/update", async ({ body }) => {
|
|
const { id, name } = body
|
|
|
|
await prisma.categoryPengaduan.update({
|
|
where: {
|
|
id,
|
|
},
|
|
data: {
|
|
name
|
|
}
|
|
})
|
|
|
|
return `
|
|
${JSON.stringify(body)}
|
|
|
|
kategori pengaduan sudah diperbarui`
|
|
}, {
|
|
body: t.Object({
|
|
id: t.String({ minLength: 1, error: "id harus diisi" }),
|
|
name: t.String({ minLength: 1, error: "name harus diisi" }),
|
|
}),
|
|
detail: {
|
|
summary: "update kategori pengaduan",
|
|
description: `tool untuk update kategori pengaduan`
|
|
}
|
|
})
|
|
.post("/category/delete", async ({ body }) => {
|
|
const { id } = body
|
|
|
|
await prisma.categoryPengaduan.update({
|
|
where: {
|
|
id,
|
|
},
|
|
data: {
|
|
isActive: false
|
|
}
|
|
})
|
|
|
|
return `
|
|
${JSON.stringify(body)}
|
|
|
|
kategori pengaduan sudah dihapus`
|
|
}, {
|
|
body: t.Object({
|
|
id: t.String({ minLength: 1, error: "id harus diisi" }),
|
|
}),
|
|
detail: {
|
|
summary: "delete kategori pengaduan",
|
|
description: `tool untuk delete kategori pengaduan`
|
|
}
|
|
})
|
|
|
|
|
|
|
|
// --- PENGADUAN ---
|
|
.post("/create", async ({ body }) => {
|
|
const { title, detail, location, image, idCategory, idWarga, phone } = body
|
|
const noPengaduan = await generateNoPengaduan()
|
|
let idCategoryFix = idCategory
|
|
let idWargaFix = idWarga
|
|
const category = await prisma.categoryPengaduan.findUnique({
|
|
where: {
|
|
id: idCategory,
|
|
}
|
|
})
|
|
|
|
if (!category) {
|
|
const cariCategory = await prisma.categoryPengaduan.findFirst({
|
|
where: {
|
|
name: idCategory,
|
|
}
|
|
})
|
|
|
|
if (!cariCategory) {
|
|
idCategoryFix = "lainnya"
|
|
} else {
|
|
idCategoryFix = cariCategory.id
|
|
}
|
|
|
|
}
|
|
|
|
const warga = await prisma.warga.findUnique({
|
|
where: {
|
|
id: idWarga,
|
|
}
|
|
})
|
|
|
|
if (!warga) {
|
|
const nomorHP = normalizePhoneNumber({ phone })
|
|
const cariWarga = await prisma.warga.findUnique({
|
|
where: {
|
|
phone: nomorHP,
|
|
}
|
|
})
|
|
|
|
if (!cariWarga) {
|
|
const wargaCreate = await prisma.warga.create({
|
|
data: {
|
|
name: idWarga,
|
|
phone: nomorHP,
|
|
},
|
|
select: {
|
|
id: true
|
|
}
|
|
})
|
|
idWargaFix = wargaCreate.id
|
|
} else {
|
|
idWargaFix = cariWarga.id
|
|
}
|
|
|
|
}
|
|
|
|
const pengaduan = await prisma.pengaduan.create({
|
|
data: {
|
|
title,
|
|
detail,
|
|
idCategory: idCategoryFix,
|
|
idWarga: idWargaFix,
|
|
location,
|
|
image,
|
|
noPengaduan,
|
|
},
|
|
select: {
|
|
id: true,
|
|
}
|
|
})
|
|
|
|
if (!pengaduan.id) {
|
|
throw new Error("gagal membuat pengaduan")
|
|
}
|
|
|
|
await prisma.historyPengaduan.create({
|
|
data: {
|
|
idPengaduan: pengaduan.id,
|
|
deskripsi: "Pengaduan dibuat",
|
|
}
|
|
})
|
|
|
|
return `
|
|
${JSON.stringify(body)}
|
|
|
|
pengaduan sudah dibuat`
|
|
}, {
|
|
body: t.Object({
|
|
title: t.String({ minLength: 1, error: "title harus diisi" }),
|
|
detail: t.String({ minLength: 1, error: "detail harus diisi" }),
|
|
location: t.String({ minLength: 1, error: "location harus diisi" }),
|
|
image: t.Any(),
|
|
idCategory: t.String({ minLength: 1, error: "idCategory harus diisi" }),
|
|
idWarga: t.String({ minLength: 1, error: "idWarga harus diisi" }),
|
|
phone: t.String({ minLength: 1, error: "phone harus diisi" }),
|
|
}),
|
|
detail: {
|
|
summary: "Create Pengaduan Warga",
|
|
description: `tool untuk membuat pengaduan warga`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.post("/update-status", async ({ body }) => {
|
|
const { id, status, keterangan, idUser } = body
|
|
let deskripsi = ""
|
|
|
|
const pengaduan = await prisma.pengaduan.update({
|
|
where: {
|
|
id,
|
|
},
|
|
data: {
|
|
status: status as StatusPengaduan,
|
|
keterangan,
|
|
}
|
|
})
|
|
|
|
if (!pengaduan) {
|
|
throw new Error("gagal membuat pengaduan")
|
|
}
|
|
|
|
if (status === "diterima") {
|
|
deskripsi = "Pengaduan diterima oleh admin"
|
|
} else if (status === "dikerjakan") {
|
|
deskripsi = "Pengaduan dikerjakan oleh petugas"
|
|
} else if (status === "ditolak") {
|
|
deskripsi = "Pengaduan ditolak dengan keterangan " + keterangan
|
|
} else if (status === "selesai") {
|
|
deskripsi = "Pengaduan selesai"
|
|
}
|
|
|
|
await prisma.historyPengaduan.create({
|
|
data: {
|
|
idPengaduan: pengaduan.id,
|
|
deskripsi,
|
|
status: status as StatusPengaduan,
|
|
idUser,
|
|
}
|
|
})
|
|
|
|
return `
|
|
${JSON.stringify(body)}
|
|
|
|
status pengaduan sudah diupdate`
|
|
}, {
|
|
body: t.Object({
|
|
id: t.String({ minLength: 1, error: "id harus diisi" }),
|
|
status: t.String({ minLength: 1, error: "status harus diisi" }),
|
|
keterangan: t.Any(),
|
|
idUser: t.String({ minLength: 1, error: "idUser harus diisi" }),
|
|
}),
|
|
|
|
detail: {
|
|
summary: "Update status pengaduan",
|
|
description: `tool untuk update status pengaduan`
|
|
}
|
|
})
|
|
.get("/detail", async ({ query }) => {
|
|
const { id } = query
|
|
const data = await prisma.pengaduan.findUnique({
|
|
where: {
|
|
id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
noPengaduan: true,
|
|
title: true,
|
|
detail: true,
|
|
location: true,
|
|
image: true,
|
|
idCategory: true,
|
|
idWarga: true,
|
|
status: true,
|
|
keterangan: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
CategoryPengaduan: {
|
|
select: {
|
|
name: true
|
|
}
|
|
},
|
|
Warga: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
const dataHistory = await prisma.historyPengaduan.findMany({
|
|
where: {
|
|
idPengaduan: id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
deskripsi: true,
|
|
status: true,
|
|
createdAt: true,
|
|
idUser: true,
|
|
User: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
const dataHistoryFix = dataHistory.map((item) => {
|
|
return {
|
|
id: item.id,
|
|
deskripsi: item.deskripsi,
|
|
status: item.status,
|
|
createdAt: item.createdAt,
|
|
idUser: item.idUser,
|
|
nameUser: item.User?.name,
|
|
}
|
|
})
|
|
|
|
const datafix = {
|
|
id: data?.id,
|
|
noPengaduan: data?.noPengaduan,
|
|
title: data?.title,
|
|
detail: data?.detail,
|
|
location: data?.location,
|
|
image: data?.image,
|
|
CategoryPengaduan: data?.CategoryPengaduan.name,
|
|
idWarga: data?.idWarga,
|
|
nameWarga: data?.Warga?.name,
|
|
status: data?.status,
|
|
keterangan: data?.keterangan,
|
|
createdAt: data?.createdAt,
|
|
updatedAt: data?.updatedAt,
|
|
history: dataHistoryFix,
|
|
}
|
|
|
|
return datafix
|
|
}, {
|
|
detail: {
|
|
summary: "Detail Pengaduan Warga",
|
|
description: `tool untuk mendapatkan detail pengaduan warga / history pengaduan / mengecek status pengaduan`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.get("/", async ({ query }) => {
|
|
const { take, page, search, phone } = query
|
|
const skip = !page ? 0 : (Number(page) - 1) * (!take ? 10 : Number(take))
|
|
|
|
const data = await prisma.pengaduan.findMany({
|
|
skip,
|
|
take: !take ? 10 : Number(take),
|
|
orderBy: {
|
|
createdAt: "asc"
|
|
},
|
|
where: {
|
|
isActive: true,
|
|
OR: [
|
|
{
|
|
title: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
{
|
|
noPengaduan: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
{
|
|
detail: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
}
|
|
],
|
|
AND: {
|
|
Warga: {
|
|
phone: phone
|
|
}
|
|
}
|
|
},
|
|
select: {
|
|
id: true,
|
|
noPengaduan: true,
|
|
title: true,
|
|
detail: true,
|
|
location: true,
|
|
status: true,
|
|
createdAt: true,
|
|
CategoryPengaduan: {
|
|
select: {
|
|
name: true
|
|
}
|
|
},
|
|
Warga: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
const dataFix = data.map((item) => {
|
|
return {
|
|
noPengaduan: item.noPengaduan,
|
|
title: item.title,
|
|
detail: item.detail,
|
|
status: item.status,
|
|
createdAt: item.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }),
|
|
}
|
|
})
|
|
|
|
return dataFix
|
|
}, {
|
|
query: t.Object({
|
|
take: t.String({ optional: true }),
|
|
page: t.String({ optional: true }),
|
|
search: t.String({ optional: true }),
|
|
phone: t.String({ minLength: 11, error: "phone harus diisi" }),
|
|
}),
|
|
detail: {
|
|
summary: "List Pengaduan Warga By Phone",
|
|
description: `tool untuk mendapatkan list pengaduan warga by phone`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.post("/upload", async ({ body }) => {
|
|
const { file } = body;
|
|
|
|
// Validasi file
|
|
if (!file) {
|
|
return { success: false, message: "File tidak ditemukan" };
|
|
}
|
|
|
|
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
|
// const buffer = await file.arrayBuffer();
|
|
const result = await uploadFile(defaultConfigSF, file);
|
|
|
|
return {
|
|
success: true,
|
|
message: "Upload berhasil",
|
|
filename: file.name,
|
|
size: file.size,
|
|
seafileResult: result
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
file: t.File({ format: "binary" })
|
|
}),
|
|
detail: {
|
|
summary: "Upload File",
|
|
description: "Tool untuk upload file ke Seafile",
|
|
tags: ["mcp"],
|
|
consumes: ["multipart/form-data"]
|
|
},
|
|
})
|
|
.post("/upload-base64", async ({ body }) => {
|
|
const { file } = body;
|
|
|
|
// Validasi file
|
|
if (!file) {
|
|
return { success: false, message: "File tidak ditemukan" };
|
|
}
|
|
|
|
// Konversi file ke base64
|
|
// const buffer = await file.arrayBuffer();
|
|
// const base64String = Buffer.from(buffer).toString("base64");
|
|
|
|
// (Opsional) jika perlu dikirim ke Seafile sebagai base64
|
|
const result = await uploadFileBase64(defaultConfigSF, { name: 'contoh', data: file });
|
|
|
|
return {
|
|
success: true,
|
|
message: "Upload berhasil",
|
|
// filename: file.name,
|
|
// size: file.size,
|
|
// base64Preview: base64String.slice(0, 100) + "...", // hanya preview
|
|
seafileResult: result
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
file: t.String()
|
|
}),
|
|
detail: {
|
|
summary: "Upload File (Base64)",
|
|
description: "Tool untuk upload file ke Seafile dalam format Base64",
|
|
tags: ["mcp"],
|
|
consumes: ["multipart/form-data"]
|
|
},
|
|
})
|
|
|
|
.get("/list", async ({ query }) => {
|
|
const { take, page, search, status } = query
|
|
const skip = !page ? 0 : (Number(page) - 1) * (!take ? 10 : Number(take))
|
|
|
|
let where: any = {
|
|
isActive: true,
|
|
OR: [
|
|
{
|
|
title: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
{
|
|
noPengaduan: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
{
|
|
detail: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
{
|
|
Warga: {
|
|
phone: {
|
|
contains: search ?? "",
|
|
mode: "insensitive"
|
|
},
|
|
},
|
|
}
|
|
]
|
|
}
|
|
|
|
if (status && status !== "semua") {
|
|
where = {
|
|
...where,
|
|
status: status
|
|
}
|
|
}
|
|
|
|
const data = await prisma.pengaduan.findMany({
|
|
skip,
|
|
take: !take ? 10 : Number(take),
|
|
orderBy: {
|
|
createdAt: "desc"
|
|
},
|
|
where,
|
|
select: {
|
|
id: true,
|
|
noPengaduan: true,
|
|
title: true,
|
|
detail: true,
|
|
location: true,
|
|
status: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
CategoryPengaduan: {
|
|
select: {
|
|
name: true
|
|
}
|
|
},
|
|
Warga: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
const dataFix = data.map((item) => {
|
|
return {
|
|
noPengaduan: item.noPengaduan,
|
|
title: item.title,
|
|
detail: item.detail,
|
|
status: item.status,
|
|
location: item.location,
|
|
createdAt: item.createdAt.toLocaleDateString("id-ID", { day: "numeric", month: "long", year: "numeric" }),
|
|
updatedAt: 'terakhir diperbarui ' + getLastUpdated(item.updatedAt),
|
|
}
|
|
})
|
|
|
|
return dataFix
|
|
}, {
|
|
query: t.Object({
|
|
take: t.String({ optional: true }),
|
|
page: t.String({ optional: true }),
|
|
search: t.String({ optional: true }),
|
|
status: t.String({ optional: true }),
|
|
}),
|
|
detail: {
|
|
summary: "List Pengaduan Warga",
|
|
description: `tool untuk mendapatkan list pengaduan warga`,
|
|
}
|
|
})
|
|
.get("/count", async ({ query }) => {
|
|
const counts = await prisma.pengaduan.groupBy({
|
|
by: ['status'],
|
|
where: {
|
|
isActive: true,
|
|
},
|
|
_count: {
|
|
status: true,
|
|
},
|
|
});
|
|
|
|
const grouped = Object.fromEntries(
|
|
counts.map(c => [c.status, c._count.status])
|
|
);
|
|
|
|
const total = await prisma.pengaduan.count({
|
|
where: { isActive: true },
|
|
});
|
|
|
|
return {
|
|
antrian: grouped?.antrian || 0,
|
|
diterima: grouped?.diterima || 0,
|
|
dikerjakan: grouped?.dikerjakan || 0,
|
|
ditolak: grouped?.ditolak || 0,
|
|
selesai: grouped?.selesai || 0,
|
|
semua: total,
|
|
};
|
|
}, {
|
|
detail: {
|
|
summary: "Jumlah Pengaduan Warga",
|
|
description: `tool untuk mendapatkan jumlah pengaduan warga`,
|
|
}
|
|
})
|
|
;
|
|
|
|
export default PengaduanRoute
|