diff --git a/src/server/request-handler.ts b/src/server/request-handler.ts
index db5ee1d..3fb70cc 100644
--- a/src/server/request-handler.ts
+++ b/src/server/request-handler.ts
@@ -19,6 +19,34 @@ type ShareListingEntry = {
isCollection: boolean;
};
+function resolveDavUrlFromShareUrl(shareUrl: string): string {
+ const url = new URL(shareUrl);
+ const publicShare = url.pathname.match(/\/s\/([^/?#]+)/);
+
+ if (publicShare) {
+ return `${url.origin}/public.php/dav/files/${publicShare[1]}/`;
+ }
+
+ const davShare = url.pathname.match(/\/public\.php\/dav\/files\/([^/?#]+)\/?/);
+
+ if (davShare) {
+ return `${url.origin}/public.php/dav/files/${davShare[1]}/`;
+ }
+
+ throw new Error("Bitte einen öffentlichen Nextcloud-Share-Link einfügen.");
+}
+
+async function proxyUpstream(url: string, init?: RequestInit) {
+ const upstream = await fetch(url, init);
+ const body = await upstream.arrayBuffer();
+
+ return {
+ body: Buffer.from(body),
+ contentType: upstream.headers.get("content-type") ?? "application/octet-stream",
+ status: upstream.status
+ };
+}
+
const demoPhotos: Photo[] = [
{
id: "demo-1",
@@ -581,7 +609,7 @@ function htmlPage(): string {
function parseShareInput(value) {
const trimmed = value.trim();
const url = new URL(trimmed);
- const publicShare = url.pathname.match(/\/s\/([^/?#]+)/);
+ const publicShare = url.pathname.match(/\\\/s\\\/([^/?#]+)/);
if (publicShare) {
return {
origin: url.origin,
@@ -589,7 +617,7 @@ function htmlPage(): string {
};
}
- const davShare = url.pathname.match(/\/public\.php\/dav\/files\/([^/?#]+)\/?/);
+ const davShare = url.pathname.match(/\\\/public\\.php\\\/dav\\\/files\\\/([^/?#]+)\\\/?/);
if (davShare) {
return {
origin: url.origin,
@@ -640,36 +668,19 @@ function htmlPage(): string {
}
async function loadShareListing(davUrl) {
- const response = await fetch(davUrl, {
- method: "PROPFIND",
- mode: "cors",
- headers: {
- Depth: "1",
- "Content-Type": "application/xml; charset=utf-8",
- Accept: "application/xml, text/xml"
- },
- body:
- '' +
- '' +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- "" +
- ""
- });
+ const response = await fetch(
+ "/api/nextcloud/list?share=" + encodeURIComponent(shareUrl.value.trim())
+ );
if (!response.ok) {
- throw new Error("WebDAV hat mit Status " + response.status + " geantwortet.");
+ throw new Error("Nextcloud-Liste konnte nicht geladen werden: " + response.status);
}
return parseListing(await response.text(), davUrl);
}
async function readRemotePhoto(entry) {
- const response = await fetch(entry.href, { mode: "cors" });
+ const response = await fetch("/api/nextcloud/blob?url=" + encodeURIComponent(entry.href));
if (!response.ok) {
throw new Error("Bild konnte nicht geladen werden: " + entry.name);
}
@@ -781,6 +792,84 @@ export function createRequestHandler() {
return;
}
+ if (url.pathname === "/api/nextcloud/list") {
+ const share = url.searchParams.get("share");
+
+ if (!share) {
+ res.statusCode = 400;
+ res.setHeader("content-type", "application/json; charset=utf-8");
+ res.end(JSON.stringify({ error: "share parameter missing" }));
+ return;
+ }
+
+ try {
+ const davUrl = resolveDavUrlFromShareUrl(share);
+ const xml =
+ '' +
+ '' +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "" +
+ "";
+
+ const upstream = await proxyUpstream(davUrl, {
+ method: "PROPFIND",
+ headers: {
+ Depth: "1",
+ "Content-Type": "application/xml; charset=utf-8",
+ Accept: "application/xml, text/xml"
+ },
+ body: xml
+ });
+
+ res.statusCode = upstream.status;
+ res.setHeader("content-type", upstream.contentType);
+ res.end(upstream.body);
+ return;
+ } catch (error) {
+ res.statusCode = 502;
+ res.setHeader("content-type", "application/json; charset=utf-8");
+ res.end(
+ JSON.stringify({
+ error: error instanceof Error ? error.message : "upstream request failed"
+ })
+ );
+ return;
+ }
+ }
+
+ if (url.pathname === "/api/nextcloud/blob") {
+ const target = url.searchParams.get("url");
+
+ if (!target) {
+ res.statusCode = 400;
+ res.setHeader("content-type", "application/json; charset=utf-8");
+ res.end(JSON.stringify({ error: "url parameter missing" }));
+ return;
+ }
+
+ try {
+ const upstream = await proxyUpstream(target);
+ res.statusCode = upstream.status;
+ res.setHeader("content-type", upstream.contentType);
+ res.end(upstream.body);
+ return;
+ } catch (error) {
+ res.statusCode = 502;
+ res.setHeader("content-type", "application/json; charset=utf-8");
+ res.end(
+ JSON.stringify({
+ error: error instanceof Error ? error.message : "upstream request failed"
+ })
+ );
+ return;
+ }
+ }
+
res.statusCode = 200;
res.setHeader("content-type", "text/html; charset=utf-8");
res.end(htmlPage());