diff --git a/prisma/lib/create_file_share_folder.ts b/prisma/lib/create_file_share_folder.ts new file mode 100644 index 00000000..3b4a881e --- /dev/null +++ b/prisma/lib/create_file_share_folder.ts @@ -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 { + 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 { + 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); +} diff --git a/prisma/lib/get_shared_images.ts b/prisma/lib/get_shared_images.ts new file mode 100644 index 00000000..c76b9064 --- /dev/null +++ b/prisma/lib/get_shared_images.ts @@ -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 { + 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 { + 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); +} diff --git a/prisma/lib/get_sharef.ts b/prisma/lib/get_sharef.ts new file mode 100644 index 00000000..13c2dc45 --- /dev/null +++ b/prisma/lib/get_sharef.ts @@ -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(); +} diff --git a/prisma/seed_assets.ts b/prisma/seed_assets.ts index 52e71e27..11f1ede2 100644 --- a/prisma/seed_assets.ts +++ b/prisma/seed_assets.ts @@ -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, diff --git a/src/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID.ts b/src/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID.ts index 4d0a3df4..fe01b462 100644 --- a/src/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID.ts +++ b/src/app/admin/(dashboard)/_state/ppid/profile_ppid/profile_PPID.ts @@ -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); - toast.error("Gagal memuat data profile"); + // 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; diff --git a/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts b/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts index aa266751..3c13f182 100644 --- a/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts +++ b/src/app/api/[[...slugs]]/_lib/ppid/profile_ppid/find-by-id.ts @@ -41,10 +41,18 @@ export default async function handler(request: Request) { } if (!data) { - return Response.json({ - success: false, - message: "Data tidak ditemukan", - }, { status: 404 }); + // 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({ diff --git a/src/app/darmasaba/(pages)/desa/profil/ui/visimisiDesa.tsx b/src/app/darmasaba/(pages)/desa/profil/ui/visimisiDesa.tsx index 26ef0ee2..bd48b653 100644 --- a/src/app/darmasaba/(pages)/desa/profil/ui/visimisiDesa.tsx +++ b/src/app/darmasaba/(pages)/desa/profil/ui/visimisiDesa.tsx @@ -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 ( @@ -23,6 +23,21 @@ function VisiMisiDesa() { ); } + if (error || !data) { + return ( + + + + Terjadi Kesalahan + + + {error || 'Data visi dan misi desa tidak ditemukan'} + + + + ); + } + return ( diff --git a/src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx b/src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx index e3ba98df..a2639e95 100644 --- a/src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx +++ b/src/app/darmasaba/(pages)/ppid/profil-ppid/page.tsx @@ -34,7 +34,7 @@ function Page() { }, []); // LOADING SKELETON - if (!allList.profile.data) + if (allList.profile.loading) return ( @@ -53,6 +53,50 @@ function Page() { ); + // ERROR HANDLING + if (allList.profile.error) + return ( + + + + {allList.profile.error === "Belum ada data profil PPID yang aktif" ? ( + <> + + Data Profile PPID Belum Tersedia + + Mohon maaf, data profil PPID belum tersedia saat ini + + ) : ( + <> + + Gagal Memuat Data Profile + + Silakan coba beberapa saat lagi + + Error: {allList.profile.error} + + + )} + + + + ); + + // NO DATA HANDLING + if (!allList.profile.data) + return ( + + + + + Data Profile PPID Belum Tersedia + + Mohon maaf, data profil PPID belum tersedia saat ini + + + + ); + const dataArray = Array.isArray(allList.profile.data) ? allList.profile.data : [allList.profile.data];