Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 55 additions & 6 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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') {
Expand Down Expand Up @@ -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') {
Expand Down Expand Up @@ -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
Expand Down
126 changes: 107 additions & 19 deletions src/app/components/FileUploadUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,68 @@ interface StampResponse {
batchTTL: number;
}

const createTag = async (swarmApiUrl: string): Promise<number> => {
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<void> => {
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
})
.catch(error => {
console.error('Error deleting tag:', error);
throw error;
})
}

const progressTag = async (swarmApiUrl: string, tagId: number): Promise<number> => {
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.synced) + Number(data.seen)) / Number(data.split)
})
.catch(error => {
console.error('Error deleting tag:', error);
throw error;
})
}

/**
* Handle the file upload process
* @param params Parameters for file upload
Expand Down Expand Up @@ -125,13 +187,17 @@ export const handleFileUpload = async (params: FileUploadParams): Promise<string
const uploadLargeFile = async (
file: File,
headers: Record<string, string>,
baseUrl: string
beeApiUrl: string
): Promise<XHRResponse> => {
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();
Expand All @@ -152,30 +218,48 @@ export const handleFileUpload = async (params: FileUploadParams): Promise<string

if (percent === 100) {
setIsDistributing(true);
// poll tagid progress and then delete tag
const pollTagProgress = async () => {
while (true) {
const progress = await progressTag(beeApiUrl, tagId);
setUploadProgress(isNaN(progress) ? 0 : Math.min(99, Math.floor(progress*100)));
console.log('distribution progress', progress);
if (progress === 1) {
deleteTag(beeApiUrl, tagId);

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));
}
};
pollTagProgress();
}
}
};

xhr.onload = () => {
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 => {
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);
Expand Down Expand Up @@ -212,7 +296,7 @@ export const handleFileUpload = async (params: FileUploadParams): Promise<string
serveUncompressed && (isTarFile || isArchive) ? 'application/x-tar' : processedFile.type,
'swarm-postage-batch-id': postageBatchId,
'swarm-pin': 'false',
'swarm-deferred-upload': 'false',
'swarm-deferred-upload': 'true', // FIXME: https://github.com/ethersphere/bee/issues/5096
'swarm-collection': serveUncompressed && (isTarFile || isArchive) ? 'true' : 'false',
};

Expand Down Expand Up @@ -292,7 +376,11 @@ export const handleFileUpload = async (params: FileUploadParams): Promise<string
message: 'Uploading file...',
});

const uploadResponse = await uploadLargeFile(processedFile, baseHeaders, `${beeApiUrl}/bzz`);
const uploadResponse = await uploadLargeFile(
processedFile,
baseHeaders,
beeApiUrl
);

if (!uploadResponse.ok) {
throw new Error(`Upload failed with status ${uploadResponse.status}`);
Expand Down
68 changes: 33 additions & 35 deletions src/app/components/SwapComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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...'}
</>
Expand All @@ -1363,40 +1363,38 @@ const SwapComponent: React.FC = () => {
</button>
{uploadStep === 'uploading' && (
<>
{!isDistributing ? (
// Show the regular progress bar during upload
<div className={styles.progressBarContainer}>
<div
className={styles.progressBar}
style={{ width: `${uploadProgress}%` }}
/>
</div>
) : (
// Show the distribution animation when distributing to Swarm
<div className={styles.distributionContainer}>
{/* Center cube (source node) */}
<div className={styles.centerNode}></div>

{/* Target nodes (cubes) */}
<div className={`${styles.node} ${styles.node1}`}></div>
<div className={`${styles.node} ${styles.node2}`}></div>
<div className={`${styles.node} ${styles.node3}`}></div>
<div className={`${styles.node} ${styles.node4}`}></div>
<div className={`${styles.node} ${styles.node5}`}></div>
<div className={`${styles.node} ${styles.node6}`}></div>
<div className={`${styles.node} ${styles.node7}`}></div>
<div className={`${styles.node} ${styles.node8}`}></div>

{/* Chunks being distributed */}
<div className={`${styles.chunk} ${styles.chunk1}`}></div>
<div className={`${styles.chunk} ${styles.chunk2}`}></div>
<div className={`${styles.chunk} ${styles.chunk3}`}></div>
<div className={`${styles.chunk} ${styles.chunk4}`}></div>
<div className={`${styles.chunk} ${styles.chunk5}`}></div>
<div className={`${styles.chunk} ${styles.chunk6}`}></div>
<div className={`${styles.chunk} ${styles.chunk7}`}></div>
<div className={`${styles.chunk} ${styles.chunk8}`}></div>
</div>
{/* Show the regular progress bar during upload */}
<div className={styles.progressBarContainer}>
<div
className={styles.progressBar}
style={{ width: `${uploadProgress}%` }}
/>
</div>
{/* Show the distribution animation when distributing to Swarm */}
{isDistributing && (<div className={styles.distributionContainer}>
{/* Center cube (source node) */}
<div className={styles.centerNode}></div>

{/* Target nodes (cubes) */}
<div className={`${styles.node} ${styles.node1}`}></div>
<div className={`${styles.node} ${styles.node2}`}></div>
<div className={`${styles.node} ${styles.node3}`}></div>
<div className={`${styles.node} ${styles.node4}`}></div>
<div className={`${styles.node} ${styles.node5}`}></div>
<div className={`${styles.node} ${styles.node6}`}></div>
<div className={`${styles.node} ${styles.node7}`}></div>
<div className={`${styles.node} ${styles.node8}`}></div>

{/* Chunks being distributed */}
<div className={`${styles.chunk} ${styles.chunk1}`}></div>
<div className={`${styles.chunk} ${styles.chunk2}`}></div>
<div className={`${styles.chunk} ${styles.chunk3}`}></div>
<div className={`${styles.chunk} ${styles.chunk4}`}></div>
<div className={`${styles.chunk} ${styles.chunk5}`}></div>
<div className={`${styles.chunk} ${styles.chunk6}`}></div>
<div className={`${styles.chunk} ${styles.chunk7}`}></div>
<div className={`${styles.chunk} ${styles.chunk8}`}></div>
</div>
)}
</>
)}
Expand Down