137 lines
4.5 KiB
TypeScript
137 lines
4.5 KiB
TypeScript
// src/routes/assets/[...path]/+server.ts
|
|
import { client } from "$lib/server/nextcloud";
|
|
import { error } from "@sveltejs/kit";
|
|
import type { RequestHandler } from "./$types";
|
|
import mime from "mime-types";
|
|
import { env } from "$env/dynamic/private";
|
|
import { withCache } from "$lib/server/cache";
|
|
|
|
const DEFAULT_TTL = 1000 * 60 * 15; // 15min
|
|
const EXTENDED_TTL = 1000 * 60 * 60 * 24 * 7; // 1 week
|
|
|
|
// Define what we are storing in the cache
|
|
type CachedAsset = {
|
|
buffer: ArrayBuffer;
|
|
contentType: string;
|
|
};
|
|
|
|
export const GET: RequestHandler = async ({ params, url }) => {
|
|
const { path } = params;
|
|
const width = url.searchParams.get("w");
|
|
const height = url.searchParams.get("h");
|
|
|
|
if (!path) {
|
|
throw error(400, "Path is required");
|
|
}
|
|
|
|
// 1. Create a unique cache key for this specific asset variation
|
|
const cacheKey = `asset:${path}?w=${width ?? ""}&h=${height ?? ""}`;
|
|
|
|
const ttl = path.toString().includes("cover") ? DEFAULT_TTL : EXTENDED_TTL;
|
|
|
|
try {
|
|
// 2. Use withCache to wrap the fetching logic
|
|
const cachedData = await withCache<CachedAsset>(
|
|
cacheKey,
|
|
async () => {
|
|
// --- START OF FETCH LOGIC ---
|
|
// This function only runs if the data is NOT in the cache
|
|
|
|
const pathsToTry = [path];
|
|
if (!path.startsWith("_")) {
|
|
pathsToTry.push(`_${path}`);
|
|
}
|
|
let lastError: any = null;
|
|
|
|
for (const currentPath of pathsToTry) {
|
|
try {
|
|
// A. Try Nextcloud Preview (Resizing)
|
|
if (width || height) {
|
|
const baseUrl = env.NEXTCLOUD_URL?.endsWith("/")
|
|
? env.NEXTCLOUD_URL
|
|
: `${env.NEXTCLOUD_URL}/`;
|
|
|
|
let fullPath = currentPath;
|
|
if (env.NEXTCLOUD_BASE_DIR) {
|
|
const baseDir = env.NEXTCLOUD_BASE_DIR.replace(
|
|
/^\/+|\/+$/g,
|
|
"",
|
|
);
|
|
fullPath = baseDir ? `${baseDir}/${currentPath}` : currentPath;
|
|
}
|
|
|
|
const isJpg = fullPath.match(/\.(jpg|jpeg)$/i);
|
|
const previewExtension = isJpg ? "jpg" : "png";
|
|
const previewUrl = new URL(
|
|
`${baseUrl}index.php/core/preview.${previewExtension}`,
|
|
);
|
|
|
|
const fileParam = fullPath.startsWith("/")
|
|
? fullPath
|
|
: `/${fullPath}`;
|
|
previewUrl.searchParams.set("file", fileParam);
|
|
if (width) previewUrl.searchParams.set("x", width);
|
|
if (height) previewUrl.searchParams.set("y", height);
|
|
previewUrl.searchParams.set("forceIcon", "0");
|
|
|
|
const auth = Buffer.from(
|
|
`${env.NEXTCLOUD_USER}:${env.NEXTCLOUD_PASSWORD}`,
|
|
).toString("base64");
|
|
|
|
console.log(
|
|
`Fetching preview: ${previewUrl.toString()} for file: ${fullPath}`,
|
|
);
|
|
|
|
const ncResponse = await fetch(previewUrl.toString(), {
|
|
headers: { Authorization: `Basic ${auth}` },
|
|
});
|
|
|
|
if (ncResponse.ok) {
|
|
const buffer = await ncResponse.arrayBuffer();
|
|
const contentType =
|
|
ncResponse.headers.get("Content-Type") || "image/png";
|
|
|
|
// Return the raw data to be cached
|
|
return { buffer, contentType };
|
|
} else {
|
|
console.warn(
|
|
`Nextcloud preview failed (${ncResponse.status}) for ${fullPath}.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
// B. Try WebDAV (Original)
|
|
console.log(`Fetching original file via WebDAV: /${currentPath}`);
|
|
const data = await client.getFileContents("/" + currentPath);
|
|
const buffer = data as ArrayBuffer;
|
|
const contentType =
|
|
mime.lookup(currentPath) || "application/octet-stream";
|
|
|
|
// Return the raw data to be cached
|
|
return { buffer, contentType };
|
|
} catch (e) {
|
|
lastError = e;
|
|
continue; // Try next path variant
|
|
}
|
|
}
|
|
|
|
throw lastError || new Error("Asset not found");
|
|
// --- END OF FETCH LOGIC ---
|
|
},
|
|
ttl,
|
|
);
|
|
|
|
// 3. Construct a fresh Response using the cached data
|
|
return new Response(cachedData.buffer, {
|
|
headers: {
|
|
"Content-Type": cachedData.contentType,
|
|
"Cache-Control": "public, max-age=86400", // Browser cache
|
|
Vary: "Accept-Encoding",
|
|
},
|
|
});
|
|
} catch (e) {
|
|
console.error(`Error fetching asset: ${path}`, e);
|
|
throw error(404, "Asset not found");
|
|
}
|
|
};
|