Merge pull request #394 from lbryio/speech-as-a-package-publish-disable
Speech as a package publish disable
This commit is contained in:
commit
4b7a96c731
18 changed files with 125 additions and 85 deletions
|
@ -16,8 +16,9 @@ function SiteConfig () {
|
||||||
description: 'Open-source, decentralized image and video sharing.',
|
description: 'Open-source, decentralized image and video sharing.',
|
||||||
};
|
};
|
||||||
this.publishing = {
|
this.publishing = {
|
||||||
additionalClaimAddresses: [], // optional
|
additionalClaimAddresses: [],
|
||||||
disabled : false,
|
disabled : false,
|
||||||
|
disabledMessage : 'Please check back soon.',
|
||||||
primaryClaimAddress : 'default',
|
primaryClaimAddress : 'default',
|
||||||
thumbnailChannel : 'default',
|
thumbnailChannel : 'default',
|
||||||
thumbnailChannelId : 'default',
|
thumbnailChannelId : 'default',
|
||||||
|
@ -29,10 +30,10 @@ function SiteConfig () {
|
||||||
}
|
}
|
||||||
const {analytics, publishing, details, assetDefaults, auth} = config;
|
const {analytics, publishing, details, assetDefaults, auth} = config;
|
||||||
this.analytics = analytics;
|
this.analytics = analytics;
|
||||||
this.publishing = publishing;
|
|
||||||
this.details = details;
|
|
||||||
this.assetDefaults = assetDefaults;
|
this.assetDefaults = assetDefaults;
|
||||||
this.auth = auth;
|
this.auth = auth;
|
||||||
|
this.details = details;
|
||||||
|
this.publishing = publishing;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,9 @@ const logger = require('winston');
|
||||||
const db = require('../models');
|
const db = require('../models');
|
||||||
const lbryApi = require('../helpers/lbryApi.js');
|
const lbryApi = require('../helpers/lbryApi.js');
|
||||||
const publishHelpers = require('../helpers/publishHelpers.js');
|
const publishHelpers = require('../helpers/publishHelpers.js');
|
||||||
const { publishing: { primaryClaimAddress } } = require('../config/siteConfig.js');
|
const { publishing: { primaryClaimAddress, additionalClaimAddresses } } = require('../config/siteConfig.js');
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const Op = Sequelize.Op;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
publish (publishParams, fileName, fileType) {
|
publish (publishParams, fileName, fileType) {
|
||||||
|
@ -87,36 +89,42 @@ module.exports = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
claimNameIsAvailable (name) {
|
claimNameIsAvailable (name) {
|
||||||
|
const claimAddresses = additionalClaimAddresses || [];
|
||||||
|
claimAddresses.push(primaryClaimAddress);
|
||||||
// find any records where the name is used
|
// find any records where the name is used
|
||||||
return db.File.findAll({ where: { name } })
|
return db.Claim
|
||||||
|
.findAll({
|
||||||
|
attributes: ['address'],
|
||||||
|
where : {
|
||||||
|
name,
|
||||||
|
address: {
|
||||||
|
[Op.or]: claimAddresses,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (result.length >= 1) {
|
if (result.length >= 1) {
|
||||||
// filter out any results that were not published from spee.ch's wallet address
|
throw new Error('That claim is already in use');
|
||||||
const filteredResult = result.filter((claim) => {
|
|
||||||
return (claim.address === primaryClaimAddress);
|
|
||||||
});
|
|
||||||
// return based on whether any non-spee.ch claims were left
|
|
||||||
if (filteredResult.length >= 1) {
|
|
||||||
throw new Error('That claim is already in use');
|
|
||||||
};
|
|
||||||
return name;
|
|
||||||
};
|
};
|
||||||
return name;
|
return name;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
throw error;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
checkChannelAvailability (name) {
|
checkChannelAvailability (name) {
|
||||||
return new Promise((resolve, reject) => {
|
return db.Channel
|
||||||
// find any records where the name is used
|
.findAll({
|
||||||
db.Channel.findAll({ where: { channelName: name } })
|
where: { channelName: name },
|
||||||
.then(result => {
|
})
|
||||||
if (result.length >= 1) {
|
.then(result => {
|
||||||
return resolve(false);
|
if (result.length >= 1) {
|
||||||
}
|
throw new Error('That channel has already been claimed');
|
||||||
resolve(true);
|
}
|
||||||
})
|
return name;
|
||||||
.catch(error => {
|
})
|
||||||
reject(error);
|
.catch(error => {
|
||||||
});
|
throw error;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const { details, publishing } = require('../config/siteConfig.js');
|
const { details, publishing } = require('../config/siteConfig.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
2
index.js
2
index.js
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -348,14 +348,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
||||||
where: { name, claimId },
|
where: { name, claimId },
|
||||||
})
|
})
|
||||||
.then(claimArray => {
|
.then(claimArray => {
|
||||||
logger.debug('claims found on resolve:', claimArray.length);
|
|
||||||
switch (claimArray.length) {
|
switch (claimArray.length) {
|
||||||
case 0:
|
case 0:
|
||||||
return resolve(null);
|
return resolve(null);
|
||||||
case 1:
|
case 1:
|
||||||
return resolve(prepareClaimData(claimArray[0].dataValues));
|
return resolve(prepareClaimData(claimArray[0].dataValues));
|
||||||
default:
|
default:
|
||||||
logger.error(`more than one entry matches that name (${name}) and claimID (${claimId})`);
|
logger.error(`more than one record matches ${name}#${claimId} in db.Claim`);
|
||||||
return resolve(prepareClaimData(claimArray[0].dataValues));
|
return resolve(prepareClaimData(claimArray[0].dataValues));
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -85,11 +85,14 @@ h3, p {
|
||||||
font-size: x-large;
|
font-size: x-large;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.text--large {
|
.text--large {
|
||||||
font-size: 2rem;
|
font-size: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.text--disabled {
|
||||||
|
color: #9b9b9b;
|
||||||
|
}
|
||||||
|
|
||||||
.pull-quote {
|
.pull-quote {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
margin-top: 1rem;
|
margin-top: 1rem;
|
||||||
|
@ -165,6 +168,11 @@ a, a:visited {
|
||||||
color: #9b9b9b;
|
color: #9b9b9b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.link--disabled-text {
|
||||||
|
color: #9b9b9b;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
.link--nav {
|
.link--nav {
|
||||||
color: black;
|
color: black;
|
||||||
border-bottom: 2px solid white;
|
border-bottom: 2px solid white;
|
||||||
|
@ -504,7 +512,7 @@ table {
|
||||||
|
|
||||||
/* PUBLISH FORM */
|
/* PUBLISH FORM */
|
||||||
|
|
||||||
.dropzone {
|
.dropzone, .dropzone--disabled {
|
||||||
border: 2px dashed #9b9b9b;
|
border: 2px dashed #9b9b9b;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -20,7 +20,7 @@ class AboutPage extends React.Component {
|
||||||
</div><div className='column column--5 column--med-10 align-content-top'>
|
</div><div className='column column--5 column--med-10 align-content-top'>
|
||||||
<div className='column column--8 column--med-10'>
|
<div className='column column--8 column--med-10'>
|
||||||
<p>Spee.ch is a media-hosting site that reads from and publishes content to the <a className='link--primary' href='https://lbry.io'>LBRY</a> blockchain.</p>
|
<p>Spee.ch is a media-hosting site that reads from and publishes content to the <a className='link--primary' href='https://lbry.io'>LBRY</a> blockchain.</p>
|
||||||
<p>Spee.ch is a hosting service, but with the added benefit that it stores your content on a decentralized network of computers -- the LBRY network. This means that your images are stored in multiple locations without a single point of failure.</p>
|
<p>Spee.ch is a hosting service, but with the added benefit that it stores your content on a decentralized network of computers -- the <a className='link--primary' href='https://lbry.io/get'>LBRY</a> network. This means that your images are stored in multiple locations without a single point of failure.</p>
|
||||||
<h3>Contribute</h3>
|
<h3>Contribute</h3>
|
||||||
<p>If you have an idea for your own spee.ch-like site on top of LBRY, fork our <a className='link--primary' href='https://github.com/lbryio/spee.ch'>github repo</a> and go to town!</p>
|
<p>If you have an idea for your own spee.ch-like site on top of LBRY, fork our <a className='link--primary' href='https://github.com/lbryio/spee.ch'>github repo</a> and go to town!</p>
|
||||||
<p>If you want to improve spee.ch, join our <a className='link--primary' href='https://discord.gg/YjYbwhS'>discord channel</a> or solve one of our <a className='link--primary' href='https://github.com/lbryio/spee.ch/issues'>github issues</a>.</p>
|
<p>If you want to improve spee.ch, join our <a className='link--primary' href='https://discord.gg/YjYbwhS'>discord channel</a> or solve one of our <a className='link--primary' href='https://github.com/lbryio/spee.ch/issues'>github issues</a>.</p>
|
||||||
|
|
|
@ -38,12 +38,8 @@ class ChannelCreateForm extends React.Component {
|
||||||
updateIsChannelAvailable (channel) {
|
updateIsChannelAvailable (channel) {
|
||||||
const channelWithAtSymbol = `@${channel}`;
|
const channelWithAtSymbol = `@${channel}`;
|
||||||
request(`/api/channel/availability/${channelWithAtSymbol}`)
|
request(`/api/channel/availability/${channelWithAtSymbol}`)
|
||||||
.then(isAvailable => {
|
.then(() => {
|
||||||
if (isAvailable) {
|
this.setState({'error': null});
|
||||||
this.setState({'error': null});
|
|
||||||
} else {
|
|
||||||
this.setState({'error': 'That channel has already been claimed'});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.setState({'error': error.message});
|
this.setState({'error': error.message});
|
||||||
|
@ -51,21 +47,9 @@ class ChannelCreateForm extends React.Component {
|
||||||
}
|
}
|
||||||
checkIsChannelAvailable (channel) {
|
checkIsChannelAvailable (channel) {
|
||||||
const channelWithAtSymbol = `@${channel}`;
|
const channelWithAtSymbol = `@${channel}`;
|
||||||
return new Promise((resolve, reject) => {
|
return request(`/api/channel/availability/${channelWithAtSymbol}`);
|
||||||
request(`/api/channel/availability/${channelWithAtSymbol}`)
|
|
||||||
.then(isAvailable => {
|
|
||||||
if (!isAvailable) {
|
|
||||||
return reject(new Error('That channel has already been claimed'));
|
|
||||||
}
|
|
||||||
resolve();
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
checkIsPasswordProvided () {
|
checkIsPasswordProvided (password) {
|
||||||
const password = this.state.password;
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!password || password.length < 1) {
|
if (!password || password.length < 1) {
|
||||||
return reject(new Error('Please provide a password'));
|
return reject(new Error('Please provide a password'));
|
||||||
|
@ -94,9 +78,9 @@ class ChannelCreateForm extends React.Component {
|
||||||
}
|
}
|
||||||
createChannel (event) {
|
createChannel (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
this.checkIsPasswordProvided()
|
this.checkIsPasswordProvided(this.state.password)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
return this.checkIsChannelAvailable(this.state.channel, this.state.password);
|
return this.checkIsChannelAvailable(this.state.channel);
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setState({status: 'We are publishing your new channel. Sit tight...'});
|
this.setState({status: 'We are publishing your new channel. Sit tight...'});
|
||||||
|
@ -107,7 +91,11 @@ class ChannelCreateForm extends React.Component {
|
||||||
this.props.onChannelLogin(result.channelName, result.shortChannelId, result.channelClaimId);
|
this.props.onChannelLogin(result.channelName, result.shortChannelId, result.channelClaimId);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.setState({'error': error.message, status: null});
|
if (error.message) {
|
||||||
|
this.setState({'error': error.message, status: null});
|
||||||
|
} else {
|
||||||
|
this.setState({'error': error, status: null});
|
||||||
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
|
|
10
react/containers/PublishDisabledMessage/index.js
Normal file
10
react/containers/PublishDisabledMessage/index.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import {connect} from 'react-redux';
|
||||||
|
import View from './view';
|
||||||
|
|
||||||
|
const mapStateToProps = ({ publish }) => {
|
||||||
|
return {
|
||||||
|
message: publish.disabledMessage,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, null)(View);
|
16
react/containers/PublishDisabledMessage/view.jsx
Normal file
16
react/containers/PublishDisabledMessage/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
class PublishDisabledMessage extends React.Component {
|
||||||
|
render () {
|
||||||
|
const message = this.props.message;
|
||||||
|
console.log('this.props.message:', message);
|
||||||
|
return (
|
||||||
|
<div className='row dropzone--disabled row--tall flex-container--column flex-container--center-center'>
|
||||||
|
<p className='text--disabled'>Publishing is currently disabled.</p>
|
||||||
|
<p className='text--disabled'>{message}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PublishDisabledMessage;
|
|
@ -3,8 +3,9 @@ import View from './view';
|
||||||
|
|
||||||
const mapStateToProps = ({ publish }) => {
|
const mapStateToProps = ({ publish }) => {
|
||||||
return {
|
return {
|
||||||
file : publish.file,
|
disabled: publish.disabled,
|
||||||
status: publish.status.status,
|
file : publish.file,
|
||||||
|
status : publish.status.status,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,26 @@ import React from 'react';
|
||||||
import Dropzone from 'containers/Dropzone';
|
import Dropzone from 'containers/Dropzone';
|
||||||
import PublishDetails from 'containers/PublishDetails';
|
import PublishDetails from 'containers/PublishDetails';
|
||||||
import PublishStatus from 'containers/PublishStatus';
|
import PublishStatus from 'containers/PublishStatus';
|
||||||
|
import PublishDisabledMessage from 'containers/PublishDisabledMessage';
|
||||||
|
|
||||||
class PublishTool extends React.Component {
|
class PublishTool extends React.Component {
|
||||||
render () {
|
render () {
|
||||||
if (this.props.file) {
|
if (this.props.disabled) {
|
||||||
if (this.props.status) {
|
console.log('publish is disabled');
|
||||||
return (
|
return (
|
||||||
<PublishStatus />
|
<PublishDisabledMessage />
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
return <PublishDetails />;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
|
console.log('publish is not disabled');
|
||||||
|
if (this.props.file) {
|
||||||
|
if (this.props.status) {
|
||||||
|
return (
|
||||||
|
<PublishStatus />
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <PublishDetails />;
|
||||||
|
}
|
||||||
|
}
|
||||||
return <Dropzone />;
|
return <Dropzone />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ class ShowAssetDetails extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import * as actions from 'constants/publish_action_types';
|
import * as actions from 'constants/publish_action_types';
|
||||||
import { LOGIN } from 'constants/publish_channel_select_states';
|
import { LOGIN } from 'constants/publish_channel_select_states';
|
||||||
|
const { publishing } = require('../../config/siteConfig.js');
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
disabled : publishing.disabled,
|
||||||
|
disabledMessage : publishing.disabledMessage,
|
||||||
publishInChannel : false,
|
publishInChannel : false,
|
||||||
selectedChannel : LOGIN,
|
selectedChannel : LOGIN,
|
||||||
showMetadataInputs: false,
|
showMetadataInputs: false,
|
||||||
|
|
|
@ -16,14 +16,12 @@ const NO_CLAIM = 'NO_CLAIM';
|
||||||
|
|
||||||
module.exports = (app) => {
|
module.exports = (app) => {
|
||||||
// route to check whether site has published to a channel
|
// route to check whether site has published to a channel
|
||||||
app.get('/api/channel/availability/:name', ({ ip, originalUrl, params }, res) => {
|
app.get('/api/channel/availability/:name', ({ ip, originalUrl, params: { name } }, res) => {
|
||||||
checkChannelAvailability(params.name)
|
const gaStartTime = Date.now();
|
||||||
.then(result => {
|
checkChannelAvailability(name)
|
||||||
if (result === true) {
|
.then(availableName => {
|
||||||
res.status(200).json(true);
|
res.status(200).json(availableName);
|
||||||
} else {
|
sendGATimingEvent('end-to-end', 'claim name availability', name, gaStartTime, Date.now());
|
||||||
res.status(200).json(false);
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
errorHandlers.handleErrorResponse(originalUrl, ip, error, res);
|
errorHandlers.handleErrorResponse(originalUrl, ip, error, res);
|
||||||
|
@ -107,10 +105,12 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// route to check whether this site published to a claim
|
// route to check whether this site published to a claim
|
||||||
app.get('/api/claim/availability/:name', ({ ip, originalUrl, params }, res) => {
|
app.get('/api/claim/availability/:name', ({ ip, originalUrl, params: { name } }, res) => {
|
||||||
claimNameIsAvailable(params.name)
|
const gaStartTime = Date.now();
|
||||||
|
claimNameIsAvailable(name)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
res.status(200).json(result);
|
res.status(200).json(result);
|
||||||
|
sendGATimingEvent('end-to-end', 'claim name availability', name, gaStartTime, Date.now());
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
errorHandlers.handleErrorResponse(originalUrl, ip, error, res);
|
errorHandlers.handleErrorResponse(originalUrl, ip, error, res);
|
||||||
|
@ -128,8 +128,6 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
// route to run a publish request on the daemon
|
// route to run a publish request on the daemon
|
||||||
app.post('/api/claim/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl, user }, res) => {
|
app.post('/api/claim/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl, user }, res) => {
|
||||||
logger.debug('api/claim/publish req.body:', body);
|
|
||||||
logger.debug('api/claim/publish req.files:', files);
|
|
||||||
// define variables
|
// define variables
|
||||||
let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title;
|
let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title;
|
||||||
// record the start time of the request
|
// record the start time of the request
|
||||||
|
|
Loading…
Reference in a new issue