Compare commits

...

23 Commits

Author SHA1 Message Date
6428f5084e upd: api jenna ai 2025-12-09 17:16:24 +08:00
bfc292ec6c upd: api jenna ai 2025-12-09 17:15:08 +08:00
5680466c98 upd: api 2025-12-09 16:17:39 +08:00
f5cc45937c upd: jenna ai mcp 2025-12-09 15:23:21 +08:00
5b4164b151 Merge pull request 'upd: api pelayanan jenna ai' (#64) from amalia/09-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/64
2025-12-09 14:15:20 +08:00
225c58b346 upd: api pelayanan jenna ai 2025-12-09 14:14:04 +08:00
b8b3aed86e Merge pull request 'upd: api jenna ai' (#63) from amalia/08-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/63
2025-12-08 16:31:00 +08:00
fc530399dd upd: api jenna ai 2025-12-08 16:28:37 +08:00
281e34ea69 Merge pull request 'upd' (#62) from amalia/08-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/62
2025-12-08 14:51:18 +08:00
f928fc504f upd 2025-12-08 14:50:29 +08:00
4fb98d0480 Merge pull request 'upd: api' (#61) from amalia/08-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/61
2025-12-08 14:32:54 +08:00
bfb33e2105 upd: api 2025-12-08 14:32:05 +08:00
2579714000 Merge pull request 'upd' (#60) from amalia/08-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/60
2025-12-08 14:12:36 +08:00
d69189cf7d upd
:  api tambah pengaduan
2025-12-08 14:10:34 +08:00
20e24a03aa Merge pull request 'amalia/08-des-25' (#59) from amalia/08-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/59
2025-12-08 11:50:41 +08:00
c256f4b729 upd: api jenna ai
Deskripsi:
- create pengaduan pake nama dan nomer hp dari header

No Issues
2025-12-08 11:49:24 +08:00
9430ad3728 upd: update data pengaduan dari wawrga 2025-12-08 11:37:16 +08:00
c6c3ba95f8 upd: update data pengaduan by jenna AI 2025-12-08 11:04:03 +08:00
bipproduction
3c58230c3a tambahan untuk handle true user 2025-12-08 10:16:27 +08:00
d22b4b973f Merge pull request 'upd: api jenna ai' (#58) from amalia/02-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/58
2025-12-02 17:27:35 +08:00
9b7a61e134 Merge pull request 'upd: api jenna ai' (#57) from amalia/02-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/57
2025-12-02 14:40:07 +08:00
7c669f3494 Merge pull request 'fix: dashboard admin' (#56) from amalia/02-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/56
2025-12-02 12:01:42 +08:00
b9984c6337 Merge pull request 'amalia/02-des-25' (#55) from amalia/02-des-25 into main
Reviewed-on: http://wibugit.wibudev.com/wibu/jenna-mcp/pulls/55
2025-12-02 11:51:54 +08:00
4 changed files with 133 additions and 31 deletions

View File

@@ -1,6 +1,6 @@
export function isValidPhone(number: string): boolean {
const clean = number.replace(/[\s.-]/g, ""); // hapus spasi, titik, strip
const regex = /^(?:\+628|08)(\d{7,12})$/;
const regex = /^(?:\+62|62|0)8\d{7,12}$/;
return regex.test(clean);
}

View File

@@ -128,7 +128,8 @@ function convertToMcpContent(payload: any) {
export async function executeTool(
tool: any,
args: Record<string, any> = {},
baseUrl: string
baseUrl: string,
xPayload: Record<string, any> = {}
) {
const x = tool["x-props"] || {};
const method = (x.method || "GET").toUpperCase();
@@ -247,6 +248,9 @@ export async function executeTool(
// Execute fetch
console.log(`[MCP] → ${method} ${url}`);
for(const [key, value] of Object.entries(xPayload)) {
opts.headers![key] = value;
}
const res = await fetch(url, opts);
const resContentType = (res.headers.get("content-type") || "").toLowerCase();
@@ -281,7 +285,7 @@ export async function executeTool(
/* -------------------------
JSON-RPC Handler
------------------------- */
async function handleMCPRequestAsync(request: JSONRPCRequest): Promise<JSONRPCResponse> {
async function handleMCPRequestAsync(request: JSONRPCRequest, xPayload: Record<string, any>): Promise<JSONRPCResponse> {
const { id, method, params } = request;
const makeError = (code: number, message: string, data?: any): JSONRPCResponse => ({
@@ -331,7 +335,7 @@ async function handleMCPRequestAsync(request: JSONRPCRequest): Promise<JSONRPCRe
const baseUrl = (params?.credentials?.baseUrl as string) || process.env.BUN_PUBLIC_BASE_URL || "http://localhost:3000";
const args = params?.arguments || {};
const result = await executeTool(tool, args, baseUrl);
const result = await executeTool(tool, args, baseUrl, xPayload);
// Extract the meaningful payload (prefer nested .data if present)
const raw = extractRaw(result.data);
@@ -365,7 +369,7 @@ async function handleMCPRequestAsync(request: JSONRPCRequest): Promise<JSONRPCRe
Elysia App & Routes
------------------------- */
export const MCPRoute = new Elysia({ tags: ["MCP Server"] })
.post("/mcp", async ({ request, set }) => {
.post("/mcp", async ({ request, set, headers }) => {
set.headers["Content-Type"] = "application/json";
set.headers["Access-Control-Allow-Origin"] = "*";
@@ -378,12 +382,17 @@ export const MCPRoute = new Elysia({ tags: ["MCP Server"] })
}
}
const xPayload = {
['x-user']: headers['x-user'] || "",
['x-phone']: headers['x-phone'] || ""
}
try {
const body = await request.json();
// If batch array -> allSettled for resilience
if (Array.isArray(body)) {
const promises = body.map((req: JSONRPCRequest) => handleMCPRequestAsync(req));
const promises = body.map((req: JSONRPCRequest) => handleMCPRequestAsync(req, xPayload));
const settled = await Promise.allSettled(promises);
const responses = settled.map((s) =>
s.status === "fulfilled"
@@ -401,7 +410,7 @@ export const MCPRoute = new Elysia({ tags: ["MCP Server"] })
return responses;
}
const single = await handleMCPRequestAsync(body as JSONRPCRequest);
const single = await handleMCPRequestAsync(body as JSONRPCRequest, xPayload);
return single;
} catch (err: any) {
set.status = 400;

View File

@@ -291,11 +291,14 @@ const PelayananRoute = new Elysia({
tags: ["mcp"]
}
})
.post("/create", async ({ body }) => {
const { kategoriId, namaWarga, noTelepon, dataText, syaratDokumen } = body
.post("/create", async ({ body, headers }) => {
const { kategoriId, dataText, syaratDokumen } = body
const namaWarga = headers['x-user'] || ""
const noTelepon = headers['x-phone'] || ""
const noPengajuan = await generateNoPengajuanSurat()
let idCategoryFix = kategoriId
let idWargaFix = ""
const category = await prisma.categoryPelayanan.findUnique({
where: {
id: kategoriId,
@@ -359,6 +362,10 @@ const PelayananRoute = new Elysia({
let dataInsertDataText = []
for (const item of syaratDokumen) {
console.log('syarat dokumen', item)
const jenisFix = (category?.syaratDokumen as Array<{ name: string; desc: string }>)
?.find((v) => v.name === item.jenis || v.desc === item.jenis);
console.log('jenis fix', jenisFix)
dataInsertSyaratDokumen.push({
idPengajuanLayanan: pengaduan.id,
idCategory: idCategoryFix,
@@ -368,6 +375,9 @@ const PelayananRoute = new Elysia({
}
for (const item of dataText) {
console.log('dataitem', item)
const jenisFix = category?.dataText.find((v) => v.toLowerCase() == item.jenis.toLowerCase())
console.log('data text fix', jenisFix)
dataInsertDataText.push({
idPengajuanLayanan: pengaduan.id,
idCategory: idCategoryFix,
@@ -376,6 +386,9 @@ const PelayananRoute = new Elysia({
})
}
console.log('datainsertsyaratdokumen', dataInsertSyaratDokumen)
console.log('datainsertdatatext', dataInsertDataText)
await prisma.syaratDokumenPelayanan.createMany({
data: dataInsertSyaratDokumen,
})
@@ -400,17 +413,17 @@ const PelayananRoute = new Elysia({
examples: ["skusaha"],
error: "ID kategori harus diisi"
}),
namaWarga: t.String({
description: "Nama warga",
examples: ["Budi Santoso"],
error: "Nama warga harus diisi"
}),
// namaWarga: t.String({
// description: "Nama warga",
// examples: ["Budi Santoso"],
// error: "Nama warga harus diisi"
// }),
noTelepon: t.String({
error: "Nomor telepon harus diisi",
examples: ["08123456789", "+628123456789"],
description: "Nomor telepon warga pelapor"
}),
// noTelepon: t.String({
// error: "Nomor telepon harus diisi",
// examples: ["08123456789", "+628123456789"],
// description: "Nomor telepon warga pelapor"
// }),
dataText: t.Array(
t.Object({

View File

@@ -107,8 +107,10 @@ const PengaduanRoute = new Elysia({
// --- PENGADUAN ---
.post("/create", async ({ body }) => {
const { judulPengaduan, detailPengaduan, lokasi, namaGambar, kategoriId, namaWarga, noTelepon } = body
.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
@@ -140,7 +142,7 @@ const PengaduanRoute = new Elysia({
}
if (!isValidPhone(noTelepon)) {
return { success: false, message: 'nomor telepon tidak valid, harap masukkan nomor yang benar' }
return { success: false, message: `nomor telepon ${noTelepon} tidak valid, harap masukkan nomor yang benar` }
}
const nomorHP = normalizePhoneNumber({ phone: noTelepon })
@@ -220,16 +222,16 @@ const PengaduanRoute = new Elysia({
description: "ID atau nama kategori pengaduan (contoh: kebersihan, keamanan, lainnya)"
})),
namaWarga: t.String({
examples: ["budiman"],
description: "Nama warga yang melapor"
}),
// 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"
}),
// noTelepon: t.String({
// error: "Nomor telepon harus diisi",
// examples: ["08123456789", "+628123456789"],
// description: "Nomor telepon warga pelapor"
// }),
}),
detail: {
@@ -289,6 +291,84 @@ const PengaduanRoute = new Elysia({
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