fix-admin-menu-desa-profile
This commit is contained in:
@@ -533,7 +533,7 @@ model SejarahDesa {
|
|||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -543,7 +543,7 @@ model VisiMisiDesa {
|
|||||||
misi String @db.Text
|
misi String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,7 +553,7 @@ model LambangDesa {
|
|||||||
deskripsi String @db.Text
|
deskripsi String @db.Text
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,7 +564,7 @@ model MaskotDesa {
|
|||||||
images ProfileDesaImage[]
|
images ProfileDesaImage[]
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
deletedAt DateTime @default(now())
|
deletedAt DateTime?
|
||||||
isActive Boolean @default(true)
|
isActive Boolean @default(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ function Page() {
|
|||||||
fz={{ base: 'md', md: 'lg' }}
|
fz={{ base: 'md', md: 'lg' }}
|
||||||
lh={{ base: 1.4, md: 1.4 }}
|
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."}
|
||||||
</Text>
|
</Text>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -354,7 +354,8 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
borderLeft: `2px solid ${tokens.colors.primary}`,
|
borderLeft: `2px solid ${tokens.colors.primary}`,
|
||||||
}),
|
}),
|
||||||
...(mounted && isChildActive && !isDark && {
|
...(mounted && isChildActive && !isDark && {
|
||||||
backgroundColor: tokens.colors.bg.hover,
|
backgroundColor: 'rgba(25, 113, 194, 0.1)',
|
||||||
|
borderLeft: `2px solid ${tokens.colors.primary}`,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -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})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
import Elysia, { t } from "elysia";
|
import Elysia, { t } from "elysia";
|
||||||
import sejarahDesaFindById from "./find-by-id";
|
import sejarahDesaFindById from "./find-by-id";
|
||||||
import sejarahDesaUpdate from "./update";
|
import sejarahDesaUpdate from "./update";
|
||||||
|
import sejarahDesaFindFirst from "./find-first";
|
||||||
|
|
||||||
const SejarahDesa = new Elysia({
|
const SejarahDesa = new Elysia({
|
||||||
prefix: "/sejarah",
|
prefix: "/sejarah",
|
||||||
tags: ["Desa/Profile"],
|
tags: ["Desa/Profile"],
|
||||||
})
|
})
|
||||||
|
.get("/first", async (context) => {
|
||||||
|
const response = await sejarahDesaFindFirst(new Request(context.request));
|
||||||
|
return response;
|
||||||
|
})
|
||||||
.get("/:id", async (context) => {
|
.get("/:id", async (context) => {
|
||||||
const response = await sejarahDesaFindById(new Request(context.request));
|
const response = await sejarahDesaFindById(new Request(context.request));
|
||||||
return response;
|
return response;
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
import { requireAuth } from "@/lib/api-auth";
|
||||||
import { Context } from "elysia";
|
import { Context } from "elysia";
|
||||||
|
|
||||||
export default async function sejarahDesaUpdate(context: Context) {
|
export default async function sejarahDesaUpdate(context: Context) {
|
||||||
|
// ✅ Authentication check
|
||||||
|
const authResult = await requireAuth(context);
|
||||||
|
if (!authResult.authenticated) {
|
||||||
|
return authResult.response;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const id = context.params?.id as string;
|
const id = context.params?.id as string;
|
||||||
const body = await context.body as {
|
const body = await context.body as {
|
||||||
|
|||||||
@@ -1,19 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import colors from "@/con/colors";
|
import colors from "@/con/colors";
|
||||||
import { MantineProvider, createTheme } from "@mantine/core";
|
|
||||||
import { Box, Space, Stack } from "@mantine/core";
|
import { Box, Space, Stack } from "@mantine/core";
|
||||||
|
|
||||||
import { Navbar } from "@/app/darmasaba/_com/Navbar";
|
import { Navbar } from "@/app/darmasaba/_com/Navbar";
|
||||||
import Footer from "./_com/Footer";
|
import Footer from "./_com/Footer";
|
||||||
|
|
||||||
const theme = createTheme({
|
|
||||||
defaultColorScheme: "light",
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function Layout({ children }: { children: React.ReactNode }) {
|
export default function Layout({ children }: { children: React.ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<MantineProvider theme={theme} defaultColorScheme="light">
|
|
||||||
<Stack gap={0} bg={colors.grey[1]}>
|
<Stack gap={0} bg={colors.grey[1]}>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<Space h={{
|
<Space h={{
|
||||||
@@ -27,6 +22,5 @@ export default function Layout({ children }: { children: React.ReactNode }) {
|
|||||||
</Box>
|
</Box>
|
||||||
<Footer />
|
<Footer />
|
||||||
</Stack>
|
</Stack>
|
||||||
</MantineProvider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
84
src/lib/api-auth.ts
Normal file
84
src/lib/api-auth.ts
Normal file
@@ -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<AuthResult> {
|
||||||
|
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<any> {
|
||||||
|
try {
|
||||||
|
const session = await getSession();
|
||||||
|
return session?.user || null;
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/lib/session.ts
Normal file
68
src/lib/session.ts
Normal file
@@ -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<void>;
|
||||||
|
destroy: () => Promise<void>;
|
||||||
|
};
|
||||||
|
|
||||||
|
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<SessionData | null> {
|
||||||
|
try {
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const session = await getIronSession<SessionData>(
|
||||||
|
cookieStore,
|
||||||
|
SESSION_OPTIONS
|
||||||
|
);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Session error:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function destroySession(): Promise<void> {
|
||||||
|
try {
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const session = await getIronSession<SessionData>(
|
||||||
|
cookieStore,
|
||||||
|
SESSION_OPTIONS
|
||||||
|
);
|
||||||
|
await session.destroy();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Destroy session error:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user