From a825934e766763c8aa2d9755c4040f9d12bca505 Mon Sep 17 00:00:00 2001 From: nugaon Date: Fri, 9 May 2025 14:27:55 +0200 Subject: [PATCH 1/4] feat: tag api console --- src/app/components/FileUploadUtils.ts | 105 +++++++++++++++++++++++--- 1 file changed, 95 insertions(+), 10 deletions(-) diff --git a/src/app/components/FileUploadUtils.ts b/src/app/components/FileUploadUtils.ts index 812aec2..34823ef 100644 --- a/src/app/components/FileUploadUtils.ts +++ b/src/app/components/FileUploadUtils.ts @@ -68,6 +68,68 @@ interface StampResponse { batchTTL: number; } +const createTag = async (swarmApiUrl: string): Promise => { + return fetch(`${swarmApiUrl}/tags`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => { + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); + } + return response.json(); + }) + .then(data => data.uid) + .catch(error => { + console.error('Error creating tag:', error); + throw error; + }) +} + +const deleteTag = async (swarmApiUrl: string, tagId: number): Promise => { + return fetch(`${swarmApiUrl}/tags/${tagId}`, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => { + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); + } + return response.json(); + }) + .catch(error => { + console.error('Error deleting tag:', error); + throw error; + }) +} + +const progressTag = async (swarmApiUrl: string, tagId: number): Promise => { + return fetch(`${swarmApiUrl}/tags/${tagId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json' + } + }) + .then(response => { + if (!response.ok) { + throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); + } + return response.json(); + }) + .then(data => { + console.log('Tag progress:', data); + return Number(data.split) / (Number(data.synced) + Number(data.seen)) + }) + .catch(error => { + console.error('Error deleting tag:', error); + throw error; + }) +} + /** * Handle the file upload process * @param params Parameters for file upload @@ -125,13 +187,17 @@ export const handleFileUpload = async (params: FileUploadParams): Promise, - baseUrl: string + beeApiUrl: string ): Promise => { - console.log('Starting file upload...'); + console.log("Starting file upload..."); + const tagId = await createTag(beeApiUrl); + console.log("Tag created with ID:", tagId); + headers["Swarm-Tag"] = tagId.toString(); + console.log("Headers with tag:", headers); // Add the filename as a query parameter - const url = `${baseUrl}?name=${encodeURIComponent(file.name)}`; - console.log('Upload URL with filename:', url); + const url = `${beeApiUrl}/bzz?name=${encodeURIComponent(file.name)}`; + console.log("Upload URL with filename:", url); return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); @@ -152,6 +218,19 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { + while (true) { + const progress = await progressTag(beeApiUrl, tagId); + console.log('distribution progress', progress); + if (progress === 1) { + deleteTag(beeApiUrl, tagId); + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + }; + pollTagProgress(); } } }; @@ -168,14 +247,16 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { - console.error('XHR Error:', e); - reject(new Error('Network request failed')); + xhr.onerror = (e) => { + console.error("XHR Error:", e); + deleteTag(beeApiUrl, tagId); + reject(new Error("Network request failed")); }; xhr.ontimeout = () => { - console.error('Upload timed out'); - reject(new Error('Upload timed out')); + console.error("Upload timed out"); + deleteTag(beeApiUrl, tagId); + reject(new Error("Upload timed out")); }; console.log('Sending file:', file.name, file.size); @@ -292,7 +373,11 @@ export const handleFileUpload = async (params: FileUploadParams): Promise Date: Wed, 14 May 2025 18:25:15 +0200 Subject: [PATCH 2/4] feat: wire tag progressing --- src/app/components/FileUploadUtils.ts | 11 +++-- src/app/components/SwapComponent.tsx | 68 +++++++++++++-------------- 2 files changed, 39 insertions(+), 40 deletions(-) diff --git a/src/app/components/FileUploadUtils.ts b/src/app/components/FileUploadUtils.ts index bc01b89..023adc3 100644 --- a/src/app/components/FileUploadUtils.ts +++ b/src/app/components/FileUploadUtils.ts @@ -88,7 +88,7 @@ const createTag = async (swarmApiUrl: string): Promise => { }) } -const deleteTag = async (swarmApiUrl: string, tagId: number): Promise => { +const deleteTag = async (swarmApiUrl: string, tagId: number): Promise => { return fetch(`${swarmApiUrl}/tags/${tagId}`, { method: 'DELETE', headers: { @@ -99,7 +99,7 @@ const deleteTag = async (swarmApiUrl: string, tagId: number): Promise => if (!response.ok) { throw new Error(`Network response was not ok: ${response.status} ${response.statusText}`); } - return response.json(); + return }) .catch(error => { console.error('Error deleting tag:', error); @@ -122,7 +122,7 @@ const progressTag = async (swarmApiUrl: string, tagId: number): Promise }) .then(data => { console.log('Tag progress:', data); - return Number(data.split) / (Number(data.synced) + Number(data.seen)) + return (Number(data.synced) + Number(data.seen)) / Number(data.split) }) .catch(error => { console.error('Error deleting tag:', error); @@ -222,9 +222,11 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { while (true) { const progress = await progressTag(beeApiUrl, tagId); + setUploadProgress(Math.min(99, Math.floor(progress*100))); console.log('distribution progress', progress); if (progress === 1) { deleteTag(beeApiUrl, tagId); + setUploadStep('complete'); break; } await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -293,7 +295,7 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { setUploadStep('idle'); diff --git a/src/app/components/SwapComponent.tsx b/src/app/components/SwapComponent.tsx index d1a3c2c..b08dccd 100644 --- a/src/app/components/SwapComponent.tsx +++ b/src/app/components/SwapComponent.tsx @@ -1353,7 +1353,7 @@ const SwapComponent: React.FC = () => { ? 'Waiting for batch to be usable...' : statusMessage.step === 'Uploading' ? isDistributing - ? 'Distributing file chunks...' + ? `Distributing file chunks... ${uploadProgress.toFixed(1)}%` : `Uploading... ${uploadProgress.toFixed(1)}%` : 'Processing...'} @@ -1363,40 +1363,38 @@ const SwapComponent: React.FC = () => { {uploadStep === 'uploading' && ( <> - {!isDistributing ? ( - // Show the regular progress bar during upload -
-
-
- ) : ( - // Show the distribution animation when distributing to Swarm -
- {/* Center cube (source node) */} -
- - {/* Target nodes (cubes) */} -
-
-
-
-
-
-
-
- - {/* Chunks being distributed */} -
-
-
-
-
-
-
-
-
+ {/* Show the regular progress bar during upload */} +
+
+
+ {/* Show the distribution animation when distributing to Swarm */} + {isDistributing && (
+ {/* Center cube (source node) */} +
+ + {/* Target nodes (cubes) */} +
+
+
+
+
+
+
+
+ + {/* Chunks being distributed */} +
+
+
+
+
+
+
+
+
)} )} From 83502a89c84ccf41940bf85106195b449d25de3b Mon Sep 17 00:00:00 2001 From: Cardinal Date: Thu, 15 May 2025 15:36:12 +0200 Subject: [PATCH 3/4] add cors and tags for example config that have swarm-tag header --- backend/README.md | 61 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 6 deletions(-) diff --git a/backend/README.md b/backend/README.md index 1d6b263..fe8fe00 100644 --- a/backend/README.md +++ b/backend/README.md @@ -47,6 +47,25 @@ server_name swarming.site www.swarming.site; # Proxy API Requests to Backend for /bzz location /bzz { + + # Add CORS headers for development + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,swarm-postage-batch-id,swarm-pin,swarm-deferred-upload,registry-address,swarm-collection,x-upload-signed-message,x-uploader-address,x-file-name,x-message-content,Swarm-Index-Document,Swarm-Error-Document,swarm-tag' always; + add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; + + # Handle preflight requests (OPTIONS) + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,swarm-postage-batch-id,swarm-pin,swarm-deferred-upload,registry-address,swarm-collection,x-upload-signed-message,x-uploader-address,x-file-name,x-message-content,Swarm-Index-Document,Swarm-Error-Document,swarm-tag' always; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain; charset=utf-8'; + add_header 'Content-Length' 0; + return 204; + } + + proxy_pass http://localhost:3333; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; @@ -65,11 +84,10 @@ server_name swarming.site www.swarming.site; # Proxy /stamps to Bee node location /stamps { - # Add CORS headers + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; # Handle preflight requests (OPTIONS) if ($request_method = 'OPTIONS') { @@ -98,11 +116,10 @@ server_name swarming.site www.swarming.site; # Proxy /wallet to Bee node location /wallet { - # Add CORS headers + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; - add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization' always; - add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range' always; # Handle preflight requests (OPTIONS) if ($request_method = 'OPTIONS') { @@ -130,6 +147,38 @@ server_name swarming.site www.swarming.site; proxy_send_timeout 3600s; } + # Proxy /tags to Bee node + location /tags { + + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,swarm-tag' always; + + # Handle preflight requests (OPTIONS) + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, PUT, DELETE' always; + add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,swarm-tag' always; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain; charset=utf-8'; + add_header 'Content-Length' 0; + return 204; + } + proxy_pass http://localhost:1633; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + + client_max_body_size 0; + proxy_read_timeout 3600s; + proxy_send_timeout 3600s; + } + } # Smart contract registry From 6b3b7336c3cd9acffd94882089634b4677bd8bf8 Mon Sep 17 00:00:00 2001 From: nugaon Date: Fri, 16 May 2025 09:58:42 +0200 Subject: [PATCH 4/4] fix: upload percentage --- src/app/components/FileUploadUtils.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/app/components/FileUploadUtils.ts b/src/app/components/FileUploadUtils.ts index 023adc3..36b0932 100644 --- a/src/app/components/FileUploadUtils.ts +++ b/src/app/components/FileUploadUtils.ts @@ -222,11 +222,20 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { while (true) { const progress = await progressTag(beeApiUrl, tagId); - setUploadProgress(Math.min(99, Math.floor(progress*100))); + setUploadProgress(isNaN(progress) ? 0 : Math.min(99, Math.floor(progress*100))); console.log('distribution progress', progress); if (progress === 1) { deleteTag(beeApiUrl, tagId); - setUploadStep('complete'); + + if (xhr.status >= 200 && xhr.status < 300) { + setUploadProgress(100); + } + console.log(`Upload completed with status: ${xhr.status}`); + resolve({ + ok: xhr.status >= 200 && xhr.status < 300, + status: xhr.status, + text: () => Promise.resolve(xhr.responseText), + }); break; } await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -238,15 +247,7 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { - if (xhr.status >= 200 && xhr.status < 300) { - setUploadProgress(100); - } console.log(`Upload completed with status: ${xhr.status}`); - resolve({ - ok: xhr.status >= 200 && xhr.status < 300, - status: xhr.status, - text: () => Promise.resolve(xhr.responseText), - }); }; xhr.onerror = (e) => { @@ -401,6 +402,7 @@ export const handleFileUpload = async (params: FileUploadParams): Promise { setUploadStep('idle');