Files
desa-darmasaba/prisma/seed_assets.ts
nico 2958950585 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>
2026-04-23 14:28:08 +08:00

93 lines
2.4 KiB
TypeScript

import prisma from "@/lib/prisma";
import { Client } from "minio";
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() {
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);
try {
const existing = await prisma.fileStorage.findUnique({
where: { name: fileName },
});
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.error(` ✗ Error: ${fileName}`, err);
}
}
console.log(`\n🎉 Asset seeding selesai — seeded: ${seeded}, updated: ${updated}, skipped: ${skipped}`);
}
if (import.meta.main) {
seedAssets()
.then(() => prisma.$disconnect())
.catch((err) => {
console.error("gagal seed assets", err);
prisma.$disconnect();
process.exit(1);
});
}