Un-authenticated resolve
(#341)
* apiCall: add option to not send the auth header ## Why Want an option to make un-authenticated `resolve` calls where appropriate, to improve caching. ## How All `apiCall`s are authenticated by default, but when clients add NO_AUTH to the params, `apiCall` will exclude the X_LBRY_AUTH_TOKEN. It will also strip NO_AUTH from the param object before sending it out. * Add hook for 'resolve' and 'claim_search' to check and skip auth... ... if the params does not contain anything that requires the wallet. * doResolveUri, doClaimSearch: let clients decide when to include_my_output - No more hardcoding 'include_purchase_receipt' and 'include_is_my_output' - doResolveUri: include these params when opening a file page. This was the only place that was doing that prior to this PR. * is_my_output: use the signing_channel as alternative ## Notes `is_my_output` is more expensive to resolve, so it is not being requested all the time. ## Change Looking at the signing channel as the additional fallback, on top of `myClaimIds`. ## Aside I think using `myClaimIds` here is redundant, as it is usually populated from `is_my_ouput`. But leaving as is for now...
This commit is contained in:
parent
c74dd49bc5
commit
4267c1ccf7
10 changed files with 104 additions and 46 deletions
|
@ -42,6 +42,7 @@ function CommentMenuList(props: Props) {
|
|||
const {
|
||||
uri,
|
||||
claim,
|
||||
claimIsMine,
|
||||
authorUri,
|
||||
commentIsMine,
|
||||
commentId,
|
||||
|
@ -104,7 +105,7 @@ function CommentMenuList(props: Props) {
|
|||
|
||||
function getBlockOptionElem() {
|
||||
const isPersonalBlockTheOnlyOption = !activeChannelIsModerator && !activeChannelIsAdmin;
|
||||
const isTimeoutBlockAvailable = (claim && claim.is_my_output) || activeChannelIsModerator;
|
||||
const isTimeoutBlockAvailable = claimIsMine || activeChannelIsModerator;
|
||||
const personalPermanentBlockOnly = isPersonalBlockTheOnlyOption && !isTimeoutBlockAvailable;
|
||||
|
||||
function getSubtitle() {
|
||||
|
|
|
@ -1 +1,5 @@
|
|||
export const X_LBRY_AUTH_TOKEN = 'X-Lbry-Auth-Token';
|
||||
|
||||
// Additional parameter for apiCall() to skip sending the auth token.
|
||||
// NO_AUTH will be stripped from the parameter object before sending out.
|
||||
export const NO_AUTH = 'no_auth';
|
||||
|
|
38
ui/lbry.js
38
ui/lbry.js
|
@ -1,4 +1,6 @@
|
|||
// @flow
|
||||
import { NO_AUTH, X_LBRY_AUTH_TOKEN } from 'constants/token';
|
||||
|
||||
require('proxy-polyfill');
|
||||
|
||||
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
|
||||
|
@ -75,9 +77,9 @@ const Lbry = {
|
|||
version: () => daemonCallWithResult('version', {}),
|
||||
|
||||
// Claim fetching and manipulation
|
||||
resolve: (params) => daemonCallWithResult('resolve', params),
|
||||
resolve: (params) => daemonCallWithResult('resolve', params, searchRequiresAuth),
|
||||
get: (params) => daemonCallWithResult('get', params),
|
||||
claim_search: (params) => daemonCallWithResult('claim_search', 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),
|
||||
|
@ -192,10 +194,18 @@ function checkAndParse(response) {
|
|||
}
|
||||
|
||||
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: Lbry.apiRequestHeaders,
|
||||
headers: apiRequestHeaders,
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
|
@ -220,11 +230,17 @@ export function apiCall(method: string, params: ?{}, resolve: Function, reject:
|
|||
.catch(reject);
|
||||
}
|
||||
|
||||
function daemonCallWithResult(name: string, params: ?{} = {}): Promise<any> {
|
||||
function daemonCallWithResult(
|
||||
name: string,
|
||||
params: ?{} = {},
|
||||
checkAuthNeededFn: ?(?{}) => boolean = undefined
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const skipAuth = checkAuthNeededFn ? !checkAuthNeededFn(params) : false;
|
||||
|
||||
apiCall(
|
||||
name,
|
||||
params,
|
||||
skipAuth ? { ...params, [NO_AUTH]: true } : params,
|
||||
(result) => {
|
||||
resolve(result);
|
||||
},
|
||||
|
@ -248,4 +264,16 @@ const lbryProxy = new Proxy(Lbry, {
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
import { selectClaimForUri, selectClaimIsMine } from 'redux/selectors/claims';
|
||||
import { doHideModal } from 'redux/actions/app';
|
||||
import { doCommentModBlock, doCommentModBlockAsAdmin, doCommentModBlockAsModerator } from 'redux/actions/comments';
|
||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||
|
@ -7,11 +7,15 @@ import { selectModerationDelegatorsById } from 'redux/selectors/comments';
|
|||
|
||||
import ModalBlockChannel from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
activeChannelClaim: selectActiveChannelClaim(state),
|
||||
contentClaim: makeSelectClaimForUri(props.contentUri)(state),
|
||||
moderationDelegatorsById: selectModerationDelegatorsById(state),
|
||||
});
|
||||
const select = (state, props) => {
|
||||
const contentClaim = selectClaimForUri(state, props.contentUri);
|
||||
return {
|
||||
activeChannelClaim: selectActiveChannelClaim(state),
|
||||
contentClaim,
|
||||
contentClaimIsMine: selectClaimIsMine(state, contentClaim),
|
||||
moderationDelegatorsById: selectModerationDelegatorsById(state),
|
||||
};
|
||||
};
|
||||
|
||||
const perform = {
|
||||
doHideModal,
|
||||
|
|
|
@ -31,6 +31,7 @@ type Props = {
|
|||
// --- redux ---
|
||||
activeChannelClaim: ?ChannelClaim,
|
||||
contentClaim: ?Claim,
|
||||
contentClaimIsMine: ?boolean,
|
||||
moderationDelegatorsById: { [string]: { global: boolean, delegators: { name: string, claimId: string } } },
|
||||
doHideModal: () => void,
|
||||
doCommentModBlock: (commenterUri: string, offendingCommentId: ?string, timeoutSec: ?number) => void,
|
||||
|
@ -55,6 +56,7 @@ export default function ModalBlockChannel(props: Props) {
|
|||
offendingCommentId,
|
||||
activeChannelClaim,
|
||||
contentClaim,
|
||||
contentClaimIsMine,
|
||||
moderationDelegatorsById,
|
||||
doHideModal,
|
||||
doCommentModBlock,
|
||||
|
@ -78,7 +80,7 @@ export default function ModalBlockChannel(props: Props) {
|
|||
const [timeoutSec, setTimeoutSec] = React.useState(-1);
|
||||
|
||||
const isPersonalTheOnlyTab = !activeChannelIsModerator && !activeChannelIsAdmin;
|
||||
const isTimeoutAvail = (contentClaim && contentClaim.is_my_output) || activeChannelIsModerator;
|
||||
const isTimeoutAvail = contentClaimIsMine || activeChannelIsModerator;
|
||||
const blockButtonDisabled = blockType === BLOCK.TIMEOUT && timeoutSec < 1;
|
||||
|
||||
// **************************************************************************
|
||||
|
|
|
@ -88,7 +88,8 @@ const select = (state, props) => {
|
|||
};
|
||||
|
||||
const perform = (dispatch) => ({
|
||||
resolveUri: (uri) => dispatch(doResolveUri(uri)),
|
||||
resolveUri: (uri, returnCached, resolveRepost, options) =>
|
||||
dispatch(doResolveUri(uri, returnCached, resolveRepost, options)),
|
||||
beginPublish: (name) => {
|
||||
dispatch(doClearPublish());
|
||||
dispatch(doPrepareEdit({ name }));
|
||||
|
|
|
@ -23,7 +23,7 @@ const isDev = process.env.NODE_ENV !== 'production';
|
|||
|
||||
type Props = {
|
||||
isResolvingUri: boolean,
|
||||
resolveUri: (string) => void,
|
||||
resolveUri: (string, boolean, boolean, any) => void,
|
||||
isSubscribed: boolean,
|
||||
uri: string,
|
||||
claim: StreamClaim,
|
||||
|
@ -107,7 +107,12 @@ function ShowPage(props: Props) {
|
|||
(resolveUri && !isResolvingUri && uri && haventFetchedYet) ||
|
||||
(claimExists && !claimIsPending && (!canonicalUrl || isMine === undefined))
|
||||
) {
|
||||
resolveUri(uri);
|
||||
resolveUri(
|
||||
uri,
|
||||
false,
|
||||
true,
|
||||
isMine === undefined ? { include_is_my_output: true, include_purchase_receipt: true } : {}
|
||||
);
|
||||
}
|
||||
}, [resolveUri, isResolvingUri, canonicalUrl, uri, claimExists, haventFetchedYet, isMine, claimIsPending, search]);
|
||||
|
||||
|
|
|
@ -27,7 +27,8 @@ let checkPendingInterval;
|
|||
export function doResolveUris(
|
||||
uris: Array<string>,
|
||||
returnCachedClaims: boolean = false,
|
||||
resolveReposts: boolean = true
|
||||
resolveReposts: boolean = true,
|
||||
additionalOptions: any = {}
|
||||
) {
|
||||
return (dispatch: Dispatch, getState: GetState) => {
|
||||
const normalizedUris = uris.map(normalizeURI);
|
||||
|
@ -47,13 +48,6 @@ export function doResolveUris(
|
|||
return;
|
||||
}
|
||||
|
||||
const options: { include_is_my_output?: boolean, include_purchase_receipt: boolean } = {
|
||||
include_purchase_receipt: true,
|
||||
};
|
||||
|
||||
if (urisToResolve.length === 1) {
|
||||
options.include_is_my_output = true;
|
||||
}
|
||||
dispatch({
|
||||
type: ACTIONS.RESOLVE_URIS_STARTED,
|
||||
data: { uris: normalizedUris },
|
||||
|
@ -70,7 +64,7 @@ export function doResolveUris(
|
|||
|
||||
const collectionIds: Array<string> = [];
|
||||
|
||||
return Lbry.resolve({ urls: urisToResolve, ...options }).then(async (result: ResolveResponse) => {
|
||||
return Lbry.resolve({ urls: urisToResolve, ...additionalOptions }).then(async (result: ResolveResponse) => {
|
||||
let repostedResults = {};
|
||||
const repostsToResolve = [];
|
||||
const fallbackResolveInfo = {
|
||||
|
@ -127,7 +121,7 @@ export function doResolveUris(
|
|||
type: ACTIONS.RESOLVE_URIS_STARTED,
|
||||
data: { uris: repostsToResolve, debug: 'reposts' },
|
||||
});
|
||||
repostedResults = await Lbry.resolve({ urls: repostsToResolve, ...options });
|
||||
repostedResults = await Lbry.resolve({ urls: repostsToResolve, ...additionalOptions });
|
||||
}
|
||||
processResult(repostedResults, resolveInfo);
|
||||
|
||||
|
@ -145,8 +139,13 @@ export function doResolveUris(
|
|||
};
|
||||
}
|
||||
|
||||
export function doResolveUri(uri: string) {
|
||||
return doResolveUris([uri]);
|
||||
export function doResolveUri(
|
||||
uri: string,
|
||||
returnCachedClaims: boolean = false,
|
||||
resolveReposts: boolean = true,
|
||||
additionalOptions: any = {}
|
||||
) {
|
||||
return doResolveUris([uri], returnCachedClaims, resolveReposts, additionalOptions);
|
||||
}
|
||||
|
||||
export function doFetchClaimListMine(
|
||||
|
@ -660,10 +659,7 @@ export function doClaimSearch(
|
|||
return false;
|
||||
};
|
||||
|
||||
return await Lbry.claim_search({
|
||||
...options,
|
||||
include_purchase_receipt: true,
|
||||
}).then(success, failure);
|
||||
return await Lbry.claim_search(options).then(success, failure);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import { normalizeURI, parseURI, isURIValid } from 'util/lbryURI';
|
|||
import { selectSupportsByOutpoint } from 'redux/selectors/wallet';
|
||||
import { createSelector } from 'reselect';
|
||||
import { createCachedSelector } from 're-reselect';
|
||||
import { isClaimNsfw, filterClaims } from 'util/claim';
|
||||
import { isClaimNsfw, filterClaims, getChannelIdFromClaim } from 'util/claim';
|
||||
import * as CLAIM from 'constants/claim';
|
||||
|
||||
type State = { claims: any };
|
||||
|
@ -216,20 +216,26 @@ const selectNormalizedAndVerifiedUri = createCachedSelector(
|
|||
|
||||
export const selectClaimIsMine = (state: State, claim: ?Claim) => {
|
||||
if (claim) {
|
||||
// The original code seems to imply that 'is_my_output' could be false even
|
||||
// when it is yours and there is a need to double-check with 'myActiveClaims'.
|
||||
// I'm retaining that logic. Otherwise, we could have just return
|
||||
// is_my_output directly when it is defined and skip the fallback.
|
||||
if (claim.is_my_output) {
|
||||
return true;
|
||||
} else {
|
||||
// 'is_my_output' is false or undefined.
|
||||
const myActiveClaims = selectMyActiveClaims(state);
|
||||
return claim.claim_id && myActiveClaims.has(claim.claim_id);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
|
||||
const signingChannelId = getChannelIdFromClaim(claim);
|
||||
const myChannelIds = selectMyChannelClaimIds(state);
|
||||
|
||||
if (signingChannelId && myChannelIds) {
|
||||
if (myChannelIds.includes(signingChannelId)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
const myActiveClaims = selectMyActiveClaims(state);
|
||||
if (claim.claim_id && myActiveClaims.has(claim.claim_id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const selectClaimIsMineForUri = (state: State, rawUri: string) => {
|
||||
|
|
|
@ -4,7 +4,12 @@ import { createCachedSelector } from 're-reselect';
|
|||
import { selectMutedChannels } from 'redux/selectors/blocked';
|
||||
import { selectShowMatureContent } from 'redux/selectors/settings';
|
||||
import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc';
|
||||
import { selectClaimsById, selectMyClaimIdsRaw, selectClaimIdForUri } from 'redux/selectors/claims';
|
||||
import {
|
||||
selectClaimsById,
|
||||
selectMyClaimIdsRaw,
|
||||
selectMyChannelClaimIds,
|
||||
selectClaimIdForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
import { isClaimNsfw } from 'util/claim';
|
||||
|
||||
type State = { claims: any, comments: CommentsState };
|
||||
|
@ -181,6 +186,7 @@ export const selectCommentIdsForUri = (state: State, uri: string) => {
|
|||
const filterCommentsDepOnList = {
|
||||
claimsById: selectClaimsById,
|
||||
myClaimIds: selectMyClaimIdsRaw,
|
||||
myChannelClaimIds: selectMyChannelClaimIds,
|
||||
mutedChannels: selectMutedChannels,
|
||||
personalBlockList: selectModerationBlockList,
|
||||
blacklistedMap: selectBlacklistedOutpointMap,
|
||||
|
@ -264,6 +270,7 @@ const filterComments = (comments: Array<Comment>, claimId?: string, filterInputs
|
|||
const {
|
||||
claimsById,
|
||||
myClaimIds,
|
||||
myChannelClaimIds,
|
||||
mutedChannels,
|
||||
personalBlockList,
|
||||
blacklistedMap,
|
||||
|
@ -282,8 +289,12 @@ const filterComments = (comments: Array<Comment>, claimId?: string, filterInputs
|
|||
|
||||
// Return comment if `channelClaim` doesn't exist so the component knows to resolve the author
|
||||
if (channelClaim) {
|
||||
if (myClaimIds && myClaimIds.size > 0) {
|
||||
const claimIsMine = channelClaim.is_my_output || myClaimIds.includes(channelClaim.claim_id);
|
||||
if ((myClaimIds && myClaimIds.size > 0) || (myChannelClaimIds && myChannelClaimIds.length > 0)) {
|
||||
const claimIsMine =
|
||||
channelClaim.is_my_output ||
|
||||
myChannelClaimIds.includes(channelClaim.claim_id) ||
|
||||
myClaimIds.includes(channelClaim.claim_id);
|
||||
// TODO: I believe 'myClaimIds' does not include channels, so it seems wasteful to include it here? ^
|
||||
if (claimIsMine) {
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue