React/Redux - publish component #323
18 changed files with 291 additions and 196 deletions
|
@ -55,4 +55,4 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
|
|||
* `channelPassword` (optional,; required if `channelName` is provided)
|
||||
|
||||
## bugs
|
||||
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack!
|
||||
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry discord!
|
||||
|
|
|
@ -31,4 +31,12 @@ module.exports = {
|
|||
defaultThumbnail : 'https://spee.ch/assets/img/video_thumb_default.png',
|
||||
defaultDescription: 'Open-source, decentralized image and video sharing.',
|
||||
},
|
||||
testing: {
|
||||
testChannel : '@testpublishchannel', // a channel to make test publishes in
|
||||
testChannelPassword: 'password', // password for the test channel
|
||||
},
|
||||
api: {
|
||||
apiHost: 'localhost',
|
||||
apiPort: '5279',
|
||||
},
|
||||
};
|
||||
|
|
4
constants/index.js
Normal file
4
constants/index.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
PUBLISH_ANONYMOUS_CLAIM : 'PUBLISH_ANONYMOUS_CLAIM',
|
||||
PUBLISH_IN_CHANNEL_CLAIM: 'PUBLISH_IN_CHANNEL_CLAIM',
|
||||
};
|
|
@ -18,7 +18,7 @@ module.exports = {
|
|||
logger.debug(`this claim was published in channel: ${publishParams.channel_name}`);
|
||||
return db.Channel.findOne({where: {channelName: publishParams.channel_name}});
|
||||
} else {
|
||||
logger.debug('this claim was published in channel: n/a');
|
||||
logger.debug('this claim was not published in a channel');
|
||||
return null;
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,74 +1,7 @@
|
|||
const logger = require('winston');
|
||||
const ua = require('universal-analytics');
|
||||
const config = require('../config/speechConfig.js');
|
||||
const db = require('../models');
|
||||
const googleApiKey = config.analytics.googleId;
|
||||
|
||||
module.exports = {
|
||||
postToStats (action, url, ipAddress, name, claimId, result) {
|
||||
logger.debug('action:', action);
|
||||
// make sure the result is a string
|
||||
if (result && (typeof result !== 'string')) {
|
||||
result = result.toString();
|
||||
}
|
||||
// make sure the ip address(es) are a string
|
||||
if (ipAddress && (typeof ipAddress !== 'string')) {
|
||||
ipAddress = ipAddress.toString();
|
||||
}
|
||||
db.File
|
||||
.findOne({where: { name, claimId }})
|
||||
.then(file => {
|
||||
// create record in the db
|
||||
let FileId;
|
||||
if (file) {
|
||||
FileId = file.dataValues.id;
|
||||
} else {
|
||||
FileId = null;
|
||||
}
|
||||
return db.Request
|
||||
.create({
|
||||
action,
|
||||
url,
|
||||
ipAddress,
|
||||
result,
|
||||
FileId,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Sequelize error >>', error);
|
||||
});
|
||||
},
|
||||
sendGoogleAnalytics (action, headers, ip, originalUrl) {
|
||||
const visitorId = ip.replace(/\./g, '-');
|
||||
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
|
||||
let params;
|
||||
switch (action) {
|
||||
case 'SERVE':
|
||||
params = {
|
||||
ec : 'serve',
|
||||
ea : originalUrl,
|
||||
uip: ip,
|
||||
ua : headers['user-agent'],
|
||||
ul : headers['accept-language'],
|
||||
};
|
||||
break;
|
||||
case 'PUBLISH':
|
||||
params = {
|
||||
ec : 'publish',
|
||||
ea : originalUrl,
|
||||
uip: ip,
|
||||
ua : headers['user-agent'],
|
||||
ul : headers['accept-language'],
|
||||
};
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
visitor.event(params, (err) => {
|
||||
if (err) {
|
||||
logger.error('Google Analytics Event Error >>', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
getTrendingClaims (startDate) {
|
||||
logger.debug('retrieving trending');
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
|
@ -7,7 +7,7 @@ module.exports = {
|
|||
if (error.code === 'ECONNREFUSED') {
|
||||
status = 503;
|
||||
message = 'Connection refused. The daemon may not be running.';
|
||||
// check for errors from the deamon
|
||||
// check for errors from the daemon
|
||||
} else if (error.response) {
|
||||
status = error.response.status || 500;
|
||||
if (error.response.data) {
|
||||
|
|
|
@ -1,5 +1,62 @@
|
|||
const Handlebars = require('handlebars');
|
||||
const { site, analytics } = require('../config/speechConfig.js');
|
||||
const { site, analytics, claim: claimDefaults } = require('../config/speechConfig.js');
|
||||
|
||||
function determineOgTitle (storedTitle, defaultTitle) {
|
||||
return ifEmptyReturnOther(storedTitle, defaultTitle);
|
||||
};
|
||||
|
||||
function determineOgDescription (storedDescription, defaultDescription) {
|
||||
const length = 200;
|
||||
let description = ifEmptyReturnOther(storedDescription, defaultDescription);
|
||||
if (description.length >= length) {
|
||||
description = `${description.substring(0, length)}...`;
|
||||
};
|
||||
return description;
|
||||
};
|
||||
|
||||
function ifEmptyReturnOther (value, replacement) {
|
||||
if (value === '') {
|
||||
return replacement;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function determineContentTypeFromFileExtension (fileExtension) {
|
||||
switch (fileExtension) {
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
return 'image/jpeg';
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
default:
|
||||
return 'image/jpeg';
|
||||
}
|
||||
};
|
||||
|
||||
function determineOgThumbnailContentType (thumbnail) {
|
||||
if (thumbnail) {
|
||||
if (thumbnail.lastIndexOf('.') !== -1) {
|
||||
return determineContentTypeFromFileExtension(thumbnail.substring(thumbnail.lastIndexOf('.')));
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function createOpenGraphDataFromClaim (claim, defaultTitle, defaultDescription) {
|
||||
let openGraphData = {};
|
||||
openGraphData['embedUrl'] = `${site.host}/${claim.claimId}/${claim.name}`;
|
||||
openGraphData['showUrl'] = `${site.host}/${claim.claimId}/${claim.name}`;
|
||||
openGraphData['source'] = `${site.host}/${claim.claimId}/${claim.name}.${claim.fileExt}`;
|
||||
openGraphData['directFileUrl'] = `${site.host}/${claim.claimId}/${claim.name}.${claim.fileExt}`;
|
||||
openGraphData['ogTitle'] = determineOgTitle(claim.title, defaultTitle);
|
||||
openGraphData['ogDescription'] = determineOgDescription(claim.description, defaultDescription);
|
||||
openGraphData['ogThumbnailContentType'] = determineOgThumbnailContentType(claim.thumbnail);
|
||||
return openGraphData;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
placeCommonHeaderTags () {
|
||||
|
@ -16,7 +73,10 @@ module.exports = {
|
|||
ga('send', 'pageview');</script>`;
|
||||
return new Handlebars.SafeString(gaCode);
|
||||
},
|
||||
addOpenGraph ({ ogTitle, contentType, ogDescription, thumbnail, showUrl, source, ogThumbnailContentType }) {
|
||||
addOpenGraph (claim) {
|
||||
const { ogTitle, ogDescription, showUrl, source, ogThumbnailContentType } = createOpenGraphDataFromClaim(claim, claimDefaults.defaultTitle, claimDefaults.defaultDescription);
|
||||
const thumbnail = claim.thumbnail;
|
||||
const contentType = claim.contentType;
|
||||
const ogTitleTag = `<meta property="og:title" content="${ogTitle}" />`;
|
||||
const ogUrlTag = `<meta property="og:url" content="${showUrl}" />`;
|
||||
const ogSiteNameTag = `<meta property="og:site_name" content="${site.title}" />`;
|
||||
|
@ -42,8 +102,10 @@ module.exports = {
|
|||
return new Handlebars.SafeString(`${basicTags} ${ogImageTag} ${ogImageTypeTag} ${ogTypeTag}`);
|
||||
}
|
||||
},
|
||||
addTwitterCard ({ contentType, source, embedUrl, directFileUrl }) {
|
||||
addTwitterCard (claim) {
|
||||
const { embedUrl, directFileUrl } = createOpenGraphDataFromClaim(claim, claimDefaults.defaultTitle, claimDefaults.defaultDescription);
|
||||
const basicTwitterTags = `<meta name="twitter:site" content="@spee_ch" >`;
|
||||
const contentType = claim.contentType;
|
||||
if (contentType === 'video/mp4') {
|
||||
const twitterName = '<meta name="twitter:card" content="player" >';
|
||||
const twitterPlayer = `<meta name="twitter:player" content="${embedUrl}" >`;
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
const axios = require('axios');
|
||||
const logger = require('winston');
|
||||
const config = require('../config/speechConfig.js');
|
||||
const { apiHost, apiPort } = config.api;
|
||||
const lbryApiUri = 'http://' + apiHost + ':' + apiPort;
|
||||
|
||||
function handleLbrynetResponse ({ data }, resolve, reject) {
|
||||
logger.debug('lbry api data:', data);
|
||||
|
@ -22,7 +25,7 @@ module.exports = {
|
|||
logger.debug(`lbryApi >> Publishing claim to "${publishParams.name}"`);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'publish',
|
||||
params: publishParams,
|
||||
})
|
||||
|
@ -38,7 +41,7 @@ module.exports = {
|
|||
logger.debug(`lbryApi >> Getting Claim for "${uri}"`);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'get',
|
||||
params: { uri, timeout: 20 },
|
||||
})
|
||||
|
@ -54,7 +57,7 @@ module.exports = {
|
|||
logger.debug(`lbryApi >> Getting claim_list for "${claimName}"`);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'claim_list',
|
||||
params: { name: claimName },
|
||||
})
|
||||
|
@ -71,7 +74,7 @@ module.exports = {
|
|||
// console.log('resolving uri', uri);
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'resolve',
|
||||
params: { uri },
|
||||
})
|
||||
|
@ -91,7 +94,7 @@ module.exports = {
|
|||
logger.debug('lbryApi >> Retrieving the download directory path from lbry daemon...');
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'settings_get',
|
||||
})
|
||||
.then(({ data }) => {
|
||||
|
@ -110,7 +113,7 @@ module.exports = {
|
|||
createChannel (name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post('http://localhost:5279/lbryapi', {
|
||||
.post(lbryApiUri, {
|
||||
method: 'channel_new',
|
||||
params: {
|
||||
channel_name: name,
|
||||
|
|
|
@ -17,6 +17,9 @@ module.exports = {
|
|||
logger.debug(`${proto}, ${value}, ${modifierSeperator}, ${modifier}`);
|
||||
|
||||
// Validate and process name
|
||||
if (!value) {
|
||||
throw new Error(`Check your url. No channel name provided before "${modifierSeperator}"`);
|
||||
}
|
||||
const isChannel = value.startsWith(module.exports.CHANNEL_CHAR);
|
||||
const channelName = isChannel ? value : null;
|
||||
let claimId;
|
||||
|
@ -36,13 +39,13 @@ module.exports = {
|
|||
let channelClaimId;
|
||||
if (modifierSeperator) {
|
||||
if (!modifier) {
|
||||
throw new Error(`No modifier provided after separator ${modifierSeperator}.`);
|
||||
throw new Error(`No modifier provided after separator "${modifierSeperator}"`);
|
||||
}
|
||||
|
||||
if (modifierSeperator === ':') {
|
||||
channelClaimId = modifier;
|
||||
} else {
|
||||
throw new Error(`The ${modifierSeperator} modifier is not currently supported.`);
|
||||
throw new Error(`The "${modifierSeperator}" modifier is not currently supported`);
|
||||
}
|
||||
}
|
||||
return {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
const constants = require('../constants');
|
||||
const logger = require('winston');
|
||||
const fs = require('fs');
|
||||
const { site, wallet } = require('../config/speechConfig.js');
|
||||
|
@ -158,5 +159,29 @@ module.exports = {
|
|||
logger.debug(`successfully deleted ${filePath}`);
|
||||
});
|
||||
},
|
||||
|
||||
addGetResultsToFileData (fileInfo, getResult) {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
return fileInfo;
|
||||
},
|
||||
createFileData ({ name, claimId, outpoint, height, address, nsfw, contentType }) {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
height,
|
||||
address,
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
fileType: contentType,
|
||||
nsfw,
|
||||
};
|
||||
},
|
||||
returnPublishTimingActionType (channelName) {
|
||||
if (channelName) {
|
||||
return constants.PUBLISH_IN_CHANNEL_CLAIM;
|
||||
} else {
|
||||
return constants.PUBLISH_ANONYMOUS_CLAIM;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
95
helpers/statsHelpers.js
Normal file
95
helpers/statsHelpers.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
const constants = require('../constants');
|
||||
const logger = require('winston');
|
||||
const ua = require('universal-analytics');
|
||||
const config = require('../config/speechConfig.js');
|
||||
const googleApiKey = config.analytics.googleId;
|
||||
const db = require('../models');
|
||||
|
||||
module.exports = {
|
||||
createPublishTimingEventParams (publishDurration, ip, headers, label) {
|
||||
return {
|
||||
userTimingCategory : 'lbrynet',
|
||||
userTimingVariableName: 'publish',
|
||||
userTimingTime : publishDurration,
|
||||
userTimingLabel : label,
|
||||
uip : ip,
|
||||
ua : headers['user-agent'],
|
||||
ul : headers['accept-language'],
|
||||
};
|
||||
},
|
||||
postToStats (action, url, ipAddress, name, claimId, result) {
|
||||
logger.debug('action:', action);
|
||||
// make sure the result is a string
|
||||
if (result && (typeof result !== 'string')) {
|
||||
result = result.toString();
|
||||
}
|
||||
// make sure the ip address(es) are a string
|
||||
if (ipAddress && (typeof ipAddress !== 'string')) {
|
||||
ipAddress = ipAddress.toString();
|
||||
}
|
||||
db.File
|
||||
.findOne({where: { name, claimId }})
|
||||
.then(file => {
|
||||
// create record in the db
|
||||
let FileId;
|
||||
if (file) {
|
||||
FileId = file.dataValues.id;
|
||||
} else {
|
||||
FileId = null;
|
||||
}
|
||||
return db.Request
|
||||
.create({
|
||||
action,
|
||||
url,
|
||||
ipAddress,
|
||||
result,
|
||||
FileId,
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Sequelize error >>', error);
|
||||
});
|
||||
},
|
||||
sendGoogleAnalyticsEvent (action, headers, ip, originalUrl) {
|
||||
const visitorId = ip.replace(/\./g, '-');
|
||||
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
|
||||
let params;
|
||||
switch (action) {
|
||||
case 'SERVE':
|
||||
params = {
|
||||
ec : 'serve',
|
||||
ea : originalUrl,
|
||||
uip: ip,
|
||||
ua : headers['user-agent'],
|
||||
ul : headers['accept-language'],
|
||||
};
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
visitor.event(params, (err) => {
|
||||
if (err) {
|
||||
logger.error('Google Analytics Event Error >>', err);
|
||||
}
|
||||
});
|
||||
},
|
||||
sendGoogleAnalyticsTiming (action, headers, ip, originalUrl, startTime, endTime) {
|
||||
const visitorId = ip.replace(/\./g, '-');
|
||||
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
|
||||
const durration = endTime - startTime;
|
||||
let params;
|
||||
switch (action) {
|
||||
case constants.PUBLISH_ANONYMOUS_CLAIM:
|
||||
case constants.PUBLISH_IN_CHANNEL_CLAIM:
|
||||
logger.verbose(`${action} completed successfully in ${durration}ms`);
|
||||
params = module.exports.createPublishTimingEventParams(durration, ip, headers, action);
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
visitor.timing(params, (err) => {
|
||||
if (err) {
|
||||
logger.error('Google Analytics Event Error >>', err);
|
||||
}
|
||||
logger.debug(`${action} timing event successfully sent to google analytics`);
|
||||
});
|
||||
},
|
||||
};
|
|
@ -1,8 +1,7 @@
|
|||
const logger = require('winston');
|
||||
const { returnShortId } = require('../helpers/sequelizeHelpers.js');
|
||||
const { claim, site } = require('../config/speechConfig.js');
|
||||
const { defaultTitle, defaultThumbnail, defaultDescription } = claim;
|
||||
const { host } = site;
|
||||
const { defaultThumbnail } = claim;
|
||||
|
||||
function determineFileExtensionFromContentType (contentType) {
|
||||
switch (contentType) {
|
||||
|
@ -21,68 +20,18 @@ function determineFileExtensionFromContentType (contentType) {
|
|||
}
|
||||
};
|
||||
|
||||
function determineContentTypeFromFileExtension (fileExtension) {
|
||||
switch (fileExtension) {
|
||||
case 'jpeg':
|
||||
case 'jpg':
|
||||
return 'image/jpeg';
|
||||
case 'png':
|
||||
return 'image/png';
|
||||
case 'gif':
|
||||
return 'image/gif';
|
||||
case 'mp4':
|
||||
return 'video/mp4';
|
||||
default:
|
||||
logger.debug('setting unknown file type as type image/jpeg');
|
||||
return 'image/jpeg';
|
||||
}
|
||||
};
|
||||
|
||||
function ifEmptyReturnOther (value, replacement) {
|
||||
if (value === '') {
|
||||
return replacement;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function determineThumbnail (storedThumbnail, defaultThumbnail) {
|
||||
return ifEmptyReturnOther(storedThumbnail, defaultThumbnail);
|
||||
};
|
||||
|
||||
function determineOgTitle (storedTitle, defaultTitle) {
|
||||
return ifEmptyReturnOther(storedTitle, defaultTitle);
|
||||
};
|
||||
|
||||
function determineOgDescription (storedDescription, defaultDescription) {
|
||||
return ifEmptyReturnOther(storedDescription, defaultDescription);
|
||||
};
|
||||
|
||||
function determineOgThumbnailContentType (thumbnail) {
|
||||
if (thumbnail) {
|
||||
if (thumbnail.lastIndexOf('.') !== -1) {
|
||||
return determineContentTypeFromFileExtension(thumbnail.substring(thumbnail.lastIndexOf('.')));
|
||||
if (storedThumbnail === '') {
|
||||
return defaultThumbnail;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function addOpengraphDataToClaim (claim) {
|
||||
claim['host'] = host;
|
||||
claim['embedUrl'] = `${host}/${claim.claimId}/${claim.name}`;
|
||||
claim['showUrl'] = `${host}/${claim.claimId}/${claim.name}`;
|
||||
claim['source'] = `${host}/${claim.claimId}/${claim.name}.${claim.fileExt}`;
|
||||
claim['directFileUrl'] = `${host}/${claim.claimId}/${claim.name}.${claim.fileExt}`;
|
||||
claim['ogTitle'] = determineOgTitle(claim.title, defaultTitle);
|
||||
claim['ogDescription'] = determineOgDescription(claim.description, defaultDescription);
|
||||
claim['ogThumbnailContentType'] = determineOgThumbnailContentType(claim.thumbnail);
|
||||
return claim;
|
||||
return storedThumbnail;
|
||||
};
|
||||
|
||||
function prepareClaimData (claim) {
|
||||
// logger.debug('preparing claim data based on resolved data:', claim);
|
||||
claim['thumbnail'] = determineThumbnail(claim.thumbnail, defaultThumbnail);
|
||||
claim['fileExt'] = determineFileExtensionFromContentType(claim.contentType);
|
||||
claim = addOpengraphDataToClaim(claim);
|
||||
claim['host'] = site.host;
|
||||
return claim;
|
||||
};
|
||||
|
||||
|
@ -307,7 +256,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
case 1:
|
||||
return resolve(result[0].claimId);
|
||||
default:
|
||||
logger.error(`${result.length} records found for ${claimName} from channel ${claimName}`);
|
||||
logger.error(`${result.length} records found for "${claimName}" in channel "${channelClaimId}"`);
|
||||
return resolve(result[0].claimId);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -40,13 +40,11 @@ module.exports = new PassportLocalStrategy(
|
|||
})
|
||||
.then(([newUser, newChannel, newCertificate]) => {
|
||||
logger.verbose('user and certificate successfully created');
|
||||
logger.debug('user result >', newUser.dataValues);
|
||||
// store the relevant newUser info to be passed back for req.User
|
||||
userInfo['id'] = newUser.id;
|
||||
userInfo['userName'] = newUser.userName;
|
||||
logger.verbose('channel result >', newChannel.dataValues);
|
||||
userInfo['channelName'] = newChannel.channelName;
|
||||
userInfo['channelClaimId'] = newChannel.channelClaimId;
|
||||
logger.verbose('certificate result >', newCertificate.dataValues);
|
||||
// associate the instances
|
||||
return Promise.all([newCertificate.setChannel(newChannel), newChannel.setUser(newUser)]);
|
||||
})
|
||||
|
|
|
@ -5,30 +5,11 @@ const multipartMiddleware = multipart({uploadDir: files.uploadDirectory});
|
|||
const db = require('../models');
|
||||
const { checkClaimNameAvailability, checkChannelAvailability, publish } = require('../controllers/publishController.js');
|
||||
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
||||
const { createPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles, parsePublishApiChannel } = require('../helpers/publishHelpers.js');
|
||||
const { createPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles, parsePublishApiChannel, addGetResultsToFileData, createFileData, returnPublishTimingActionType } = require('../helpers/publishHelpers.js');
|
||||
const errorHandlers = require('../helpers/errorHandlers.js');
|
||||
const { sendGoogleAnalyticsTiming } = require('../helpers/statsHelpers.js');
|
||||
const { authenticateIfNoUserToken } = require('../auth/authentication.js');
|
||||
|
||||
function addGetResultsToFileData (fileInfo, getResult) {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
function createFileData ({ name, claimId, outpoint, height, address, nsfw, contentType }) {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
height,
|
||||
address,
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
fileType: contentType,
|
||||
nsfw,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = (app) => {
|
||||
// route to run a claim_list request on the daemon
|
||||
app.get('/api/claim-list/:name', ({ ip, originalUrl, params }, res) => {
|
||||
|
@ -122,9 +103,14 @@ module.exports = (app) => {
|
|||
});
|
||||
});
|
||||
// route to run a publish request on the daemon
|
||||
app.post('/api/claim-publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => {
|
||||
app.post('/api/claim-publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl, user }, res) => {
|
||||
logger.debug('api/claim-publish body:', body);
|
||||
logger.debug('api/claim-publish files:', files);
|
||||
// record the start time of the request and create variable for storing the action type
|
||||
const publishStartTime = Date.now();
|
||||
logger.debug('publish request started @', publishStartTime);
|
||||
let timingActionType;
|
||||
// define variables
|
||||
let name, fileName, filePath, fileType, nsfw, license, title, description, thumbnail, channelName, channelPassword;
|
||||
// validate the body and files of the request
|
||||
try {
|
||||
|
@ -153,7 +139,8 @@ module.exports = (app) => {
|
|||
return createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName);
|
||||
})
|
||||
.then(publishParams => {
|
||||
logger.debug('publishParams:', publishParams);
|
||||
// set the timing event type for reporting
|
||||
timingActionType = returnPublishTimingActionType(publishParams.channel_name);
|
||||
// publish the asset
|
||||
return publish(publishParams, fileName, fileType);
|
||||
})
|
||||
|
@ -166,6 +153,10 @@ module.exports = (app) => {
|
|||
lbryTx: result,
|
||||
},
|
||||
});
|
||||
// log the publish end time
|
||||
const publishEndTime = Date.now();
|
||||
logger.debug('publish request completed @', publishEndTime);
|
||||
sendGoogleAnalyticsTiming(timingActionType, headers, ip, originalUrl, publishStartTime, publishEndTime);
|
||||
})
|
||||
.catch(error => {
|
||||
errorHandlers.handleApiError(originalUrl, ip, error, res);
|
||||
|
|
|
@ -2,7 +2,7 @@ const logger = require('winston');
|
|||
const { getClaimId, getChannelViewData, getLocalFileRecord } = require('../controllers/serveController.js');
|
||||
const serveHelpers = require('../helpers/serveHelpers.js');
|
||||
const { handleRequestError } = require('../helpers/errorHandlers.js');
|
||||
const { postToStats } = require('../controllers/statsController.js');
|
||||
const { postToStats } = require('../helpers/statsHelpers.js');
|
||||
const db = require('../models');
|
||||
const lbryUri = require('../helpers/lbryUri.js');
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
const chai = require('chai');
|
||||
const expect = chai.expect;
|
||||
const chaiHttp = require('chai-http');
|
||||
const { host } = require('../../config/speechConfig.js').site;
|
||||
const { site, testing } = require('../../config/speechConfig.js');
|
||||
const { host } = site;
|
||||
const { testChannel, testChannelPassword } = testing;
|
||||
const requestTimeout = 20000;
|
||||
const publishTimeout = 120000;
|
||||
const fs = require('fs');
|
||||
|
@ -82,15 +84,17 @@ describe('end-to-end', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('publish', function () {
|
||||
describe('publish requests', function () {
|
||||
const publishUrl = '/api/claim-publish';
|
||||
const date = new Date();
|
||||
const name = `test-publish-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getTime()}`;
|
||||
const filePath = './test/mock-data/bird.jpeg';
|
||||
const fileName = 'byrd.jpeg';
|
||||
const channelName = testChannel;
|
||||
const channelPassword = testChannelPassword;
|
||||
|
||||
describe(publishUrl, function () {
|
||||
describe('anonymous publishes', function () {
|
||||
it(`should receive a status code 200 within ${publishTimeout}ms @usesLbc`, function (done) {
|
||||
const date = new Date();
|
||||
const name = `test-publish-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getTime()}`;
|
||||
chai.request(host)
|
||||
.post(publishUrl)
|
||||
.type('form')
|
||||
|
@ -104,6 +108,25 @@ describe('end-to-end', function () {
|
|||
}).timeout(publishTimeout);
|
||||
});
|
||||
|
||||
describe('in-channel publishes', function () {
|
||||
it(`should receive a status code 200 within ${publishTimeout}ms @usesLbc`, function (done) {
|
||||
const date = new Date();
|
||||
const name = `test-publish-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getTime()}`;
|
||||
chai.request(host)
|
||||
.post(publishUrl)
|
||||
.type('form')
|
||||
.attach('file', fs.readFileSync(filePath), fileName)
|
||||
.field('name', name)
|
||||
.field('channelName', channelName)
|
||||
.field('channelPassword', channelPassword)
|
||||
.end(function (err, res) {
|
||||
// expect(err).to.be.null;
|
||||
expect(res).to.have.status(200);
|
||||
done();
|
||||
});
|
||||
}).timeout(publishTimeout);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
|
4
views/partials/maintenanceBanner.handlebars
Normal file
4
views/partials/maintenanceBanner.handlebars
Normal file
|
@ -0,0 +1,4 @@
|
|||
<div id="new-release-banner" class="row row--short row--wide">
|
||||
<p style="font-size: medium"> Hi there! Spee.ch is currently undergoing maintenance, and as a result publishing may be disabled. Please visit our <a style="
|
||||
color:white; text-decoration: underline" target="_blank" href="https://discord.gg/YjYbwhS">discord channel</a> for updates.</p>
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<div id="new-release-banner" class="row row--short row--wide">
|
||||
Hi there! You've stumbled upon the new version of Spee<h, launching soon! Send us your feedback in <a style="color:white; text-decoration: underline" target="_blank" href="https://discord.gg/YjYbwhS">our discord</a>
|
||||
</div>
|
Loading…
Reference in a new issue