c4d6fa9ff0
This reverts commit 36a63e27c1
.
Probably a bad idea, too many
298 lines
11 KiB
JavaScript
298 lines
11 KiB
JavaScript
// @flow
|
|
import { NO_AUTH, X_LBRY_AUTH_TOKEN } from 'constants/token';
|
|
|
|
require('proxy-polyfill');
|
|
|
|
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
|
|
//
|
|
// Basic LBRY sdk connection config
|
|
// Offers a proxy to call LBRY sdk methods
|
|
|
|
//
|
|
const Lbry = {
|
|
isConnected: false,
|
|
connectPromise: null,
|
|
daemonConnectionString: 'http://localhost:5279',
|
|
alternateConnectionString: '',
|
|
methodsUsingAlternateConnectionString: [],
|
|
apiRequestHeaders: { 'Content-Type': 'application/json-rpc' },
|
|
|
|
// Allow overriding daemon connection string (e.g. to `/api/proxy` for lbryweb)
|
|
setDaemonConnectionString: (value: string) => {
|
|
Lbry.daemonConnectionString = value;
|
|
},
|
|
|
|
setApiHeader: (key: string, value: string) => {
|
|
Lbry.apiRequestHeaders = Object.assign(Lbry.apiRequestHeaders, { [key]: value });
|
|
},
|
|
|
|
unsetApiHeader: (key) => {
|
|
Object.keys(Lbry.apiRequestHeaders).includes(key) && delete Lbry.apiRequestHeaders[key];
|
|
},
|
|
// Allow overriding Lbry methods
|
|
overrides: {},
|
|
setOverride: (methodName, newMethod) => {
|
|
Lbry.overrides[methodName] = newMethod;
|
|
},
|
|
getApiRequestHeaders: () => Lbry.apiRequestHeaders,
|
|
|
|
// Returns a human readable media type based on the content type or extension of a file that is returned by the sdk
|
|
getMediaType: (contentType: ?string, fileName: ?string) => {
|
|
if (fileName) {
|
|
const formats = [
|
|
[/\.(mp4|m4v|webm|flv|f4v|ogv)$/i, 'video'],
|
|
[/\.(mp3|m4a|aac|wav|flac|ogg|opus)$/i, 'audio'],
|
|
[/\.(jpeg|jpg|png|gif|svg|webp)$/i, 'image'],
|
|
[/\.(h|go|ja|java|js|jsx|c|cpp|cs|css|rb|scss|sh|php|py)$/i, 'script'],
|
|
[/\.(html|json|csv|txt|log|md|markdown|docx|pdf|xml|yml|yaml)$/i, 'document'],
|
|
[/\.(pdf|odf|doc|docx|epub|org|rtf)$/i, 'e-book'],
|
|
[/\.(stl|obj|fbx|gcode)$/i, '3D-file'],
|
|
[/\.(cbr|cbt|cbz)$/i, 'comic-book'],
|
|
[/\.(lbry)$/i, 'application'],
|
|
];
|
|
|
|
const res = formats.reduce((ret, testpair) => {
|
|
switch (testpair[0].test(ret)) {
|
|
case true:
|
|
return testpair[1];
|
|
default:
|
|
return ret;
|
|
}
|
|
}, fileName);
|
|
return res === fileName ? 'unknown' : res;
|
|
} else if (contentType) {
|
|
// $FlowFixMe
|
|
return /^[^/]+/.exec(contentType)[0];
|
|
}
|
|
|
|
return 'unknown';
|
|
},
|
|
|
|
//
|
|
// Lbry SDK Methods
|
|
// https://lbry.tech/api/sdk
|
|
//
|
|
status: (params = {}) => daemonCallWithResult('status', params),
|
|
stop: () => daemonCallWithResult('stop', {}),
|
|
version: () => daemonCallWithResult('version', {}),
|
|
|
|
// Claim fetching and manipulation
|
|
resolve: (params) => daemonCallWithResult('resolve', params, searchRequiresAuth),
|
|
get: (params) => daemonCallWithResult('get', params),
|
|
claim_search: (params) => daemonCallWithResult('claim_search', params, searchRequiresAuth),
|
|
claim_list: (params) => daemonCallWithResult('claim_list', params),
|
|
channel_create: (params) => daemonCallWithResult('channel_create', params),
|
|
channel_update: (params) => daemonCallWithResult('channel_update', params),
|
|
channel_import: (params) => daemonCallWithResult('channel_import', params),
|
|
channel_list: (params) => daemonCallWithResult('channel_list', params),
|
|
stream_abandon: (params) => daemonCallWithResult('stream_abandon', params),
|
|
stream_list: (params) => daemonCallWithResult('stream_list', params),
|
|
channel_abandon: (params) => daemonCallWithResult('channel_abandon', params),
|
|
channel_sign: (params) => daemonCallWithResult('channel_sign', params),
|
|
support_create: (params) => daemonCallWithResult('support_create', params),
|
|
support_list: (params) => daemonCallWithResult('support_list', params),
|
|
stream_repost: (params) => daemonCallWithResult('stream_repost', params),
|
|
collection_resolve: (params) => daemonCallWithResult('collection_resolve', params),
|
|
collection_list: (params) => daemonCallWithResult('collection_list', params),
|
|
collection_create: (params) => daemonCallWithResult('collection_create', params),
|
|
collection_update: (params) => daemonCallWithResult('collection_update', params),
|
|
|
|
// File fetching and manipulation
|
|
file_list: (params = {}) => daemonCallWithResult('file_list', params),
|
|
file_delete: (params = {}) => daemonCallWithResult('file_delete', params),
|
|
file_set_status: (params = {}) => daemonCallWithResult('file_set_status', params),
|
|
blob_delete: (params = {}) => daemonCallWithResult('blob_delete', params),
|
|
blob_list: (params = {}) => daemonCallWithResult('blob_list', params),
|
|
file_reflect: (params = {}) => daemonCallWithResult('file_reflect', params),
|
|
|
|
// Wallet utilities
|
|
wallet_balance: (params = {}) => daemonCallWithResult('wallet_balance', params),
|
|
wallet_decrypt: () => daemonCallWithResult('wallet_decrypt', {}),
|
|
wallet_encrypt: (params = {}) => daemonCallWithResult('wallet_encrypt', params),
|
|
wallet_unlock: (params = {}) => daemonCallWithResult('wallet_unlock', params),
|
|
wallet_list: (params = {}) => daemonCallWithResult('wallet_list', params),
|
|
wallet_send: (params = {}) => daemonCallWithResult('wallet_send', params),
|
|
wallet_status: (params = {}) => daemonCallWithResult('wallet_status', params),
|
|
address_is_mine: (params = {}) => daemonCallWithResult('address_is_mine', params),
|
|
address_unused: (params = {}) => daemonCallWithResult('address_unused', params),
|
|
address_list: (params = {}) => daemonCallWithResult('address_list', params),
|
|
transaction_list: (params = {}) => daemonCallWithResult('transaction_list', params),
|
|
utxo_release: (params = {}) => daemonCallWithResult('utxo_release', params),
|
|
support_abandon: (params = {}) => daemonCallWithResult('support_abandon', params),
|
|
purchase_list: (params = {}) => daemonCallWithResult('purchase_list', params),
|
|
txo_list: (params = {}) => daemonCallWithResult('txo_list', params),
|
|
account_list: (params = {}) => daemonCallWithResult('account_list', params),
|
|
account_set: (params = {}) => daemonCallWithResult('account_set', params),
|
|
|
|
sync_hash: (params = {}) => daemonCallWithResult('sync_hash', params),
|
|
sync_apply: (params = {}) => daemonCallWithResult('sync_apply', params),
|
|
|
|
// Preferences
|
|
preference_get: (params = {}) => daemonCallWithResult('preference_get', params),
|
|
preference_set: (params = {}) => daemonCallWithResult('preference_set', params),
|
|
|
|
// Comments
|
|
comment_list: (params = {}) => daemonCallWithResult('comment_list', params),
|
|
comment_create: (params = {}) => daemonCallWithResult('comment_create', params),
|
|
comment_hide: (params = {}) => daemonCallWithResult('comment_hide', params),
|
|
comment_abandon: (params = {}) => daemonCallWithResult('comment_abandon', params),
|
|
comment_update: (params = {}) => daemonCallWithResult('comment_update', params),
|
|
|
|
// Connect to the sdk
|
|
connect: () => {
|
|
if (Lbry.connectPromise === null) {
|
|
// $FlowFixMe
|
|
Lbry.connectPromise = new Promise((resolve, reject) => {
|
|
let tryNum = 0;
|
|
// Check every half second to see if the daemon is accepting connections
|
|
function checkDaemonStarted() {
|
|
tryNum += 1;
|
|
Lbry.status()
|
|
.then(resolve)
|
|
.catch(() => {
|
|
if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
|
|
setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
|
|
} else {
|
|
reject(new Error('Unable to connect to LBRY'));
|
|
}
|
|
});
|
|
}
|
|
|
|
checkDaemonStarted();
|
|
});
|
|
}
|
|
|
|
// Flow thinks this could be empty, but it will always reuturn a promise
|
|
// $FlowFixMe
|
|
return Lbry.connectPromise;
|
|
},
|
|
|
|
publish: (params = {}) =>
|
|
new Promise((resolve, reject) => {
|
|
if (Lbry.overrides.publish) {
|
|
Lbry.overrides.publish(params).then(resolve, reject);
|
|
} else {
|
|
apiCall('publish', params, resolve, reject);
|
|
}
|
|
}),
|
|
};
|
|
|
|
function checkAndParse(response: Response, method: string) {
|
|
if (!response.ok) {
|
|
// prettier-ignore
|
|
switch (response.status) {
|
|
case 504: // Gateway timeout
|
|
case 524: // Cloudflare: a timeout occurred
|
|
switch (method) {
|
|
case 'publish':
|
|
throw Error(__('[Publish]: Your action timed out, but may have been completed. Refresh and check your Uploads or Wallet page to confirm after a few minutes.'));
|
|
default:
|
|
throw Error(`${method}: ${response.statusText} (${response.status})`);
|
|
}
|
|
default:
|
|
throw Error(`${method}: ${response.statusText} (${response.status})`);
|
|
}
|
|
}
|
|
|
|
if (response.status >= 200 && response.status < 300) {
|
|
return response.json();
|
|
}
|
|
|
|
return response
|
|
.json()
|
|
.then((json) => {
|
|
if (json.error) {
|
|
const errorMessage = typeof json.error === 'object' ? json.error.message : json.error;
|
|
return Promise.reject(new Error(errorMessage));
|
|
} else {
|
|
return Promise.reject(new Error('Protocol error with unknown response signature'));
|
|
}
|
|
})
|
|
.catch(() => {
|
|
// If not parsable, throw the initial response rather than letting
|
|
// the json failure ("unexpected token at..") pass through.
|
|
return Promise.reject(new Error(`${method}: ${response.statusText} (${response.status}, JSON)`));
|
|
});
|
|
}
|
|
|
|
export function apiCall(method: string, params: ?{}, resolve: Function, reject: Function) {
|
|
let apiRequestHeaders = Lbry.apiRequestHeaders;
|
|
|
|
if (params && params[NO_AUTH]) {
|
|
apiRequestHeaders = Object.assign({}, Lbry.apiRequestHeaders);
|
|
delete apiRequestHeaders[X_LBRY_AUTH_TOKEN];
|
|
delete params[NO_AUTH];
|
|
}
|
|
|
|
const counter = new Date().getTime();
|
|
const options = {
|
|
method: 'POST',
|
|
headers: apiRequestHeaders,
|
|
body: JSON.stringify({
|
|
jsonrpc: '2.0',
|
|
method,
|
|
params,
|
|
id: counter,
|
|
}),
|
|
};
|
|
|
|
const connectionString = Lbry.methodsUsingAlternateConnectionString.includes(method)
|
|
? Lbry.alternateConnectionString
|
|
: Lbry.daemonConnectionString;
|
|
|
|
return fetch(connectionString + '?m=' + method, options)
|
|
.then((response) => checkAndParse(response, method))
|
|
.then((response) => {
|
|
const error = response.error || (response.result && response.result.error);
|
|
return error ? reject(error) : resolve(response.result);
|
|
})
|
|
.catch(reject);
|
|
}
|
|
|
|
function daemonCallWithResult(
|
|
name: string,
|
|
params: ?{} = {},
|
|
checkAuthNeededFn: ?(?{}) => boolean = undefined
|
|
): Promise<any> {
|
|
return new Promise((resolve, reject) => {
|
|
const skipAuth = checkAuthNeededFn ? !checkAuthNeededFn(params) : false;
|
|
|
|
apiCall(
|
|
name,
|
|
skipAuth ? { ...params, [NO_AUTH]: true } : params,
|
|
(result) => {
|
|
resolve(result);
|
|
},
|
|
reject
|
|
);
|
|
});
|
|
}
|
|
|
|
// This is only for a fallback
|
|
// If there is a Lbry method that is being called by an app, it should be added to /flow-typed/Lbry.js
|
|
const lbryProxy = new Proxy(Lbry, {
|
|
get(target: LbryTypes, name: string) {
|
|
if (name in target) {
|
|
return target[name];
|
|
}
|
|
|
|
return (params = {}) =>
|
|
new Promise((resolve, reject) => {
|
|
apiCall(name, params, resolve, reject);
|
|
});
|
|
},
|
|
});
|
|
|
|
/**
|
|
* daemonCallWithResult hook that checks if the search option requires the
|
|
* auth-token. This hook works for 'resolve' and 'claim_search'.
|
|
*
|
|
* @param options
|
|
* @returns {boolean}
|
|
*/
|
|
function searchRequiresAuth(options: any) {
|
|
const KEYS_REQUIRE_AUTH = ['include_purchase_receipt', 'include_is_my_output'];
|
|
return options && KEYS_REQUIRE_AUTH.some((k) => options.hasOwnProperty(k));
|
|
}
|
|
|
|
export default lbryProxy;
|