fix(storage): migrate fileStorage handlers and seeder from local disk/Seafile to MinIO
Root cause: images not showing because:
1. seed_assets.ts was listing from Seafile (unreliable, 502 errors)
2. fileStorage create/findUniq/del handlers used local disk (not available in containers)
3. link format in file-storage.json had inconsistent path prefix
Changes:
- seed_assets.ts: list objects from MinIO bucket instead of Seafile API; seed
link as /api/img/{name}, path as "image" (matches MinIO prefix)
- fileStorage/create.ts: upload image/audio/document to MinIO via putObject;
remove WIBU_UPLOAD_DIR dependency and all fs.writeFile calls
- fileStorage/findUniq.ts: stream file from MinIO via getObject; remove
WIBU_UPLOAD_DIR and fs.readFile
- fileStorage/del.ts: delete from MinIO via removeObject; remove fs.unlink
- file-storage.json: fix path field to "image" (was full path); all 80 entries
already have correct link=/api/img/{name} format
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,49 +1,92 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import prisma from "@/lib/prisma";
|
||||
import { Client } from "minio";
|
||||
|
||||
import { getAllPublicCdnUrls } from "./lib/create_file_share_folder";
|
||||
const minio = new Client({
|
||||
endPoint: process.env.MINIO_ENDPOINT!,
|
||||
accessKey: process.env.MINIO_ACCESS_KEY!,
|
||||
secretKey: process.env.MINIO_SECRET_KEY!,
|
||||
useSSL: process.env.MINIO_USE_SSL === "true",
|
||||
});
|
||||
|
||||
const BUCKET = process.env.MINIO_BUCKET!;
|
||||
|
||||
function guessMimeType(fileName: string): string {
|
||||
const ext = fileName.split(".").pop()?.toLowerCase() ?? "";
|
||||
const map: Record<string, string> = {
|
||||
webp: "image/webp",
|
||||
jpg: "image/jpeg",
|
||||
jpeg: "image/jpeg",
|
||||
png: "image/png",
|
||||
gif: "image/gif",
|
||||
mp3: "audio/mpeg",
|
||||
mp4: "audio/mp4",
|
||||
pdf: "application/pdf",
|
||||
};
|
||||
return map[ext] ?? "application/octet-stream";
|
||||
}
|
||||
|
||||
export default async function seedAssets() {
|
||||
const images = await getAllPublicCdnUrls();
|
||||
console.log("📂 Seeding assets dari MinIO...");
|
||||
|
||||
const stream = minio.listObjects(BUCKET, "image/", true);
|
||||
let seeded = 0;
|
||||
let updated = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for await (const obj of stream) {
|
||||
if (!obj.name) continue;
|
||||
const fileName = obj.name.replace("image/", "");
|
||||
if (!fileName) continue;
|
||||
|
||||
const mimeType = guessMimeType(fileName);
|
||||
|
||||
for (const img of images) {
|
||||
try {
|
||||
// Check if the image already exists by name
|
||||
const existingImage = await prisma.fileStorage.findUnique({
|
||||
where: { name: img.name },
|
||||
const existing = await prisma.fileStorage.findUnique({
|
||||
where: { name: fileName },
|
||||
});
|
||||
|
||||
if (!existingImage) {
|
||||
// Only create if it doesn't exist
|
||||
await prisma.fileStorage.create({
|
||||
data: {
|
||||
name: img.name,
|
||||
category: "image",
|
||||
mimeType: "image/webp",
|
||||
link: img.cdnUrl,
|
||||
path: "images",
|
||||
realName: img.name,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log(`✅ Created new image: ${img.name}`);
|
||||
} else {
|
||||
console.log(`ℹ️ Image already exists, skipping: ${img.name}`);
|
||||
if (existing) {
|
||||
// Perbaiki link lama yang masih pakai Seafile URL atau format lama
|
||||
if (!existing.link?.startsWith("/api/img/")) {
|
||||
await prisma.fileStorage.update({
|
||||
where: { name: fileName },
|
||||
data: { link: `/api/img/${fileName}`, path: "image" },
|
||||
});
|
||||
console.log(` 🔄 Updated: ${fileName}`);
|
||||
updated++;
|
||||
} else {
|
||||
skipped++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
await prisma.fileStorage.create({
|
||||
data: {
|
||||
name: fileName,
|
||||
realName: fileName,
|
||||
path: "image",
|
||||
mimeType,
|
||||
category: "image",
|
||||
link: `/api/img/${fileName}`,
|
||||
isActive: true,
|
||||
},
|
||||
});
|
||||
console.log(` ✓ Seeded: ${fileName}`);
|
||||
seeded++;
|
||||
} catch (err) {
|
||||
console.log(`❌ Failed to seed asset ${img.name}:`, JSON.stringify(err));
|
||||
console.error(` ✗ Error: ${fileName}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("🎉 Image seeding completed");
|
||||
console.log(`\n🎉 Asset seeding selesai — seeded: ${seeded}, updated: ${updated}, skipped: ${skipped}`);
|
||||
}
|
||||
|
||||
if (import.meta.main) {
|
||||
seedAssets()
|
||||
.then(() => {
|
||||
console.log("seed assets success");
|
||||
})
|
||||
.then(() => prisma.$disconnect())
|
||||
.catch((err) => {
|
||||
console.log("gagal seed assets", JSON.stringify(err));
|
||||
console.error("gagal seed assets", err);
|
||||
prisma.$disconnect();
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user