diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 328e2ee6..d57e88d8 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -76,6 +76,8 @@ model FileStorage { KontakDarurat KontakDarurat[] InfoWabahPenyakit InfoWabahPenyakit[] + + KeamananLingkungan KeamananLingkungan[] } //========================================= MENU PPID ========================================= // @@ -873,3 +875,45 @@ model InfoWabahPenyakit { deletedAt DateTime @default(now()) isActive Boolean @default(true) } + +// ========================================= MENU KEAMANAN ========================================= // +// ========================================= KEAMANAN LINGKUNGAN ========================================= // +model KeamananLingkungan { + id String @id @default(cuid()) + name String @db.Text + deskripsi String @db.Text + image FileStorage? @relation(fields: [imageId], references: [id]) + imageId String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} +// ========================================= POLSEK TERDEKAT ========================================= // +model PolsekTerdekat { + id String @id @default(uuid()) + nama String + jarakKeDesa String + alamat String + nomorTelepon String + jamOperasional String + embedMapUrl String + namaTempatMaps String + alamatMaps String + linkPetunjukArah String + layananPolsek LayananPolsek @relation(fields: [layananPolsekId], references: [id]) + layananPolsekId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) +} +model LayananPolsek { + id String @id @default(uuid()) + nama String // contoh: "Pelayanan SKCK", "Laporan Kriminal" + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + deletedAt DateTime @default(now()) + isActive Boolean @default(true) + PolsekTerdekat PolsekTerdekat[] +} diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts new file mode 100644 index 00000000..eabfbb33 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/index.ts @@ -0,0 +1,8 @@ +import Elysia from "elysia"; +import KeamananLingkungan from "./keamanan-lingkungan"; +import PolsekTerdekat from "./polsek-terdekat"; + +const Keamanan = new Elysia({ prefix: "/api/keamanan", tags: ["Keamanan"] }) +.use(KeamananLingkungan) +.use(PolsekTerdekat) +export default Keamanan; diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/create.ts new file mode 100644 index 00000000..5f332ebc --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/create.ts @@ -0,0 +1,30 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormCreate = Prisma.KeamananLingkunganGetPayload<{ + select: { + name: true; + deskripsi: true; + imageId: true; + }; +}>; +async function createKeamananLingkungan(context: Context) { + const body = context.body as FormCreate; + + await prisma.keamananLingkungan.create({ + data: { + name: body.name, + deskripsi: body.deskripsi, + imageId: body.imageId, + }, + }); + return { + success: true, + message: "Success create keamanan lingkungan", + data: { + ...body, + }, + }; +} +export default createKeamananLingkungan \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts new file mode 100644 index 00000000..a9f5d3aa --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/del.ts @@ -0,0 +1,52 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; +import path from "path"; +import fs from "fs/promises"; + +const keamananLingkunganDelete = async (context: Context) => { + const id = context.params?.id as string; + + if (!id) { + return { + status: 400, + body: "ID tidak diberikan", + }; + } + + const keamananLingkungan = await prisma.keamananLingkungan.findUnique({ + where: { id }, + include: { + image: true, + }, + }); + + if (!keamananLingkungan) { + return { + status: 404, + body: "Keamanan lingkungan tidak ditemukan", + }; + } + + // Hapus file gambar dari filesystem jika ada + if (keamananLingkungan.image) { + try { + const filePath = path.join(keamananLingkungan.image.path, keamananLingkungan.image.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: keamananLingkungan.image.id }, + }); + } catch (err) { + console.error("Gagal hapus gambar lama:", err); + } + } + + const deleted = await prisma.keamananLingkungan.delete({ + where: { id }, + }); + + return { + status: 200, + body: deleted, + }; +}; +export default keamananLingkunganDelete; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findMany.ts new file mode 100644 index 00000000..ea5985e9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findMany.ts @@ -0,0 +1,24 @@ +import prisma from "@/lib/prisma"; + +export default async function keamananLingkunganFindMany() { + try { + const data = await prisma.keamananLingkungan.findMany({ + where: { isActive: true }, + include: { + image: true, + }, + }); + + return { + success: true, + message: "Success fetch keamanan lingkungan", + data, + }; + } catch (e) { + console.error("Find many error:", e); + return { + success: false, + message: "Failed fetch keamanan lingkungan", + }; + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findUnique.ts new file mode 100644 index 00000000..34e885fb --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/findUnique.ts @@ -0,0 +1,49 @@ +import prisma from "@/lib/prisma"; + +export default async function keamananLingkunganFindUnique(request: Request){ + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if(!id){ + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, { status: 400 }); + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, { status: 400 }); + } + + const data = await prisma.keamananLingkungan.findUnique({ + where: { id }, + include: { + image: true, + }, + }); + + if (!data) { + return Response.json({ + success: false, + message: "Keamanan lingkungan tidak ditemukan", + }, { status: 404 }); + } + + return Response.json({ + success: true, + message: "Success fetch keamanan lingkungan by ID", + data, + }, { status: 200 }); + } catch (error) { + console.error("Find by ID error:", error); + return Response.json({ + success: false, + message: "Gagal mengambil keamanan lingkungan: " + (error instanceof Error ? error.message : 'Unknown error'), + }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/index.ts new file mode 100644 index 00000000..e84a4f91 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/index.ts @@ -0,0 +1,34 @@ +import Elysia from "elysia"; +import keamananLingkunganFindUnique from "./findUnique"; +import keamananLingkunganCreate from "./create"; +import keamananLingkunganUpdate from "./updt"; +import keamananLingkunganDelete from "./del"; +import { t } from "elysia"; +import keamananLingkunganFindMany from "./findMany"; + +const KeamananLingkungan = new Elysia({ prefix: "/keamananlingkungan", tags: ["Keamanan/Keamanan Lingkungan"] }) +.get("/find-many", keamananLingkunganFindMany) +.get("/:id", async (context) => { + const response = await keamananLingkunganFindUnique(new Request(context.request)); + return response; +}) +.post("/create", keamananLingkunganCreate, { + body: t.Object({ + name: t.String(), + deskripsi: t.String(), + imageId: t.String(), + }), +}) +.delete("/del/:id", keamananLingkunganDelete) +.put("/:id", async (context) => { + const response = await keamananLingkunganUpdate(context); + return response; +}, +{ + body: t.Object({ + name: t.String(), + deskripsi: t.String(), + imageId: t.String(), + }), +}) +export default KeamananLingkungan; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts new file mode 100644 index 00000000..8418e7d9 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/keamanan-lingkungan/updt.ts @@ -0,0 +1,97 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import path from "path"; +import fs from "fs/promises"; +import { Context } from "elysia"; + +type FormUpdate = Prisma.KeamananLingkunganGetPayload<{ + select: { + name: true; + deskripsi: true; + imageId: true; + }; +}>; +export default async function updateKeamananLingkungan(context: Context) { + try { + const id = context.params?.id; + const body = (await context.body) as Omit; + + const { name, deskripsi, imageId } = body; + + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak diberikan", + }), { + status: 400, + headers: { + "Content-Type": "application/json", + }, + }); + } + const existing = await prisma.keamananLingkungan.findUnique({ + where: { id }, + include: { + image: true, + } + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Keamanan lingkungan tidak ditemukan", + }), { + status: 404, + headers: { + "Content-Type": "application/json", + }, + }); + } + + if (existing.imageId && existing.imageId !== imageId) { + const oldImage = existing.image; + if (oldImage) { + try { + const filePath = path.join(oldImage.path, oldImage.name); + await fs.unlink(filePath); + await prisma.fileStorage.delete({ + where: { id: oldImage.id }, + }); + } catch (err) { + console.error("Gagal hapus gambar lama:", err); + } + } + } + + const updated = await prisma.keamananLingkungan.update({ + where: { id }, + data: { + name, + deskripsi, + imageId, + }, + }) + + return new Response(JSON.stringify({ + success: true, + message: "Success update keamanan lingkungan", + data: updated, + }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + } catch (error) { + console.error("Error updating keamanan lingkungan:", error); + return new Response(JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat mengupdate keamanan lingkungan", + }), { + status: 500, + headers: { + "Content-Type": "application/json", + }, + }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts new file mode 100644 index 00000000..1d196824 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/create.ts @@ -0,0 +1,44 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormCreate = Prisma.PolsekTerdekatGetPayload<{ + select: { + nama: true; + jarakKeDesa: true; + alamat: true; + nomorTelepon: true; + jamOperasional: true; + embedMapUrl: true; + namaTempatMaps: true; + alamatMaps: true; + linkPetunjukArah: true; + layananPolsekId: true; + }; +}>; +async function polsekTerdekatCreate(context: Context) { + const body = context.body as FormCreate; + + await prisma.polsekTerdekat.create({ + data: { + nama: body.nama, + jarakKeDesa: body.jarakKeDesa, + alamat: body.alamat, + nomorTelepon: body.nomorTelepon, + jamOperasional: body.jamOperasional, + embedMapUrl: body.embedMapUrl, + namaTempatMaps: body.namaTempatMaps, + alamatMaps: body.alamatMaps, + linkPetunjukArah: body.linkPetunjukArah, + layananPolsekId: body.layananPolsekId, + }, + }); + return { + success: true, + message: "Success create polsek terdekat", + data: { + ...body, + }, + }; +} +export default polsekTerdekatCreate; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts new file mode 100644 index 00000000..e060ae1f --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/del.ts @@ -0,0 +1,38 @@ +import prisma from "@/lib/prisma"; +import { Context } from "elysia"; + +const polsekTerdekatDelete = async (context: Context) => { + const id = context.params?.id as string; + + if (!id) { + return { + status: 400, + body: "ID tidak diberikan", + }; + } + + const polsekTerdekat = await prisma.polsekTerdekat.findUnique({ + where: { id }, + include: { + layananPolsek: true, + } + }); + + if (!polsekTerdekat) { + return { + status: 404, + body: "Polsek terdekat tidak ditemukan", + }; + } + + await prisma.polsekTerdekat.delete({ + where: { id }, + }); + + return { + status: 200, + body: "Polsek terdekat berhasil dihapus", + }; +}; + +export default polsekTerdekatDelete; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts new file mode 100644 index 00000000..9d3e8a7e --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findMany.ts @@ -0,0 +1,24 @@ +import prisma from "@/lib/prisma"; + +export default async function polsekTerdekatFindMany() { + try { + const data = await prisma.polsekTerdekat.findMany({ + where: { isActive: true }, + include: { + layananPolsek: true, + }, + }); + + return { + success: true, + message: "Success fetch polsek terdekat", + data, + }; + } catch (e) { + console.error("Find many error:", e); + return { + success: false, + message: "Failed fetch polsek terdekat", + }; + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts new file mode 100644 index 00000000..bdf6b92b --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/findUnique.ts @@ -0,0 +1,51 @@ +import prisma from "@/lib/prisma"; + +export default async function polsekTerdekatFindUnique(request: Request){ + const url = new URL(request.url); + const pathSegments = url.pathname.split('/'); + const id = pathSegments[pathSegments.length - 1]; + + if(!id){ + return Response.json({ + success: false, + message: "ID tidak boleh kosong", + }, { status: 400 }); + } + + try { + if (typeof id !== 'string') { + return Response.json({ + success: false, + message: "ID tidak valid", + }, { status: 400 }); + } + + const data = await prisma.polsekTerdekat.findUnique({ + where: { id }, + include: { + layananPolsek: true, + }, + }); + + if (!data) { + return Response.json({ + success: false, + message: "Polsek terdekat tidak ditemukan", + }, { status: 404 }); + } + + return Response.json({ + success: true, + message: "Success fetch polsek terdekat by ID", + data, + }, { status: 200 }); + } catch (e) { + console.error("Find by ID error:", e); + return Response.json({ + success: false, + message: "Gagal mengambil polsek terdekat: " + (e instanceof Error ? e.message : 'Unknown error'), + }, { + status: 500, + }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts new file mode 100644 index 00000000..2b2ff147 --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/index.ts @@ -0,0 +1,47 @@ +import Elysia, { t } from "elysia"; +import polsekTerdekatFindMany from "./findMany"; +import polsekTerdekatFindUnique from "./findUnique"; +import polsekTerdekatDelete from "./del"; +import polsekTerdekatCreate from "./create"; +import polsekTerdekatUpdate from "./updt"; + +const PolsekTerdekat = new Elysia({ prefix: "/polsekterdekat", tags: ["Keamanan/Polsek Terdekat"] }) +.get("/find-many", polsekTerdekatFindMany) +.get("/:id", async (context) => { + const response = await polsekTerdekatFindUnique(new Request(context.request)); + return response; +}) +.post("/create", polsekTerdekatCreate, { + body: t.Object({ + nama: t.String(), + jarakKeDesa: t.String(), + alamat: t.String(), + nomorTelepon: t.String(), + jamOperasional: t.String(), + embedMapUrl: t.String(), + namaTempatMaps: t.String(), + alamatMaps: t.String(), + linkPetunjukArah: t.String(), + layananPolsekId: t.String(), + }), +}) +.delete("/del/:id", polsekTerdekatDelete) +.put("/:id", async (context) => { + const response = await polsekTerdekatUpdate(context); + return response; +}, +{ + body: t.Object({ + nama: t.String(), + jarakKeDesa: t.String(), + alamat: t.String(), + nomorTelepon: t.String(), + jamOperasional: t.String(), + embedMapUrl: t.String(), + namaTempatMaps: t.String(), + alamatMaps: t.String(), + linkPetunjukArah: t.String(), + layananPolsekId: t.String(), + }), +}) +export default PolsekTerdekat; \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts new file mode 100644 index 00000000..b629cd2a --- /dev/null +++ b/src/app/api/[[...slugs]]/_lib/keamanan/polsek-terdekat/updt.ts @@ -0,0 +1,94 @@ +import prisma from "@/lib/prisma"; +import { Prisma } from "@prisma/client"; +import { Context } from "elysia"; + +type FormUpdate = Prisma.PolsekTerdekatGetPayload<{ + select: { + nama: true; + jarakKeDesa: true; + alamat: true; + nomorTelepon: true; + jamOperasional: true; + embedMapUrl: true; + namaTempatMaps: true; + alamatMaps: true; + linkPetunjukArah: true; + layananPolsekId: true; + }; +}>; +export default async function polsekTerdekatUpdate(context: Context) { + try { + const id = context.params?.id; + const body = (await context.body) as Omit; + + const { nama, jarakKeDesa, alamat, nomorTelepon, jamOperasional, embedMapUrl, namaTempatMaps, alamatMaps, linkPetunjukArah, layananPolsekId } = body; + + if (!id) { + return new Response(JSON.stringify({ + success: false, + message: "ID tidak diberikan", + }), { + status: 400, + headers: { + "Content-Type": "application/json", + }, + }); + } + const existing = await prisma.polsekTerdekat.findUnique({ + where: { id }, + include: { + layananPolsek: true, + } + }); + + if (!existing) { + return new Response(JSON.stringify({ + success: false, + message: "Polsek terdekat tidak ditemukan", + }), { + status: 404, + headers: { + "Content-Type": "application/json", + }, + }); + } + + const updated = await prisma.polsekTerdekat.update({ + where: { id }, + data: { + nama, + jarakKeDesa, + alamat, + nomorTelepon, + jamOperasional, + embedMapUrl, + namaTempatMaps, + alamatMaps, + linkPetunjukArah, + layananPolsekId, + }, + }); + + return new Response(JSON.stringify({ + success: true, + message: "Success update polsek terdekat", + data: updated, + }), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + } catch (error) { + console.error("Error updating polsek terdekat:", error); + return new Response(JSON.stringify({ + success: false, + message: "Terjadi kesalahan saat mengupdate polsek terdekat", + }), { + status: 500, + headers: { + "Content-Type": "application/json", + }, + }); + } +} \ No newline at end of file diff --git a/src/app/api/[[...slugs]]/route.ts b/src/app/api/[[...slugs]]/route.ts index 3f2fd804..ae8d1408 100644 --- a/src/app/api/[[...slugs]]/route.ts +++ b/src/app/api/[[...slugs]]/route.ts @@ -17,6 +17,7 @@ import { uplCsvSingle } from "./_lib/upl-csv-single"; import uplImg from "./_lib/upl-img"; import { uplImgSingle } from "./_lib/upl-img-single"; import FileStorage from "./_lib/fileStorage"; +import Keamanan from "./_lib/keamanan"; const ROOT = process.cwd(); @@ -75,6 +76,7 @@ const ApiServer = new Elysia() .use(PPID) .use(Kesehatan) .use(Desa) + .use(Keamanan) .use(Utils) .use(FileStorage) .onError(({ code }) => {