Deskripsi: - upload surat ke seafile - update struktur db - notif wa kirim link download surat - api download surat No Issues;
1045 lines
29 KiB
TypeScript
1045 lines
29 KiB
TypeScript
import Elysia, { t } from "elysia";
|
|
import fs from 'fs';
|
|
import type { StatusPengaduan } from "generated/prisma";
|
|
import _ from "lodash";
|
|
import path from "path";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import { getLastUpdated } from "../lib/get-last-updated";
|
|
import { mimeToExtension } from "../lib/mimetypeToExtension";
|
|
import { generateNoPengaduan } from "../lib/no-pengaduan";
|
|
import { isValidPhone, normalizePhoneNumber } from "../lib/normalizePhone";
|
|
import { prisma } from "../lib/prisma";
|
|
import { renameFile } from "../lib/rename-file";
|
|
import { catFile, defaultConfigSF, downloadFile, removeFile, uploadFile, uploadFileToFolder } 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"
|
|
},
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
}
|
|
})
|
|
|
|
|
|
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 { success: true, message: '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 { success: true, message: '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 { success: true, message: '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, headers }) => {
|
|
const { judulPengaduan, detailPengaduan, lokasi, namaGambar, kategoriId } = body
|
|
const namaWarga = headers['x-user'] || ""
|
|
const noTelepon = headers['x-phone'] || ""
|
|
let imageFix = namaGambar
|
|
const noPengaduan = await generateNoPengaduan()
|
|
let idCategoryFix = kategoriId
|
|
let idWargaFix = ""
|
|
|
|
if (idCategoryFix) {
|
|
const category = await prisma.categoryPengaduan.findUnique({
|
|
where: {
|
|
id: idCategoryFix,
|
|
}
|
|
})
|
|
|
|
if (!category) {
|
|
const cariCategory = await prisma.categoryPengaduan.findFirst({
|
|
where: {
|
|
name: kategoriId,
|
|
}
|
|
})
|
|
|
|
|
|
if (!cariCategory) {
|
|
idCategoryFix = "lainnya"
|
|
} else {
|
|
idCategoryFix = cariCategory.id
|
|
}
|
|
|
|
|
|
}
|
|
} else {
|
|
idCategoryFix = "lainnya"
|
|
}
|
|
|
|
if (!isValidPhone(noTelepon)) {
|
|
return { success: false, message: `nomor telepon ${noTelepon} tidak valid, harap masukkan nomor yang benar` }
|
|
}
|
|
|
|
const nomorHP = normalizePhoneNumber({ phone: noTelepon })
|
|
const dataWarga = await prisma.warga.upsert({
|
|
where: {
|
|
phone: nomorHP
|
|
},
|
|
create: {
|
|
name: namaWarga,
|
|
phone: nomorHP,
|
|
},
|
|
update: {
|
|
name: namaWarga,
|
|
},
|
|
select: {
|
|
id: true
|
|
}
|
|
})
|
|
|
|
idWargaFix = dataWarga.id
|
|
|
|
|
|
const pengaduan = await prisma.pengaduan.create({
|
|
data: {
|
|
title: judulPengaduan,
|
|
detail: detailPengaduan,
|
|
idCategory: idCategoryFix,
|
|
idWarga: idWargaFix || "",
|
|
location: lokasi,
|
|
image: imageFix,
|
|
noPengaduan,
|
|
},
|
|
select: {
|
|
id: true,
|
|
}
|
|
})
|
|
|
|
if (!pengaduan.id) {
|
|
return { success: false, message: 'gagal membuat pengaduan' }
|
|
}
|
|
|
|
await prisma.historyPengaduan.create({
|
|
data: {
|
|
idPengaduan: pengaduan.id,
|
|
deskripsi: "Pengaduan dibuat",
|
|
}
|
|
})
|
|
|
|
return { success: true, message: 'pengaduan sudah dibuat dengan nomer ' + noPengaduan + ', nomer ini akan digunakan untuk mengakses pengaduan ini' }
|
|
}, {
|
|
body: t.Object({
|
|
judulPengaduan: t.String({
|
|
error: "Judul pengaduan harus diisi",
|
|
examples: ["Sampah menumpuk di depan rumah"],
|
|
description: "Judul singkat dari pengaduan warga"
|
|
}),
|
|
|
|
detailPengaduan: t.String({
|
|
error: "Deskripsi pengaduan harus diisi",
|
|
examples: ["Terdapat sampah yang menumpuk selama seminggu di depan rumah saya"],
|
|
description: "Penjelasan lebih detail mengenai pengaduan"
|
|
}),
|
|
|
|
lokasi: t.String({
|
|
error: "Lokasi pengaduan harus diisi",
|
|
examples: ["Jl. Raya No. 1, RT 01 RW 02, Darmasaba"],
|
|
description: "Alamat atau titik lokasi pengaduan"
|
|
}),
|
|
|
|
namaGambar: t.Optional(t.String({
|
|
examples: ["sampah.jpg"],
|
|
description: "Nama file gambar yang telah diupload (opsional)"
|
|
})),
|
|
|
|
kategoriId: t.Optional(t.String({
|
|
examples: ["kebersihan", "infrastruktur", "keamanan"],
|
|
description: "Nama kategori pengaduan (contoh: kebersihan, keamanan, lainnya)"
|
|
})),
|
|
|
|
// namaWarga: t.String({
|
|
// examples: ["budiman"],
|
|
// description: "Nama warga yang melapor"
|
|
// }),
|
|
|
|
// noTelepon: t.String({
|
|
// error: "Nomor telepon harus diisi",
|
|
// examples: ["08123456789", "+628123456789"],
|
|
// description: "Nomor telepon warga pelapor"
|
|
// }),
|
|
}),
|
|
|
|
detail: {
|
|
summary: "Buat Pengaduan Warga",
|
|
description: `Endpoint ini digunakan untuk membuat data pengaduan (laporan) baru dari 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) {
|
|
return { success: false, message: 'gagal update status 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 { success: true, message: '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`
|
|
}
|
|
})
|
|
.post("/update", async ({ body }) => {
|
|
const { noPengaduan, judul, detail, lokasi, namaGambar } = body
|
|
let dataUpdate = {}
|
|
|
|
const cek = await prisma.pengaduan.findFirst({
|
|
where: {
|
|
noPengaduan,
|
|
},
|
|
select: {
|
|
id: true
|
|
}
|
|
})
|
|
|
|
|
|
if (!cek) {
|
|
return { success: false, message: 'gagal update status pengaduan, nomer ' + noPengaduan + ' tidak ditemukan' }
|
|
}
|
|
|
|
if (judul) {
|
|
dataUpdate = { title: judul }
|
|
}
|
|
|
|
if (detail) {
|
|
dataUpdate = { ...dataUpdate, detail }
|
|
}
|
|
|
|
if (lokasi) {
|
|
dataUpdate = { ...dataUpdate, location: lokasi }
|
|
}
|
|
|
|
if (namaGambar) {
|
|
dataUpdate = { ...dataUpdate, image: namaGambar }
|
|
}
|
|
|
|
const pengaduan = await prisma.pengaduan.updateMany({
|
|
where: {
|
|
noPengaduan
|
|
},
|
|
data: dataUpdate
|
|
})
|
|
|
|
const keys = Object.keys(dataUpdate).join(", ");
|
|
|
|
await prisma.historyPengaduan.create({
|
|
data: {
|
|
idPengaduan: cek.id,
|
|
deskripsi: `Pengaduan diupdate oleh warga (data yg diupdate: ${keys})`,
|
|
}
|
|
})
|
|
|
|
return { success: true, message: 'pengaduan dengan nomer ' + noPengaduan + ' sudah diupdate' }
|
|
}, {
|
|
body: t.Object({
|
|
noPengaduan: t.String({
|
|
error: "nomer pengaduan harus diisi",
|
|
description: "Nomer pengaduan yang ingin diupdate"
|
|
}),
|
|
judul: t.Optional(t.String({
|
|
error: "judul harus diisi",
|
|
description: "Judul pengaduan yang ingin diupdate"
|
|
})),
|
|
detail: t.Optional(t.String({
|
|
description: "detail pengaduan yang ingin diupdate"
|
|
})),
|
|
lokasi: t.Optional(t.String({
|
|
description: "lokasi pengaduan yang ingin diupdate"
|
|
})),
|
|
namaGambar: t.Optional(t.String({
|
|
description: "Nama file gambar yang telah diupload untuk update data pengaduan"
|
|
})),
|
|
}),
|
|
|
|
detail: {
|
|
summary: "Update Data Pengaduan",
|
|
description: `tool untuk update data pengaduan`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.get("/detail", async ({ query }) => {
|
|
const { id } = query
|
|
|
|
const data = await prisma.pengaduan.findFirst({
|
|
where: {
|
|
id: 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,
|
|
phone: true,
|
|
_count: {
|
|
select: {
|
|
Pengaduan: true,
|
|
PelayananAjuan: true,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
if (!data) {
|
|
const datafix = {
|
|
pengaduan: {},
|
|
history: [],
|
|
warga: null,
|
|
}
|
|
|
|
return datafix
|
|
}
|
|
|
|
const dataHistory = await prisma.historyPengaduan.findMany({
|
|
where: {
|
|
idPengaduan: data?.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
deskripsi: true,
|
|
status: true,
|
|
createdAt: true,
|
|
idUser: true,
|
|
User: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
},
|
|
orderBy: {
|
|
createdAt: "desc"
|
|
}
|
|
})
|
|
|
|
|
|
const dataHistoryFix = dataHistory.map((item: any) => ({
|
|
..._.omit(item, ["User", "createdAt"]),
|
|
nameUser: item.User?.name,
|
|
createdAt: item.createdAt
|
|
}))
|
|
|
|
|
|
const warga = {
|
|
name: data?.Warga?.name,
|
|
phone: data?.Warga?.phone,
|
|
pengaduan: data?.Warga?._count.Pengaduan,
|
|
pelayanan: data?.Warga?._count.PelayananAjuan,
|
|
}
|
|
|
|
const dataPengaduan = {
|
|
id: data?.id,
|
|
noPengaduan: data?.noPengaduan,
|
|
title: data?.title,
|
|
detail: data?.detail,
|
|
location: data?.location,
|
|
image: data?.image,
|
|
category: data?.CategoryPengaduan.name,
|
|
status: data?.status,
|
|
keterangan: data?.keterangan,
|
|
createdAt: data?.createdAt,
|
|
updatedAt: data?.updatedAt,
|
|
}
|
|
|
|
const datafix = {
|
|
pengaduan: dataPengaduan,
|
|
history: dataHistoryFix,
|
|
warga: warga,
|
|
}
|
|
|
|
return datafix
|
|
|
|
}, {
|
|
detail: {
|
|
summary: "Detail Pengaduan Warga By ID",
|
|
description: `tool untuk mendapatkan detail pengaduan warga / history pengaduan / mengecek status pengaduan berdasarkan id pengaduan`,
|
|
}
|
|
})
|
|
.get("/", async ({ query, headers }) => {
|
|
// const { take, page, search } = query
|
|
const phone = headers['x-phone'] || ""
|
|
// 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 }),
|
|
// }),
|
|
detail: {
|
|
summary: "List Pengaduan Warga By Phone",
|
|
description: `tool untuk mendapatkan list pengaduan warga by phone`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
.post("/upload", async ({ body }) => {
|
|
const { file, folder } = body;
|
|
|
|
// Validasi file
|
|
if (!file) {
|
|
return { success: false, message: "File tidak ditemukan" };
|
|
}
|
|
|
|
// Rename file
|
|
const renamedFile = renameFile({ oldFile: file, newName: 'random' });
|
|
|
|
|
|
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
|
// const buffer = await file.arrayBuffer();
|
|
const result = await uploadFile(defaultConfigSF, renamedFile, folder);
|
|
if (result == 'gagal') {
|
|
return { success: false, message: "Upload gagal" };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
message: "Upload berhasil",
|
|
filename: renamedFile.name,
|
|
size: renamedFile.size,
|
|
seafileResult: result
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
file: t.Any(),
|
|
folder: t.String(),
|
|
}),
|
|
detail: {
|
|
summary: "Upload File (FormData)",
|
|
description: "Tool untuk upload file ke folder tujuan dengan memakai FormData",
|
|
consumes: ["multipart/form-data"]
|
|
},
|
|
})
|
|
.get("/download", async ({ query, set }) => {
|
|
const { file, folder } = query;
|
|
|
|
// Validasi file
|
|
if (!file) {
|
|
return { success: false, message: "File tidak ditemukan" };
|
|
}
|
|
|
|
// if (!folder) {
|
|
// return { success: false, message: "Folder tidak ditemukan" };
|
|
// }
|
|
|
|
const localPath = path.join("/tmp", file);
|
|
|
|
// Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
|
// const buffer = await file.arrayBuffer();
|
|
const result = await downloadFile(defaultConfigSF, file, 'surat', localPath);
|
|
|
|
if(result=="gagal") {
|
|
return { success: false, message: "Download gagal" };
|
|
}
|
|
|
|
set.headers["Content-Type"] = "application/pdf";
|
|
set.headers["Content-Disposition"] = `attachment; filename="${file}"`;
|
|
|
|
// 🔹 kirim file ke browser
|
|
return fs.createReadStream(localPath);
|
|
}, {
|
|
body: t.Object({
|
|
file: t.Any(),
|
|
folder: t.String(),
|
|
}),
|
|
detail: {
|
|
summary: "Download Surat",
|
|
description: "Tool untuk download surat dari Seafile",
|
|
},
|
|
})
|
|
.post("/upload-file-form-data", async ({ body }) => {
|
|
const { file } = body;
|
|
|
|
// // Validasi file
|
|
// if (!file) {
|
|
// return { success: false, message: "File tidak ditemukan" };
|
|
// }
|
|
|
|
// // Rename file
|
|
// const renamedFile = renameFile({ oldFile: file, newName: 'random' });
|
|
|
|
|
|
// // Upload ke Seafile (pastikan uploadFile menerima Blob atau ArrayBuffer)
|
|
// // const buffer = await file.arrayBuffer();
|
|
// const result = await uploadFile(defaultConfigSF, renamedFile, 'pengaduan');
|
|
// if (result == 'gagal') {
|
|
// return { success: false, message: "Upload gagal" };
|
|
// }
|
|
|
|
return {
|
|
success: true,
|
|
file: JSON.stringify(file),
|
|
fileInfo: {
|
|
name: file.name || 'kosong',
|
|
size: file.size || 0,
|
|
type: file.type || 'kosong'
|
|
}
|
|
// message: "Upload berhasil",
|
|
// filename: renamedFile.name,
|
|
// size: renamedFile.size,
|
|
// seafileResult: result
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
file: t.Any(),
|
|
// folder: t.String(),
|
|
}),
|
|
detail: {
|
|
summary: "Upload File (FormData)",
|
|
description: "Tool untuk upload file ke folder tujuan dengan memakai FormData",
|
|
consumes: ["multipart/form-data"]
|
|
},
|
|
})
|
|
.post("/upload-base64", async ({ body }) => {
|
|
const { data, mimetype, kategori } = body;
|
|
const ext = mimeToExtension(mimetype)
|
|
const name = `${uuidv4()}.${ext}`
|
|
const kategoriFix = kategori === 'pengaduan' ? 'pengaduan' : 'syarat-dokumen';
|
|
|
|
// Validasi file
|
|
if (!data) {
|
|
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: name, data: data });
|
|
const result = await uploadFileToFolder(defaultConfigSF, { name: name, data: data }, kategoriFix);
|
|
|
|
return {
|
|
success: true,
|
|
message: "Upload berhasil",
|
|
data: {
|
|
name,
|
|
mimetype,
|
|
ext,
|
|
kategori,
|
|
}
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
data: t.String(),
|
|
mimetype: t.String(),
|
|
kategori: t.String()
|
|
}),
|
|
detail: {
|
|
summary: "Upload File (Base64)",
|
|
description: "Tool untuk upload file ke Seafile dalam format Base64",
|
|
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 totalData = await prisma.pengaduan.count({
|
|
where
|
|
});
|
|
|
|
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,
|
|
id: item.id,
|
|
title: item.title,
|
|
detail: item.detail,
|
|
status: item.status,
|
|
location: item.location,
|
|
createdAt: item.createdAt.toISOString(),
|
|
updatedAt: 'terakhir diperbarui ' + getLastUpdated(item.updatedAt),
|
|
}
|
|
})
|
|
|
|
const dataReturn = {
|
|
data: dataFix,
|
|
total: totalData,
|
|
page: Number(page) || 1,
|
|
pageSize: !take ? 10 : Number(take),
|
|
totalPages: Math.ceil(totalData / (!take ? 10 : Number(take)))
|
|
}
|
|
|
|
return dataReturn
|
|
}, {
|
|
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`,
|
|
}
|
|
})
|
|
.get("/image", async ({ query, set }) => {
|
|
const { fileName, folder } = query;
|
|
|
|
const hasil = await catFile(defaultConfigSF, folder, fileName);
|
|
|
|
const ext = fileName.split(".").pop()?.toLowerCase();
|
|
let mime = "application/octet-stream"; // default
|
|
|
|
if (["jpg", "jpeg"].includes(ext!)) mime = "image/jpeg";
|
|
if (["png"].includes(ext!)) mime = "image/png";
|
|
if (["gif"].includes(ext!)) mime = "image/gif";
|
|
if (["webp"].includes(ext!)) mime = "image/webp";
|
|
if (["svg"].includes(ext!)) mime = "image/svg+xml";
|
|
if (["pdf"].includes(ext!)) mime = "application/pdf";
|
|
|
|
set.headers["Content-Type"] = mime;
|
|
set.headers["Content-Length"] = hasil.byteLength.toString();
|
|
|
|
return new Response(hasil);
|
|
}, {
|
|
query: t.Object({
|
|
fileName: t.String(),
|
|
folder: t.String()
|
|
}),
|
|
detail: {
|
|
summary: "View Gambar",
|
|
description: "tool untuk mendapatkan gambar",
|
|
}
|
|
})
|
|
.post("/delete-image", async ({ body }) => {
|
|
const { file, folder } = body;
|
|
|
|
// Validasi file
|
|
if (!file) {
|
|
return { success: false, message: "File tidak ditemukan" };
|
|
}
|
|
|
|
const result = await removeFile(defaultConfigSF, file, folder);
|
|
if (result == 'gagal') {
|
|
return { success: false, message: "Delete gagal" };
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
message: "Delete berhasil",
|
|
};
|
|
}, {
|
|
body: t.Object({
|
|
file: t.String(),
|
|
folder: t.String(),
|
|
}),
|
|
detail: {
|
|
summary: "Delete File",
|
|
description: "Tool untuk delete file Seafile",
|
|
},
|
|
})
|
|
.post("/detail-data", async ({ body }) => {
|
|
const { nomerPengaduan } = body
|
|
|
|
const data = await prisma.pengaduan.findFirst({
|
|
where: {
|
|
noPengaduan: nomerPengaduan
|
|
},
|
|
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,
|
|
phone: true,
|
|
_count: {
|
|
select: {
|
|
Pengaduan: true,
|
|
PelayananAjuan: true,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
if (!data) {
|
|
return { success: false, message: "Data tidak ditemukan" };
|
|
}
|
|
|
|
const dataHistory = await prisma.historyPengaduan.findMany({
|
|
where: {
|
|
idPengaduan: data?.id,
|
|
},
|
|
select: {
|
|
id: true,
|
|
deskripsi: true,
|
|
status: true,
|
|
createdAt: true,
|
|
idUser: true,
|
|
User: {
|
|
select: {
|
|
name: true,
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
|
|
const dataHistoryFix = dataHistory.map((item: any) => ({
|
|
..._.omit(item, ["User", "createdAt"]),
|
|
nameUser: item.User?.name,
|
|
createdAt: item.createdAt
|
|
}))
|
|
|
|
|
|
const warga = {
|
|
name: data?.Warga?.name,
|
|
phone: data?.Warga?.phone,
|
|
pengaduan: data?.Warga?._count.Pengaduan,
|
|
pelayanan: data?.Warga?._count.PelayananAjuan,
|
|
}
|
|
|
|
const dataPengaduan = {
|
|
id: data?.id,
|
|
noPengaduan: data?.noPengaduan,
|
|
title: data?.title,
|
|
detail: data?.detail,
|
|
location: data?.location,
|
|
image: data?.image,
|
|
category: data?.CategoryPengaduan.name,
|
|
status: data?.status,
|
|
keterangan: data?.keterangan,
|
|
createdAt: data?.createdAt,
|
|
updatedAt: data?.updatedAt,
|
|
}
|
|
|
|
const datafix = {
|
|
pengaduan: dataPengaduan,
|
|
history: dataHistoryFix,
|
|
warga: warga,
|
|
}
|
|
|
|
return datafix
|
|
}, {
|
|
body: t.Object({
|
|
nomerPengaduan: t.String({
|
|
description: "Nomer pengaduan yg ingin diakses",
|
|
examples: ["PGD-101225-001", "PGD-101225-002"],
|
|
error: "Nomer pengaduan harus diisi",
|
|
}),
|
|
}),
|
|
detail: {
|
|
summary: "Detail Pengaduan Warga By Nomor Pengaduan",
|
|
description: `tool untuk mendapatkan detail data pengaduan berdasarkan nomor pengaduan`,
|
|
tags: ["mcp"]
|
|
}
|
|
})
|
|
;
|
|
|
|
export default PengaduanRoute
|