diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index d93448bf..041fac02 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -533,7 +533,7 @@ model SejarahDesa {
deskripsi String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
+ deletedAt DateTime?
isActive Boolean @default(true)
}
@@ -543,7 +543,7 @@ model VisiMisiDesa {
misi String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
+ deletedAt DateTime?
isActive Boolean @default(true)
}
@@ -553,7 +553,7 @@ model LambangDesa {
deskripsi String @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
+ deletedAt DateTime?
isActive Boolean @default(true)
}
@@ -564,7 +564,7 @@ model MaskotDesa {
images ProfileDesaImage[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
- deletedAt DateTime @default(now())
+ deletedAt DateTime?
isActive Boolean @default(true)
}
diff --git a/src/app/admin/(dashboard)/desa/profil/profil-perbekel/page.tsx b/src/app/admin/(dashboard)/desa/profil/profil-perbekel/page.tsx
index d3b1cb21..e79103d6 100644
--- a/src/app/admin/(dashboard)/desa/profil/profil-perbekel/page.tsx
+++ b/src/app/admin/(dashboard)/desa/profil/profil-perbekel/page.tsx
@@ -95,7 +95,7 @@ function Page() {
fz={{ base: 'md', md: 'lg' }}
lh={{ base: 1.4, md: 1.4 }}
>
- I.B. Surya Prabhawa Manuaba, S.H., M.H.
+ {perbekel.nama || "I.B. Surya Prabhawa Manuaba, S.H., M.H."}
diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx
index 6700ea9b..b38ac3f5 100644
--- a/src/app/admin/layout.tsx
+++ b/src/app/admin/layout.tsx
@@ -354,7 +354,8 @@ export default function Layout({ children }: { children: React.ReactNode }) {
borderLeft: `2px solid ${tokens.colors.primary}`,
}),
...(mounted && isChildActive && !isDark && {
- backgroundColor: tokens.colors.bg.hover,
+ backgroundColor: 'rgba(25, 113, 194, 0.1)',
+ borderLeft: `2px solid ${tokens.colors.primary}`,
}),
}
}}
diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-first.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-first.ts
new file mode 100644
index 00000000..c5b8359b
--- /dev/null
+++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/find-first.ts
@@ -0,0 +1,40 @@
+import prisma from "@/lib/prisma";
+import { requireAuth } from "@/lib/api-auth";
+
+export default async function sejarahDesaFindFirst(request: Request) {
+ // ✅ Authentication check
+ const headers = new Headers(request.url);
+ const authResult = await requireAuth({ headers });
+ if (!authResult.authenticated) {
+ return authResult.response;
+ }
+
+ try {
+ // Get the first active record
+ const data = await prisma.sejarahDesa.findFirst({
+ where: {
+ isActive: true,
+ deletedAt: null
+ },
+ orderBy: { createdAt: 'asc' } // Get the oldest one first
+ });
+
+ if (!data) {
+ return Response.json({
+ success: false,
+ message: "Data tidak ditemukan",
+ }, {status: 404})
+ }
+
+ return Response.json({
+ success: true,
+ data,
+ }, {status: 200})
+ } catch (error) {
+ console.error("Gagal mengambil data sejarah desa:", error)
+ return Response.json({
+ success: false,
+ message: "Terjadi kesalahan saat mengambil data",
+ }, {status: 500})
+ }
+}
diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts
index bb972b6d..5f39e890 100644
--- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts
+++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/index.ts
@@ -1,11 +1,16 @@
import Elysia, { t } from "elysia";
import sejarahDesaFindById from "./find-by-id";
import sejarahDesaUpdate from "./update";
+import sejarahDesaFindFirst from "./find-first";
const SejarahDesa = new Elysia({
prefix: "/sejarah",
tags: ["Desa/Profile"],
})
+ .get("/first", async (context) => {
+ const response = await sejarahDesaFindFirst(new Request(context.request));
+ return response;
+ })
.get("/:id", async (context) => {
const response = await sejarahDesaFindById(new Request(context.request));
return response;
diff --git a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts
index 32958feb..aeedfde5 100644
--- a/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts
+++ b/src/app/api/[[...slugs]]/_lib/desa/profile/profile_desa/sejarah/update.ts
@@ -1,7 +1,14 @@
import prisma from "@/lib/prisma";
+import { requireAuth } from "@/lib/api-auth";
import { Context } from "elysia";
export default async function sejarahDesaUpdate(context: Context) {
+ // ✅ Authentication check
+ const authResult = await requireAuth(context);
+ if (!authResult.authenticated) {
+ return authResult.response;
+ }
+
try {
const id = context.params?.id as string;
const body = await context.body as {
diff --git a/src/app/darmasaba/layout.tsx b/src/app/darmasaba/layout.tsx
index 71971662..7be59fe4 100644
--- a/src/app/darmasaba/layout.tsx
+++ b/src/app/darmasaba/layout.tsx
@@ -1,19 +1,14 @@
"use client";
import colors from "@/con/colors";
-import { MantineProvider, createTheme } from "@mantine/core";
import { Box, Space, Stack } from "@mantine/core";
import { Navbar } from "@/app/darmasaba/_com/Navbar";
import Footer from "./_com/Footer";
-const theme = createTheme({
- defaultColorScheme: "light",
-});
export default function Layout({ children }: { children: React.ReactNode }) {
return (
-
-
)
}
\ No newline at end of file
diff --git a/src/lib/api-auth.ts b/src/lib/api-auth.ts
new file mode 100644
index 00000000..d639b296
--- /dev/null
+++ b/src/lib/api-auth.ts
@@ -0,0 +1,84 @@
+/**
+ * Authentication helper untuk API endpoints
+ *
+ * Usage:
+ * import { requireAuth } from "@/lib/api-auth";
+ *
+ * export default async function myEndpoint(context: Context) {
+ * const authResult = await requireAuth(context);
+ * if (!authResult.authenticated) {
+ * return authResult.response;
+ * }
+ * // Lanjut proses dengan authResult.user
+ * }
+ */
+
+import { getSession } from "@/lib/session";
+
+export type AuthResult =
+ | { authenticated: true; user: any }
+ | { authenticated: false; response: Response };
+
+export async function requireAuth(context: any): Promise {
+ try {
+ // Cek session dari cookies
+ const session = await getSession();
+
+ if (!session || !session.user) {
+ return {
+ authenticated: false,
+ response: new Response(JSON.stringify({
+ success: false,
+ message: "Unauthorized - Silakan login terlebih dahulu"
+ }), {
+ status: 401,
+ headers: { 'Content-Type': 'application/json' }
+ })
+ };
+ }
+
+ // Check jika user masih aktif
+ if (!session.user.isActive) {
+ return {
+ authenticated: false,
+ response: new Response(JSON.stringify({
+ success: false,
+ message: "Akun Anda tidak aktif. Hubungi administrator."
+ }), {
+ status: 403,
+ headers: { 'Content-Type': 'application/json' }
+ })
+ };
+ }
+
+ return {
+ authenticated: true,
+ user: session.user
+ };
+ } catch (error) {
+ console.error("Auth error:", error);
+ return {
+ authenticated: false,
+ response: new Response(JSON.stringify({
+ success: false,
+ message: "Authentication error"
+ }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' }
+ })
+ };
+ }
+}
+
+/**
+ * Optional auth - tidak error jika tidak authenticated
+ * Berguna untuk endpoint yang bisa diakses public atau private
+ */
+export async function optionalAuth(context: any): Promise {
+ try {
+ const session = await getSession();
+ return session?.user || null;
+ } catch (error) {
+ return null;
+ }
+}
diff --git a/src/lib/session.ts b/src/lib/session.ts
new file mode 100644
index 00000000..10c4fc57
--- /dev/null
+++ b/src/lib/session.ts
@@ -0,0 +1,68 @@
+/**
+ * Session helper menggunakan iron-session
+ *
+ * Usage:
+ * import { getSession } from "@/lib/session";
+ *
+ * const session = await getSession();
+ * if (session?.user) {
+ * // User authenticated
+ * }
+ */
+
+import { getIronSession } from 'iron-session';
+import { cookies } from 'next/headers';
+
+export type SessionData = {
+ user?: {
+ id: string;
+ name: string;
+ roleId: number;
+ menuIds?: string[] | null;
+ isActive?: boolean;
+ };
+};
+
+export type Session = SessionData & {
+ save: () => Promise;
+ destroy: () => Promise;
+};
+
+const SESSION_OPTIONS = {
+ cookieName: 'desa-session',
+ password: process.env.SESSION_PASSWORD || 'default-password-change-in-production',
+ cookieOptions: {
+ secure: process.env.NODE_ENV === 'production',
+ httpOnly: true,
+ sameSite: 'lax' as const,
+ maxAge: 60 * 60 * 24 * 7, // 7 days
+ },
+};
+
+export async function getSession(): Promise {
+ try {
+ const cookieStore = await cookies();
+ const session = await getIronSession(
+ cookieStore,
+ SESSION_OPTIONS
+ );
+
+ return session;
+ } catch (error) {
+ console.error('Session error:', error);
+ return null;
+ }
+}
+
+export async function destroySession(): Promise {
+ try {
+ const cookieStore = await cookies();
+ const session = await getIronSession(
+ cookieStore,
+ SESSION_OPTIONS
+ );
+ await session.destroy();
+ } catch (error) {
+ console.error('Destroy session error:', error);
+ }
+}