Fix image di seafile sudah tidak pakai token tapi by folder di seafile
Kasih console di page profil ppid & visi misi di Profile Desa
This commit is contained in:
79
prisma/lib/create_file_share_folder.ts
Normal file
79
prisma/lib/create_file_share_folder.ts
Normal 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);
|
||||||
|
}
|
||||||
71
prisma/lib/get_shared_images.ts
Normal file
71
prisma/lib/get_shared_images.ts
Normal 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
33
prisma/lib/get_sharef.ts
Normal 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();
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
|
|
||||||
import { getAllDownloadUrls } from "./lib/get_images";
|
import { getAllPublicCdnUrls } from "./lib/create_file_share_folder";
|
||||||
|
|
||||||
export default async function seedAssets() {
|
export default async function seedAssets() {
|
||||||
const images = await getAllDownloadUrls();
|
const images = await getAllPublicCdnUrls();
|
||||||
|
|
||||||
for (const img of images) {
|
for (const img of images) {
|
||||||
try {
|
try {
|
||||||
@@ -20,7 +20,7 @@ export default async function seedAssets() {
|
|||||||
name: img.name,
|
name: img.name,
|
||||||
category: "image",
|
category: "image",
|
||||||
mimeType: "image/webp",
|
mimeType: "image/webp",
|
||||||
link: img.downloadUrl,
|
link: img.cdnUrl,
|
||||||
path: "images",
|
path: "images",
|
||||||
realName: img.name,
|
realName: img.name,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
|
|||||||
@@ -57,12 +57,24 @@ const stateProfilePPID = proxy({
|
|||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.data = result.data;
|
this.data = result.data;
|
||||||
return 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) {
|
} catch (err) {
|
||||||
const msg = (err as Error).message;
|
const msg = (err as Error).message;
|
||||||
this.error = msg;
|
this.error = msg;
|
||||||
console.error("Load profile 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;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|||||||
@@ -41,10 +41,18 @@ export default async function handler(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return Response.json({
|
// Untuk ID 'edit', kembalikan pesan khusus karena mungkin memang belum ada data
|
||||||
success: false,
|
if (id === 'edit') {
|
||||||
message: "Data tidak ditemukan",
|
return Response.json({
|
||||||
}, { status: 404 });
|
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({
|
return Response.json({
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ function VisiMisiDesa() {
|
|||||||
state.findUnique.load('edit');
|
state.findUnique.load('edit');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { data, loading } = state.findUnique;
|
const { data, loading, error } = state.findUnique;
|
||||||
|
|
||||||
if (loading || !data) {
|
if (loading) {
|
||||||
return (
|
return (
|
||||||
<Box py="xl">
|
<Box py="xl">
|
||||||
<Skeleton h={500} radius="lg" />
|
<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 (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
<Stack align="center" gap="xl">
|
<Stack align="center" gap="xl">
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ function Page() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// LOADING SKELETON
|
// LOADING SKELETON
|
||||||
if (!allList.profile.data)
|
if (allList.profile.loading)
|
||||||
return (
|
return (
|
||||||
<Stack bg={colors.Bg} py="xl" gap="22">
|
<Stack bg={colors.Bg} py="xl" gap="22">
|
||||||
<Box px={{ base: 'md', md: 100 }}>
|
<Box px={{ base: 'md', md: 100 }}>
|
||||||
@@ -53,6 +53,50 @@ function Page() {
|
|||||||
</Stack>
|
</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)
|
const dataArray = Array.isArray(allList.profile.data)
|
||||||
? allList.profile.data
|
? allList.profile.data
|
||||||
: [allList.profile.data];
|
: [allList.profile.data];
|
||||||
|
|||||||
Reference in New Issue
Block a user