106 lines
3.0 KiB
JavaScript
106 lines
3.0 KiB
JavaScript
// server/media.mjs — Openinary (media.ayris.tech) proxy, "mucomutfak" klasörüne kilitli
|
||
const BASE = () => process.env.OPENINARY_API_URL?.replace(/\/$/, "");
|
||
const KEY = () => process.env.OPENINARY_API_KEY;
|
||
export const MEDIA_ROOT = "mucomutfak";
|
||
|
||
export function mediaEnabled() {
|
||
return Boolean(BASE() && KEY());
|
||
}
|
||
|
||
function assertInsideRoot(p) {
|
||
const clean = String(p || "").replace(/^\/+|\/+$/g, "");
|
||
if (
|
||
clean !== MEDIA_ROOT &&
|
||
!clean.startsWith(`${MEDIA_ROOT}/`)
|
||
) {
|
||
throw Object.assign(new Error(`Yol '${MEDIA_ROOT}' klasörü içinde olmalı`), {
|
||
status: 400,
|
||
});
|
||
}
|
||
if (clean.includes("..")) {
|
||
throw Object.assign(new Error("Geçersiz yol"), { status: 400 });
|
||
}
|
||
return clean;
|
||
}
|
||
|
||
function deliveryUrl(path, transform = "") {
|
||
const t = transform ? `${transform}/` : "";
|
||
return `${BASE()}/t/${t}${path}`;
|
||
}
|
||
|
||
// Klasör içeriğini listele
|
||
export async function listMedia(path = MEDIA_ROOT) {
|
||
const clean = assertInsideRoot(path);
|
||
const res = await fetch(
|
||
`${BASE()}/api/storage?path=${encodeURIComponent(clean)}`,
|
||
{ headers: { Authorization: `Bearer ${KEY()}` } },
|
||
);
|
||
if (!res.ok) {
|
||
throw Object.assign(new Error(`Openinary listeleme hatası (${res.status})`), {
|
||
status: 502,
|
||
});
|
||
}
|
||
const data = await res.json();
|
||
const IMG_RE = /\.(webp|jpe?g|png|gif|svg|avif|heic|heif)$/i;
|
||
return {
|
||
path: clean,
|
||
folders: (data.folders || []).map((f) => ({ name: f.name, path: f.path })),
|
||
files: (data.files || [])
|
||
.filter((f) => IMG_RE.test(f.name))
|
||
.map((f) => ({
|
||
name: f.name,
|
||
path: f.path,
|
||
size: f.size,
|
||
mtime: f.mtime,
|
||
url: deliveryUrl(f.path),
|
||
thumb: deliveryUrl(f.path, "w_240,h_240,c_fill"),
|
||
})),
|
||
};
|
||
}
|
||
|
||
// Multipart isteği olduğu gibi Openinary'ye aktar (folder alanı client FormData'sında)
|
||
export async function proxyUpload(req) {
|
||
const res = await fetch(`${BASE()}/api/upload`, {
|
||
method: "POST",
|
||
headers: {
|
||
Authorization: `Bearer ${KEY()}`,
|
||
"content-type": req.headers["content-type"],
|
||
...(req.headers["content-length"]
|
||
? { "content-length": req.headers["content-length"] }
|
||
: {}),
|
||
},
|
||
body: req,
|
||
duplex: "half",
|
||
});
|
||
const body = await res.json().catch(() => ({}));
|
||
if (body?.files) {
|
||
for (const f of body.files) {
|
||
if (f.path) {
|
||
f.url = deliveryUrl(f.path);
|
||
f.thumb = deliveryUrl(f.path, "w_240,h_240,c_fill");
|
||
}
|
||
}
|
||
}
|
||
return { status: res.status, body };
|
||
}
|
||
|
||
export async function deleteMedia(path) {
|
||
const clean = assertInsideRoot(path);
|
||
if (clean === MEDIA_ROOT) {
|
||
throw Object.assign(new Error("Kök klasör silinemez"), { status: 400 });
|
||
}
|
||
const res = await fetch(
|
||
`${BASE()}/api/storage/${clean
|
||
.split("/")
|
||
.map(encodeURIComponent)
|
||
.join("/")}`,
|
||
{ method: "DELETE", headers: { Authorization: `Bearer ${KEY()}` } },
|
||
);
|
||
if (!res.ok) {
|
||
throw Object.assign(new Error(`Silme hatası (${res.status})`), {
|
||
status: 502,
|
||
});
|
||
}
|
||
return true;
|
||
}
|