lbry-desktop/web/setup/publish-v1.js
infinite-persistence 9b44b7eb91 Add a timeout on SDK calls to allow specific error messages.
## Issue 1263
Previously, we tried to inform the user that when an SDK call such as `support_create` and `publish` fails (specifically, timed out), the operation could be successful -- please check the transactions later.

However, we only covered the case of `fetch` actually getting a response that indicated a timeout, e.g. "status = 524". For our SDK case, the timeout scenario is an error that goes into the `catch` block. In the `catch` block, we can't differentiate whether it is a timeout because it only returns a generic "failed to fetch" message.

## New Approach
Since `fetch` does not support a timeout value, the usual solution is to wrap it with a `setTimeout`. This already exists in our code as `fetchWithTimeout` (yay).

By setting a timeout that is lower than the browser's default and also lower than the SDK operation (90s for most commands, 5m for `publish`), we would now have a way to detect a timeout and inform the user.

Firefox's 90s seems to be the lowest common denominator ... so 60s was chosen as the default (added some buffer).

For the case of 'publish', it is actually called in the backend, so wrap the xhr call with a timeout as well.
2022-05-04 08:10:17 -04:00

80 lines
2.4 KiB
JavaScript

// @flow
// https://api.na-backend.odysee.com/api/v1/proxy currently expects publish to
// consist of a multipart/form-data POST request with:
// - 'file' binary
// - 'json_payload' publish params to be passed to the server's sdk.
import { PUBLISH_TIMEOUT_BUT_LIKELY_SUCCESSFUL } from '../../ui/constants/errors';
import { X_LBRY_AUTH_TOKEN } from '../../ui/constants/token';
import { doUpdateUploadAdd, doUpdateUploadProgress, doUpdateUploadRemove } from '../../ui/redux/actions/publish';
import { LBRY_WEB_PUBLISH_API } from 'config';
const ENDPOINT = LBRY_WEB_PUBLISH_API;
const ENDPOINT_METHOD = 'publish';
const PUBLISH_FETCH_TIMEOUT_MS = 60000;
export function makeUploadRequest(
token: string,
params: FileUploadSdkParams,
file: File | string,
isPreview?: boolean
) {
const { remote_url: remoteUrl } = params;
const body = new FormData();
if (file) {
body.append('file', file);
delete params['remote_url'];
} else if (remoteUrl) {
body.append('remote_url', remoteUrl);
delete params['remote_url'];
}
const { uploadUrl, guid, ...sdkParams } = params;
const jsonPayload = JSON.stringify({
jsonrpc: '2.0',
method: ENDPOINT_METHOD,
params: sdkParams,
id: new Date().getTime(),
});
// no fileData? do the livestream remote publish
body.append('json_payload', jsonPayload);
return new Promise<any>((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('POST', ENDPOINT);
xhr.setRequestHeader(X_LBRY_AUTH_TOKEN, token);
xhr.timeout = PUBLISH_FETCH_TIMEOUT_MS;
xhr.responseType = 'json';
xhr.upload.onprogress = (e) => {
const percentage = ((e.loaded / e.total) * 100).toFixed(2);
window.store.dispatch(doUpdateUploadProgress({ guid, progress: percentage }));
};
xhr.onload = () => {
window.store.dispatch(doUpdateUploadRemove(guid));
resolve(xhr);
};
xhr.onerror = () => {
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'error' }));
reject(new Error(__('There was a problem with your upload. Please try again.')));
};
xhr.ontimeout = () => {
window.store.dispatch(doUpdateUploadProgress({ guid, status: 'error' }));
reject(new Error(PUBLISH_TIMEOUT_BUT_LIKELY_SUCCESSFUL));
};
xhr.onabort = () => {
window.store.dispatch(doUpdateUploadRemove(guid));
};
if (!isPreview) {
window.store.dispatch(doUpdateUploadAdd(file, params, xhr));
}
xhr.send(body);
});
}