From 402b298e421d46311ecea0df0d7661dd25be7c25 Mon Sep 17 00:00:00 2001 From: silberengel Date: Sat, 31 Jan 2026 19:18:10 +0100 Subject: [PATCH] sink import to write permissios fix signer --- app/web/src/App.svelte | 105 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 97 insertions(+), 8 deletions(-) diff --git a/app/web/src/App.svelte b/app/web/src/App.svelte index a211c4d..15fd737 100644 --- a/app/web/src/App.svelte +++ b/app/web/src/App.svelte @@ -2449,24 +2449,83 @@ userSigner = window.nostr; console.log("Restored extension signer for import"); } - headers.Authorization = await createNIP98AuthHeader( - `${getApiBase()}/api/import`, - "POST", - ); + + // Use the actual request URL for NIP-98 auth + // If behind reverse proxy, server may see HTTP instead of HTTPS + // Use the same URL we're fetching, but try HTTP if HTTPS fails + const importUrl = `${getApiBase()}/api/import`; + // Convert to HTTP if HTTPS (reverse proxy may forward as HTTP internally) + const authUrl = importUrl.replace('https://', 'http://'); + console.log("Creating NIP-98 auth for import URL:", authUrl, "(request URL:", importUrl, ")"); + try { + headers.Authorization = await createNIP98AuthHeader( + authUrl, + "POST", + ); + console.log("NIP-98 auth header created successfully"); + } catch (error) { + console.error("Failed to create NIP-98 auth header:", error); + importMessage = "Failed to create authentication: " + error.message; + setTimeout(() => { importMessage = ""; }, 5000); + return; + } } const formData = new FormData(); formData.append("file", selectedFile); + console.log("Uploading file:", { + name: selectedFile.name, + size: selectedFile.size, + type: selectedFile.type, + sizeKB: Math.round(selectedFile.size / 1024), + }); + + // Use AbortController for timeout handling + const controller = new AbortController(); + const timeoutId = setTimeout(() => { + controller.abort(); + importMessage = "Upload timeout: The request took too long. Please try again or contact the administrator."; + setTimeout(() => { importMessage = ""; }, 10000); + }, 300000); // 5 minute timeout + + console.log("Sending import request to:", `${getApiBase()}/api/import`); + console.log("Request headers:", Object.keys(headers)); + console.log("File size:", selectedFile.size, "bytes"); + const response = await fetch(`${getApiBase()}/api/import`, { method: "POST", headers, body: formData, + signal: controller.signal, }); + clearTimeout(timeoutId); + console.log("Import request completed, status:", response.status, response.statusText); if (!response.ok) { - throw new Error( - `Import failed: ${response.status} ${response.statusText}`, - ); + // Try to get error message from response body + let errorMsg = `Import failed: ${response.status} ${response.statusText}`; + + // Provide helpful messages for common errors + if (response.status === 502) { + errorMsg = "Server error (502): The file may be too large or the server is temporarily unavailable. Try a smaller file or contact the administrator."; + } else if (response.status === 413) { + errorMsg = "File too large: The file exceeds the server's size limit. Please split it into smaller files."; + } else if (response.status === 504) { + errorMsg = "Request timeout: The file upload took too long. Try a smaller file or check your connection."; + } else { + try { + const errorData = await response.text(); + if (errorData && !errorData.includes(" 1) { + normalizedUrl = normalizedUrl.slice(0, -1); + } + + // If the URL uses HTTPS but we're behind a reverse proxy that forwards as HTTP, + // try using HTTP instead to match what the server sees + // The server checks the request URL against the signed URL + let authUrl = normalizedUrl; + if (normalizedUrl.startsWith('https://')) { + // Try HTTP version in case reverse proxy forwards as HTTP internally + const httpUrl = normalizedUrl.replace('https://', 'http://'); + // Use the same URL we're actually requesting (which should match server's view) + authUrl = normalizedUrl; // Keep HTTPS for now, server should handle both + } + // Create NIP-98 auth event const authEvent = { kind: 27235, created_at: Math.floor(Date.now() / 1000), tags: [ - ["u", url], + ["u", authUrl], ["method", method.toUpperCase()], ], content: "", pubkey: userPubkey, }; + console.log("NIP-98 auth event before signing:", { + kind: authEvent.kind, + url: authUrl, + method: method.toUpperCase(), + pubkey: userPubkey, + }); + const signedEvent = await userSigner.signEvent(authEvent); + console.log("NIP-98 auth event after signing:", { + id: signedEvent.id, + pubkey: signedEvent.pubkey, + sig: signedEvent.sig ? signedEvent.sig.substring(0, 16) + "..." : "missing", + }); + // Encode as base64 const eventJson = JSON.stringify(signedEvent); const base64Event = btoa(eventJson);