diff --git a/config/speechConfig.js.example b/config/speechConfig.js.example index 1b59b86c..fe0d5e76 100644 --- a/config/speechConfig.js.example +++ b/config/speechConfig.js.example @@ -1,7 +1,4 @@ module.exports = { - wallet: { - lbryClaimAddress: null, // choose an address from your lbry wallet - }, analytics: { googleId: null, // google id for analytics tracking; leave `null` if not applicable }, @@ -29,8 +26,10 @@ module.exports = { description: 'Open-source, decentralized image and video sharing.', }, publish: { - thumbnailChannel : '@channelName', // create a channel to use for thumbnail images - thumbnailChannelId: 'xyz123...', // the channel_id (claim id) for the channel above + primaryClaimAddress : null, // choose any address from your lbry wallet + additionalClaimAddresses: [], // // optional: add previously used claim addresses + thumbnailChannel : '@channelName', // create a channel to use for thumbnail images + thumbnailChannelId : 'xyz123...', // the channel_id (claim id) for the channel above }, claim: { defaultTitle : 'Spee.ch', diff --git a/controllers/publishController.js b/controllers/publishController.js index 18fa640e..8733a147 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -2,7 +2,9 @@ const logger = require('winston'); const db = require('../models'); const lbryApi = require('../helpers/lbryApi.js'); const publishHelpers = require('../helpers/publishHelpers.js'); -const config = require('../config/speechConfig.js'); +const { publish : { primaryClaimAddress, additionalClaimAddresses } } = require('../config/speechConfig.js'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; module.exports = { publish (publishParams, fileName, fileType) { @@ -87,37 +89,42 @@ module.exports = { }); }, claimNameIsAvailable (name) { + const claimAddresses = additionalClaimAddresses || []; + claimAddresses.push(primaryClaimAddress); // 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 => { if (result.length >= 1) { - const claimAddress = config.wallet.lbryClaimAddress; - // filter out any results that were not published from spee.ch's wallet address - const filteredResult = result.filter((claim) => { - return (claim.address === claimAddress); - }); - // 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; + throw new Error('That claim is already in use'); }; return name; + }) + .catch(error => { + throw error; }); }, checkChannelAvailability (name) { - return new Promise((resolve, reject) => { - // find any records where the name is used - db.Channel.findAll({ where: { channelName: name } }) - .then(result => { - if (result.length >= 1) { - return resolve(false); - } - resolve(true); - }) - .catch(error => { - reject(error); - }); - }); + return db.Channel + .findAll({ + where: { channelName: name }, + }) + .then(result => { + if (result.length >= 1) { + throw new Error('That channel has already been claimed'); + } + return name; + }) + .catch(error => { + throw error; + }); }, }; diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 5e515834..1b894eea 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -1,6 +1,6 @@ const logger = require('winston'); const fs = require('fs'); -const { site, wallet, publish } = require('../config/speechConfig.js'); +const { site, publish } = require('../config/speechConfig.js'); module.exports = { parsePublishApiRequestBody ({name, nsfw, license, title, description, thumbnail}) { @@ -114,7 +114,7 @@ module.exports = { license, nsfw, }, - claim_address: wallet.lbryClaimAddress, + claim_address: publish.primaryClaimAddress, }; // add thumbnail to channel if video if (thumbnail) { @@ -140,7 +140,7 @@ module.exports = { license, nsfw, }, - claim_address: wallet.lbryClaimAddress, + claim_address: publish.primaryClaimAddress, channel_name : publish.thumbnailChannel, channel_id : publish.thumbnailChannelId, }; diff --git a/models/claim.js b/models/claim.js index 0641ab65..35d8c4bd 100644 --- a/models/claim.js +++ b/models/claim.js @@ -349,14 +349,13 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => { where: { name, claimId }, }) .then(claimArray => { - logger.debug('claims found on resolve:', claimArray.length); switch (claimArray.length) { case 0: return resolve(null); case 1: return resolve(prepareClaimData(claimArray[0].dataValues)); 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)); } }) diff --git a/models/index.js b/models/index.js index 1e6b6352..370f43ee 100644 --- a/models/index.js +++ b/models/index.js @@ -1,7 +1,4 @@ -// const fs = require('fs'); -// const path = require('path'); const Sequelize = require('sequelize'); -// const basename = path.basename(module.filename); const logger = require('winston'); const config = require('../config/speechConfig.js'); const { database, username, password } = config.sql; diff --git a/react/containers/ChannelCreateForm/view.jsx b/react/containers/ChannelCreateForm/view.jsx index 269a08eb..44336daa 100644 --- a/react/containers/ChannelCreateForm/view.jsx +++ b/react/containers/ChannelCreateForm/view.jsx @@ -38,12 +38,8 @@ class ChannelCreateForm extends React.Component { updateIsChannelAvailable (channel) { const channelWithAtSymbol = `@${channel}`; request(`/api/channel/availability/${channelWithAtSymbol}`) - .then(isAvailable => { - if (isAvailable) { - this.setState({'error': null}); - } else { - this.setState({'error': 'That channel has already been claimed'}); - } + .then(() => { + this.setState({'error': null}); }) .catch((error) => { this.setState({'error': error.message}); @@ -51,21 +47,9 @@ class ChannelCreateForm extends React.Component { } checkIsChannelAvailable (channel) { const channelWithAtSymbol = `@${channel}`; - return new Promise((resolve, reject) => { - request(`/api/channel/availability/${channelWithAtSymbol}`) - .then(isAvailable => { - if (!isAvailable) { - return reject(new Error('That channel has already been claimed')); - } - resolve(); - }) - .catch((error) => { - reject(error); - }); - }); + return request(`/api/channel/availability/${channelWithAtSymbol}`); } - checkIsPasswordProvided () { - const password = this.state.password; + checkIsPasswordProvided (password) { return new Promise((resolve, reject) => { if (!password || password.length < 1) { return reject(new Error('Please provide a password')); @@ -94,9 +78,9 @@ class ChannelCreateForm extends React.Component { } createChannel (event) { event.preventDefault(); - this.checkIsPasswordProvided() + this.checkIsPasswordProvided(this.state.password) .then(() => { - return this.checkIsChannelAvailable(this.state.channel, this.state.password); + return this.checkIsChannelAvailable(this.state.channel); }) .then(() => { 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); }) .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 () { diff --git a/routes/api-routes.js b/routes/api-routes.js index e65d0a1c..4f75ecb3 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -16,14 +16,12 @@ const NO_CLAIM = 'NO_CLAIM'; module.exports = (app) => { // route to check whether site has published to a channel - app.get('/api/channel/availability/:name', ({ ip, originalUrl, params }, res) => { - checkChannelAvailability(params.name) - .then(result => { - if (result === true) { - res.status(200).json(true); - } else { - res.status(200).json(false); - } + app.get('/api/channel/availability/:name', ({ ip, originalUrl, params: { name } }, res) => { + const gaStartTime = Date.now(); + checkChannelAvailability(name) + .then(availableName => { + res.status(200).json(availableName); + sendGATimingEvent('end-to-end', 'claim name availability', name, gaStartTime, Date.now()); }) .catch(error => { errorHandlers.handleErrorResponse(originalUrl, ip, error, res); @@ -107,10 +105,12 @@ module.exports = (app) => { }); }); // route to check whether this site published to a claim - app.get('/api/claim/availability/:name', ({ ip, originalUrl, params }, res) => { - claimNameIsAvailable(params.name) + app.get('/api/claim/availability/:name', ({ ip, originalUrl, params: { name } }, res) => { + const gaStartTime = Date.now(); + claimNameIsAvailable(name) .then(result => { res.status(200).json(result); + sendGATimingEvent('end-to-end', 'claim name availability', name, gaStartTime, Date.now()); }) .catch(error => { errorHandlers.handleErrorResponse(originalUrl, ip, error, res); @@ -128,8 +128,6 @@ module.exports = (app) => { }); // route to run a publish request on the daemon 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 let channelName, channelId, channelPassword, description, fileName, filePath, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title; // record the start time of the request