commit
fd86b7ce49
23 changed files with 194 additions and 60 deletions
|
@ -157,7 +157,7 @@ inquirer
|
|||
method: 'channel_new',
|
||||
params: {
|
||||
channel_name: thumbnailChannelDefault,
|
||||
amount : 0.1,
|
||||
amount : '0.1',
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
|
|
|
@ -26,7 +26,11 @@
|
|||
"thumbnailChannelId": null,
|
||||
"additionalClaimAddresses": [],
|
||||
"disabled": false,
|
||||
"disabledMessage": "Default publishing disabled message"
|
||||
"disabledMessage": "Default publishing disabled message",
|
||||
"closedRegistration": false,
|
||||
"serveOnlyApproved": false,
|
||||
"publishOnlyApproved": false,
|
||||
"approvedChannels": []
|
||||
},
|
||||
"startup": {
|
||||
"performChecks": true,
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import React from 'react';
|
||||
import ChannelLoginForm from '@containers/ChannelLoginForm';
|
||||
import ChannelCreateForm from '@containers/ChannelCreateForm';
|
||||
import Row from '@components/Row';
|
||||
|
||||
const ChannelTools = () => {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<h3>Log in to an existing channel:</h3>
|
||||
<ChannelLoginForm />
|
||||
</Row>
|
||||
<Row>
|
||||
<h3>Create a brand new channel:</h3>
|
||||
<ChannelCreateForm />
|
||||
</Row>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChannelTools;
|
|
@ -1,13 +1,18 @@
|
|||
import {connect} from 'react-redux';
|
||||
import {setPublishInChannel, updateSelectedChannel, updateError} from '../../actions/publish';
|
||||
// import isApprovedChannel from '../../../../utils/isApprovedChannel';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ channel, publish }) => {
|
||||
const mapStateToProps = ({ publish, site, channel: { loggedInChannel: { name, shortId, longId } } }) => {
|
||||
return {
|
||||
loggedInChannelName: channel.loggedInChannel.name,
|
||||
// isApprovedChannel : isApprovedChannel({ longId }, site.approvedChannels),
|
||||
publishOnlyApproved: site.publishOnlyApproved,
|
||||
// closedRegistration : site.closedRegistration,
|
||||
loggedInChannelName: name,
|
||||
publishInChannel : publish.publishInChannel,
|
||||
selectedChannel : publish.selectedChannel,
|
||||
channelError : publish.error.channel,
|
||||
longId,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -16,9 +16,12 @@ class ChannelSelect extends React.Component {
|
|||
this.handleSelection = this.handleSelection.bind(this);
|
||||
}
|
||||
componentWillMount () {
|
||||
const { loggedInChannelName } = this.props;
|
||||
const { loggedInChannelName, onChannelSelect, publishOnlyApproved, onPublishInChannelChange } = this.props;
|
||||
if (loggedInChannelName) {
|
||||
this.props.onChannelSelect(loggedInChannelName);
|
||||
onChannelSelect(loggedInChannelName);
|
||||
}
|
||||
if (publishOnlyApproved) {
|
||||
onPublishInChannelChange(true);
|
||||
}
|
||||
}
|
||||
toggleAnonymousPublish (event) {
|
||||
|
@ -34,7 +37,17 @@ class ChannelSelect extends React.Component {
|
|||
this.props.onChannelSelect(selectedOption);
|
||||
}
|
||||
render () {
|
||||
const { publishInChannel, channelError, selectedChannel, loggedInChannelName } = this.props;
|
||||
const { publishInChannel, channelError, selectedChannel, loggedInChannelName, publishOnlyApproved } = this.props;
|
||||
if (publishOnlyApproved) {
|
||||
return (
|
||||
<div>
|
||||
<RowLabeled
|
||||
label={<Label value={'Channel:'} />}
|
||||
content={<span>{loggedInChannelName}</span>}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<RowLabeled
|
||||
|
|
10
client/src/containers/ChannelTools/index.js
Normal file
10
client/src/containers/ChannelTools/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ site: { closedRegistration } }) => {
|
||||
return {
|
||||
closedRegistration,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(View);
|
23
client/src/containers/ChannelTools/view.jsx
Normal file
23
client/src/containers/ChannelTools/view.jsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
import React from 'react';
|
||||
import ChannelLoginForm from '@containers/ChannelLoginForm';
|
||||
import ChannelCreateForm from '@containers/ChannelCreateForm';
|
||||
import Row from '@components/Row';
|
||||
|
||||
class ChannelTools extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<h3>Log in to an existing channel:</h3>
|
||||
<ChannelLoginForm />
|
||||
</Row>
|
||||
{!this.props.closedRegistration && (<Row>
|
||||
<h3>Create a brand new channel:</h3>
|
||||
<ChannelCreateForm />
|
||||
</Row>)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChannelTools;
|
|
@ -1,12 +1,15 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { logOutChannel, checkForLoggedInChannel } from '../../actions/channel';
|
||||
import isApprovedChannel from '../../../../utils/isApprovedChannel';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ channel: { loggedInChannel: { name, shortId, longId } } }) => {
|
||||
const mapStateToProps = ({ site, channel: { loggedInChannel: { name, shortId, longId } } }) => {
|
||||
return {
|
||||
channelName : name,
|
||||
channelShortId: shortId,
|
||||
channelLongId : longId,
|
||||
showPublish : (!site.publishOnlyApproved || isApprovedChannel({ longId }, site.approvedChannels)),
|
||||
closedRegistration: site.closedRegistration,
|
||||
channelName : name,
|
||||
channelShortId : shortId,
|
||||
channelLongId : longId,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -28,16 +28,17 @@ class NavigationLinks extends React.Component {
|
|||
}
|
||||
}
|
||||
render () {
|
||||
const { channelName, showPublish, closedRegistration } = this.props;
|
||||
return (
|
||||
<div className='navigation-links'>
|
||||
<NavLink
|
||||
{showPublish && <NavLink
|
||||
className='nav-bar-link link--nav'
|
||||
activeClassName='link--nav-active'
|
||||
to='/'
|
||||
exact
|
||||
>
|
||||
Publish
|
||||
</NavLink>
|
||||
</NavLink>}
|
||||
<NavLink
|
||||
className='nav-bar-link link--nav'
|
||||
activeClassName='link--nav-active'
|
||||
|
@ -45,7 +46,7 @@ class NavigationLinks extends React.Component {
|
|||
>
|
||||
About
|
||||
</NavLink>
|
||||
{ this.props.channelName ? (
|
||||
{ channelName ? (
|
||||
<NavBarChannelOptionsDropdown
|
||||
channelName={this.props.channelName}
|
||||
handleSelection={this.handleSelection}
|
||||
|
@ -53,7 +54,7 @@ class NavigationLinks extends React.Component {
|
|||
VIEW={VIEW}
|
||||
LOGOUT={LOGOUT}
|
||||
/>
|
||||
) : (
|
||||
) : !closedRegistration && (
|
||||
<NavLink
|
||||
id='nav-bar-login-link'
|
||||
className='nav-bar-link link--nav'
|
||||
|
|
|
@ -3,12 +3,14 @@ import ErrorPage from '@pages/ErrorPage';
|
|||
import ShowAssetLite from '@pages/ShowAssetLite';
|
||||
import ShowAssetDetails from '@pages/ShowAssetDetails';
|
||||
import ShowChannel from '@pages/ShowChannel';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
|
||||
import { CHANNEL, ASSET_LITE, ASSET_DETAILS } from '../../constants/show_request_types';
|
||||
|
||||
class ContentPageWrapper extends React.Component {
|
||||
componentDidMount () {
|
||||
this.props.onHandleShowPageUri(this.props.match.params);
|
||||
const { onHandleShowPageUri, match, homeChannel } = this.props;
|
||||
onHandleShowPageUri(homeChannel ? { claim: homeChannel } : match.params);
|
||||
}
|
||||
componentWillReceiveProps (nextProps) {
|
||||
if (nextProps.match.params !== this.props.match.params) {
|
||||
|
@ -35,4 +37,4 @@ class ContentPageWrapper extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
export default ContentPageWrapper;
|
||||
export default withRouter(ContentPageWrapper);
|
||||
|
|
|
@ -2,10 +2,11 @@ import { connect } from 'react-redux';
|
|||
import { onHandleShowPageUri } from '../../actions/show';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ show }) => {
|
||||
const mapStateToProps = ({ show, site, channel }) => {
|
||||
return {
|
||||
error : show.request.error,
|
||||
requestType: show.request.type,
|
||||
homeChannel: site.publishOnlyApproved && !channel.loggedInChannel.name ? `${site.approvedChannels[0].name}:${site.approvedChannels[0].longId}` : null,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React from 'react';
|
||||
import PageLayout from '@components/PageLayout';
|
||||
|
||||
import PublishTool from '@containers/PublishTool';
|
||||
import ContentPageWrapper from '@pages/ContentPageWrapper';
|
||||
|
||||
class HomePage extends React.Component {
|
||||
render () {
|
||||
return (
|
||||
const { homeChannel } = this.props;
|
||||
return homeChannel ? (
|
||||
<ContentPageWrapper homeChannel={homeChannel} />
|
||||
) : (
|
||||
<PageLayout
|
||||
pageTitle={'Speech'}
|
||||
pageUri={''}
|
||||
|
|
|
@ -4,7 +4,7 @@ import PageLayout from '@components/PageLayout';
|
|||
import HorizontalSplit from '@components/HorizontalSplit';
|
||||
|
||||
import ChannelAbout from '@components/ChannelAbout';
|
||||
import ChannelTools from '@components/ChannelTools';
|
||||
import ChannelTools from '@containers/ChannelTools';
|
||||
|
||||
class LoginPage extends React.Component {
|
||||
componentWillReceiveProps (newProps) {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import siteConfig from '@config/siteConfig.json';
|
||||
|
||||
let initialState = {
|
||||
description : 'default description',
|
||||
googleAnalyticsId : 'default google id',
|
||||
host : 'default host',
|
||||
title : 'default title',
|
||||
twitter : 'default twitter',
|
||||
defaultDescription: 'default description',
|
||||
defaultThumbnail : 'default thumbnail',
|
||||
description : 'default description',
|
||||
googleAnalyticsId : 'default google id',
|
||||
host : 'default host',
|
||||
title : 'default title',
|
||||
twitter : 'default twitter',
|
||||
defaultDescription : 'default description',
|
||||
defaultThumbnail : 'default thumbnail',
|
||||
closedRegistration : false,
|
||||
serveOnlyApproved : false,
|
||||
publishOnlyApproved: false,
|
||||
approvedChannels : [],
|
||||
|
||||
};
|
||||
|
||||
if (siteConfig) {
|
||||
|
@ -25,6 +30,12 @@ if (siteConfig) {
|
|||
title,
|
||||
twitter,
|
||||
},
|
||||
publishing: {
|
||||
closedRegistration,
|
||||
serveOnlyApproved,
|
||||
publishOnlyApproved,
|
||||
approvedChannels,
|
||||
},
|
||||
} = siteConfig;
|
||||
|
||||
initialState = {
|
||||
|
@ -35,6 +46,10 @@ if (siteConfig) {
|
|||
twitter,
|
||||
defaultDescription,
|
||||
defaultThumbnail,
|
||||
closedRegistration,
|
||||
serveOnlyApproved,
|
||||
publishOnlyApproved,
|
||||
approvedChannels,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
|
||||
const getChannelData = require('./getChannelData.js');
|
||||
const isApprovedChannel = require('../../../../../utils/isApprovedChannel');
|
||||
const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
|
||||
const NO_CHANNEL = 'NO_CHANNEL';
|
||||
const LONG_ID = 'longId';
|
||||
const SHORT_ID = 'shortId';
|
||||
const LONG_CLAIM_LENGTH = 40;
|
||||
|
||||
/*
|
||||
|
||||
|
@ -14,6 +18,16 @@ const channelData = ({ ip, originalUrl, body, params }, res) => {
|
|||
const channelName = params.channelName;
|
||||
let channelClaimId = params.channelClaimId;
|
||||
if (channelClaimId === 'none') channelClaimId = null;
|
||||
const chanObj = {};
|
||||
if (channelName) chanObj.name = channelName;
|
||||
if (channelClaimId) chanObj[(channelClaimId.length === LONG_CLAIM_LENGTH ? LONG_ID : SHORT_ID)] = channelClaimId;
|
||||
if (serveOnlyApproved && !isApprovedChannel(chanObj, approvedChannels)) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'This content is unavailable',
|
||||
});
|
||||
}
|
||||
|
||||
getChannelData(channelName, channelClaimId)
|
||||
.then(data => {
|
||||
res.status(200).json({
|
||||
|
|
|
@ -18,7 +18,7 @@ const createPublishParams = (filePath, name, title, description, license, nsfw,
|
|||
const publishParams = {
|
||||
name,
|
||||
file_path: filePath,
|
||||
bid : 0.01,
|
||||
bid : '0.01',
|
||||
metadata : {
|
||||
description,
|
||||
title,
|
||||
|
|
|
@ -10,7 +10,7 @@ const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, nsf
|
|||
return {
|
||||
name : `${claimName}-thumb`,
|
||||
file_path: thumbnailFilePath,
|
||||
bid : 0.01,
|
||||
bid : '0.01',
|
||||
metadata : {
|
||||
title : `${claimName} thumbnail`,
|
||||
description: `a thumbnail for ${claimName}`,
|
||||
|
|
|
@ -3,6 +3,8 @@ const logger = require('winston');
|
|||
const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig');
|
||||
|
||||
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
|
||||
const isApprovedChannel = require('../../../../../utils/isApprovedChannel');
|
||||
const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
|
||||
|
@ -16,6 +18,7 @@ const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
|
|||
const authenticateUser = require('./authentication.js');
|
||||
|
||||
const CLAIM_TAKEN = 'CLAIM_TAKEN';
|
||||
const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL';
|
||||
|
||||
/*
|
||||
|
||||
|
@ -54,6 +57,13 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
// check channel authorization
|
||||
authenticateUser(channelName, channelId, channelPassword, user)
|
||||
.then(({ channelName, channelClaimId }) => {
|
||||
if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId }, approvedChannels)) {
|
||||
const error = {
|
||||
name : UNAPPROVED_CHANNEL,
|
||||
message: 'This spee.ch instance only allows publishing to approved channels',
|
||||
};
|
||||
throw error;
|
||||
}
|
||||
return Promise.all([
|
||||
checkClaimAvailability(name),
|
||||
createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId),
|
||||
|
@ -92,7 +102,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res)
|
|||
sendGATimingEvent('end-to-end', 'publish', fileType, gaStartTime, Date.now());
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.name === CLAIM_TAKEN) {
|
||||
if ([CLAIM_TAKEN, UNAPPROVED_CHANNEL].includes(error.name)) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: error.message,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const logger = require('winston');
|
||||
|
||||
const db = require('../../../models');
|
||||
const isApprovedChannel = require('../../../../utils/isApprovedChannel');
|
||||
|
||||
const getClaimId = require('../../utils/getClaimId.js');
|
||||
const { handleErrorResponse } = require('../../utils/errorHandlers.js');
|
||||
|
@ -11,17 +12,28 @@ const NO_CHANNEL = 'NO_CHANNEL';
|
|||
const NO_CLAIM = 'NO_CLAIM';
|
||||
const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
|
||||
const NO_FILE = 'NO_FILE';
|
||||
const CONTENT_UNAVAILABLE = 'CONTENT_UNAVAILABLE';
|
||||
|
||||
const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
|
||||
const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => {
|
||||
getClaimId(channelName, channelClaimId, claimName, claimId)
|
||||
.then(fullClaimId => {
|
||||
claimId = fullClaimId;
|
||||
logger.debug('Full claim id:', fullClaimId);
|
||||
return db.Claim.getOutpoint(claimName, fullClaimId);
|
||||
return db.Claim.findOne({
|
||||
where: {
|
||||
name : claimName,
|
||||
claimId: fullClaimId,
|
||||
},
|
||||
});
|
||||
})
|
||||
.then(outpoint => {
|
||||
logger.debug('Outpoint:', outpoint);
|
||||
return db.Blocked.isNotBlocked(outpoint);
|
||||
.then(claim => {
|
||||
if (serveOnlyApproved && !isApprovedChannel({ longId: claim.dataValues.certificateId }, approvedChannels)) {
|
||||
throw new Error(CONTENT_UNAVAILABLE);
|
||||
}
|
||||
logger.debug('Outpoint:', claim.dataValues.outpoint);
|
||||
return db.Blocked.isNotBlocked(claim.dataValues.outpoint);
|
||||
})
|
||||
.then(() => {
|
||||
return db.File.findOne({
|
||||
|
@ -52,6 +64,13 @@ const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId
|
|||
message: 'No matching channel id could be found for that url',
|
||||
});
|
||||
}
|
||||
if (error === CONTENT_UNAVAILABLE) {
|
||||
logger.debug('unapproved channel');
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'This content is unavailable',
|
||||
});
|
||||
}
|
||||
if (error === BLOCKED_CLAIM) {
|
||||
logger.debug('claim was blocked');
|
||||
return res.status(451).json({
|
||||
|
|
|
@ -116,7 +116,7 @@ module.exports = {
|
|||
method: 'channel_new',
|
||||
params: {
|
||||
channel_name: name,
|
||||
amount : 0.1,
|
||||
amount : '0.1',
|
||||
},
|
||||
})
|
||||
.then(response => {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
const logger = require('winston');
|
||||
const returnShortId = require('./utils/returnShortId.js');
|
||||
const isApprovedChannel = require('../../utils/isApprovedChannel');
|
||||
const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('@config/siteConfig');
|
||||
const { publishing: { serveOnlyApproved, approvedChannels } } = require('@config/siteConfig');
|
||||
|
||||
const NO_CLAIM = 'NO_CLAIM';
|
||||
|
||||
|
@ -354,7 +356,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
}
|
||||
};
|
||||
|
||||
Claim.resolveClaim = function (name, claimId) {
|
||||
Claim.fetchClaim = function (name, claimId) {
|
||||
logger.debug(`Claim.resolveClaim: ${name} ${claimId}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
|
@ -378,6 +380,23 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
});
|
||||
};
|
||||
|
||||
Claim.resolveClaim = function (name, claimId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this
|
||||
.fetchClaim(name, claimId)
|
||||
.then(claim => {
|
||||
logger.info('resolveClaim claims:', claim);
|
||||
if (serveOnlyApproved && !isApprovedChannel({ longId: claim.certificateId }, approvedChannels)) {
|
||||
throw new Error('This content is unavailable');
|
||||
}
|
||||
return resolve(claim);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Claim.getOutpoint = function (name, claimId) {
|
||||
logger.debug(`finding outpoint for ${name}#${claimId}`);
|
||||
return this
|
||||
|
|
|
@ -2,6 +2,7 @@ const PassportLocalStrategy = require('passport-local').Strategy;
|
|||
const { createChannel } = require('../../lbrynet');
|
||||
const logger = require('winston');
|
||||
const db = require('../../models');
|
||||
const { publishing: { closedRegistration } } = require('@config/siteConfig');
|
||||
|
||||
module.exports = new PassportLocalStrategy(
|
||||
{
|
||||
|
@ -9,10 +10,13 @@ module.exports = new PassportLocalStrategy(
|
|||
passwordField: 'password',
|
||||
},
|
||||
(username, password, done) => {
|
||||
if (closedRegistration) {
|
||||
return done('Registration is disabled');
|
||||
}
|
||||
|
||||
logger.verbose(`new channel signup request. user: ${username} pass: ${password} .`);
|
||||
let userInfo = {};
|
||||
// server-side validaton of inputs (username, password)
|
||||
|
||||
// create the channel and retrieve the metadata
|
||||
return createChannel(`@${username}`)
|
||||
.then(tx => {
|
||||
|
|
9
utils/isApprovedChannel.js
Normal file
9
utils/isApprovedChannel.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
function isApprovedChannel (channel, channels) {
|
||||
const { name, shortId: short, longId: long } = channel;
|
||||
return Boolean(
|
||||
(long && channels.find(chan => chan.longId === long)) ||
|
||||
(name && short && channels.find(chan => chan.name === name && chan.shortId === short))
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = isApprovedChannel;
|
Loading…
Reference in a new issue