Merge pull request 'nico / 5-feb-26' (#62) from nico/5-feb-26 into staggingweb

Reviewed-on: http://wibugit.wibudev.com/wibu/desa-darmasaba/pulls/62
This commit is contained in:
2026-02-05 11:14:42 +08:00
8 changed files with 274 additions and 12 deletions

View File

@@ -0,0 +1,79 @@
import { getValidAuthToken } from "../../src/lib/seafile-auth-service";
type CdnItem = {
name: string;
path: string;
cdnUrl: string;
};
type DirItem = {
type: "file" | "dir";
name: string;
};
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
const REPO_ID = process.env.SEAFILE_REPO_ID!;
// folder yang dishare (RELATIVE, tanpa slash depan)
const DIR_TARGET = "asset-web";
// 🔑 TOKEN DIRECTORY SHARE (/d/{token})
const PUBLIC_SHARE_TOKEN = "3a9a9ecb5e244f4da8ae";
/**
* Ambil list file dari repo (butuh token sekali)
*/
async function getDirItems(): Promise<DirItem[]> {
const token = await getValidAuthToken();
const res = await fetch(
`${BASE_URL}/api2/repos/${REPO_ID}/dir/?p=/${DIR_TARGET}`,
{
headers: {
Authorization: `Token ${token}`,
},
},
);
if (!res.ok) {
const text = await res.text();
throw new Error(`Failed get dir items: ${text}`);
}
return res.json();
}
/**
* Build PUBLIC CDN URL
*/
function buildPublicCdnUrl(fileName: string) {
return `${BASE_URL}/d/${PUBLIC_SHARE_TOKEN}/files/?p=${encodeURIComponent(
fileName,
)}&raw=1`;
}
/**
* Ambil semua PUBLIC CDN URL
*/
export async function getAllPublicCdnUrls(): Promise<CdnItem[]> {
const items = await getDirItems();
return items
.filter((item) => item.type === "file")
.map((file) => {
const path = `${DIR_TARGET}/${file.name}`;
return {
name: file.name,
path,
cdnUrl: buildPublicCdnUrl(file.name),
};
});
}
/**
* Run langsung (optional)
*/
if (import.meta.main) {
const data = await getAllPublicCdnUrls();
console.log(data);
}

View File

@@ -0,0 +1,71 @@
//ini code awal cari image by folder di seafile
type CdnItem = {
name: string;
path: string;
cdnUrl: string;
};
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
const SHARE_ID = "3325e9db2c504ebf9584";
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/
// https://cld-dkr-makuro-seafile.wibudev.com/d/3a9a9ecb5e244f4da8ae/files/?p=-M_tICRVz6ZxOfvkuHQgU-mobile.webp&raw=1
/**
* Build CDN URL langsung (tanpa API, tanpa token)
*/
export function buildCdnUrl(filePath: string) {
// filePath contoh: "banner/home.jpg"
return `${BASE_URL}/f/${SHARE_ID}/${filePath}?raw=1`;
}
/**
* Ambil daftar file dari PUBLIC SHARE (optional)
* Tidak pakai token
*/
async function getPublicDirItems(path = "/"): Promise<any[]> {
const res = await fetch(
`${BASE_URL}/api/v2.1/share-links/${SHARE_ID}/dir/?p=${encodeURIComponent(
path,
)}`,
);
if (!res.ok) {
const text = await res.text();
throw new Error(`Failed get public dir items: ${text}`);
}
return res.json();
}
/**
* Ambil semua CDN URL dari folder public share
*/
export async function getAllCdnUrls(
dirPath = "/",
): Promise<CdnItem[]> {
const items = await getPublicDirItems(dirPath);
return items
.filter((item: any) => item.type === "file")
.map((file: any) => {
const filePath =
dirPath === "/"
? file.name
: `${dirPath.replace(/\/$/, "")}/${file.name}`;
return {
name: file.name,
path: filePath,
cdnUrl: buildCdnUrl(filePath),
};
});
}
if(import.meta.main) {
const allCdnUrls = await getAllCdnUrls();
console.log(allCdnUrls);
}

33
prisma/lib/get_sharef.ts Normal file
View File

@@ -0,0 +1,33 @@
const BASE_URL = "https://cld-dkr-makuro-seafile.wibudev.com";
const ADMIN_TOKEN = process.env.SEAFILE_TOKEN!;
const REPO_ID = process.env.SEAFILE_REPO_ID!;
export async function createFileShareForFolder() {
const res = await fetch(`${BASE_URL}/api/v2.1/share-links/`, {
method: "POST",
headers: {
Authorization: `Token ${ADMIN_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
repo_id: REPO_ID,
path: "/asset-web", // FOLDER
permission: "r",
}),
});
if (!res.ok) {
const text = await res.text();
throw new Error(text);
}
const data = await res.json();
console.log("FILE SHARE LINK:", data);
// data.link -> https://domain/f/XXXX/
// data.token / data.id (tergantung versi)
}
if (import.meta.main) {
await createFileShareForFolder();
}

View File

@@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import prisma from "@/lib/prisma";
import { getAllDownloadUrls } from "./lib/get_images";
import { getAllPublicCdnUrls } from "./lib/create_file_share_folder";
export default async function seedAssets() {
const images = await getAllDownloadUrls();
const images = await getAllPublicCdnUrls();
for (const img of images) {
try {
@@ -20,7 +20,7 @@ export default async function seedAssets() {
name: img.name,
category: "image",
mimeType: "image/webp",
link: img.downloadUrl,
link: img.cdnUrl,
path: "images",
realName: img.name,
isActive: true,

View File

@@ -57,12 +57,24 @@ const stateProfilePPID = proxy({
if (result.success) {
this.data = result.data;
return result.data;
} else throw new Error(result.message || "Gagal memuat data profile");
} else {
// Jika pesan adalah "Data tidak ditemukan" atau "Belum ada data profil PPID yang aktif",
// tetap simpan sebagai error tapi tidak perlu menampilkan toast error karena ini bukan error sebenarnya
if (result.message === "Data tidak ditemukan" || result.message === "Belum ada data profil PPID yang aktif") {
this.error = result.message;
return null;
} else {
throw new Error(result.message || "Gagal memuat data profile");
}
}
} catch (err) {
const msg = (err as Error).message;
this.error = msg;
console.error("Load profile error:", msg);
// Hanya tampilkan toast error jika bukan karena data tidak ditemukan
if (msg !== "Data tidak ditemukan" && msg !== "Belum ada data profil PPID yang aktif") {
toast.error("Gagal memuat data profile");
}
return null;
} finally {
this.loading = false;

View File

@@ -41,11 +41,19 @@ export default async function handler(request: Request) {
}
if (!data) {
// Untuk ID 'edit', kembalikan pesan khusus karena mungkin memang belum ada data
if (id === 'edit') {
return Response.json({
success: false,
message: "Belum ada data profil PPID yang aktif",
}, { status: 404 });
} else {
return Response.json({
success: false,
message: "Data tidak ditemukan",
}, { status: 404 });
}
}
return Response.json({
success: true,

View File

@@ -13,9 +13,9 @@ function VisiMisiDesa() {
state.findUnique.load('edit');
}, []);
const { data, loading } = state.findUnique;
const { data, loading, error } = state.findUnique;
if (loading || !data) {
if (loading) {
return (
<Box py="xl">
<Skeleton h={500} radius="lg" />
@@ -23,6 +23,21 @@ function VisiMisiDesa() {
);
}
if (error || !data) {
return (
<Box py="xl">
<Paper p="xl" radius="lg" shadow="md" withBorder>
<Title order={2} c="red" ta="center" mb="md">
Terjadi Kesalahan
</Title>
<Text ta="center" c="dimmed">
{error || 'Data visi dan misi desa tidak ditemukan'}
</Text>
</Paper>
</Box>
);
}
return (
<Box>
<Stack align="center" gap="xl">

View File

@@ -34,7 +34,7 @@ function Page() {
}, []);
// LOADING SKELETON
if (!allList.profile.data)
if (allList.profile.loading)
return (
<Stack bg={colors.Bg} py="xl" gap="22">
<Box px={{ base: 'md', md: 100 }}>
@@ -53,6 +53,50 @@ function Page() {
</Stack>
);
// ERROR HANDLING
if (allList.profile.error)
return (
<Stack bg={colors.Bg} py="xl" gap="22" justify="center" align="center">
<Box px={{ base: 'md', md: 100 }}>
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
{allList.profile.error === "Belum ada data profil PPID yang aktif" ? (
<>
<Title order={3} ta="center" c={"orange"} fw={700}>
Data Profile PPID Belum Tersedia
</Title>
<Text ta="center" mt="md">Mohon maaf, data profil PPID belum tersedia saat ini</Text>
</>
) : (
<>
<Title order={3} ta="center" c={"red"} fw={700}>
Gagal Memuat Data Profile
</Title>
<Text ta="center" mt="md">Silakan coba beberapa saat lagi</Text>
<Text ta="center" size="sm" c={"gray"} mt="sm">
Error: {allList.profile.error}
</Text>
</>
)}
</Paper>
</Box>
</Stack>
);
// NO DATA HANDLING
if (!allList.profile.data)
return (
<Stack bg={colors.Bg} py="xl" gap="22" justify="center" align="center">
<Box px={{ base: 'md', md: 100 }}>
<Paper p="xl" bg={colors['white-trans-1']} radius="lg" shadow="xl">
<Title order={3} ta="center" c={"orange"} fw={700}>
Data Profile PPID Belum Tersedia
</Title>
<Text ta="center" mt="md">Mohon maaf, data profil PPID belum tersedia saat ini</Text>
</Paper>
</Box>
</Stack>
);
const dataArray = Array.isArray(allList.profile.data)
? allList.profile.data
: [allList.profile.data];