Merge pull request #428 from lbryio/412-blocked_content

412 blocked content
This commit is contained in:
Bill Bittner 2018-04-30 16:07:58 -07:00 committed by GitHub
commit 832e18072b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
80 changed files with 1121 additions and 958 deletions

View file

@ -55,7 +55,7 @@ function Server () {
app.use(requestLogger);
// configure passport
const speechPassport = require('./server/speechPassport/index');
const speechPassport = require('./server/speechPassport');
// initialize passport
const sessionKey = siteConfig.auth.sessionKey;
app.use(cookieSession({

View file

@ -1,74 +0,0 @@
const logger = require('winston');
const db = require('../models');
module.exports = {
authenticateUser (channelName, channelId, channelPassword, user) {
// case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided
if (!channelName && !channelId) {
return {
channelName : null,
channelClaimId: null,
};
}
// case: channelName or channel Id are provided with user token
if (user) {
if (channelName && channelName !== user.channelName) {
throw new Error('the provided channel name does not match user credentials');
}
if (channelId && channelId !== user.channelClaimId) {
throw new Error('the provided channel id does not match user credentials');
}
return {
channelName : user.channelName,
channelClaimId: user.channelClaimId,
};
}
// case: channelName or channel Id are provided with password instead of user token
if (!channelPassword) throw new Error('no channel password provided');
return module.exports.authenticateChannelCredentials(channelName, channelId, channelPassword);
},
authenticateChannelCredentials (channelName, channelId, userPassword) {
return new Promise((resolve, reject) => {
// hoisted variables
let channelData;
// build the params for finding the channel
let channelFindParams = {};
if (channelName) channelFindParams['channelName'] = channelName;
if (channelId) channelFindParams['channelClaimId'] = channelId;
// find the channel
db.Channel
.findOne({
where: channelFindParams,
})
.then(channel => {
if (!channel) {
logger.debug('no channel found');
throw new Error('Authentication failed, you do not have access to that channel');
}
channelData = channel.get();
logger.debug('channel data:', channelData);
return db.User.findOne({
where: { userName: channelData.channelName.substring(1) },
});
})
.then(user => {
if (!user) {
logger.debug('no user found');
throw new Error('Authentication failed, you do not have access to that channel');
}
return user.comparePassword(userPassword);
})
.then(isMatch => {
if (!isMatch) {
logger.debug('incorrect password');
throw new Error('Authentication failed, you do not have access to that channel');
}
logger.debug('...password was a match...');
resolve(channelData);
})
.catch(error => {
reject(error);
});
});
},
};

View file

@ -0,0 +1,19 @@
const db = require('../../../../models');
const checkChannelAvailability = (name) => {
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;
});
};
module.exports = checkChannelAvailability;

View file

@ -1,6 +1,6 @@
const { checkChannelAvailability } = require('../../controllers/publishController.js');
const { sendGATimingEvent } = require('../../helpers/googleAnalytics.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const checkChannelAvailability = require('./checkChannelAvailability.js');
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
/*

View file

@ -4,7 +4,7 @@ module.exports = {
returnPaginatedChannelClaims (channelName, longChannelClaimId, claims, page) {
const totalPages = module.exports.determineTotalPages(claims);
const paginationPage = module.exports.getPageFromQuery(page);
const viewData = {
return {
channelName : channelName,
longChannelClaimId: longChannelClaimId,
claims : module.exports.extractPageFromClaims(claims, paginationPage),
@ -14,7 +14,6 @@ module.exports = {
totalPages : totalPages,
totalResults : module.exports.determineTotalClaims(claims),
};
return viewData;
},
getPageFromQuery (page) {
if (page) {
@ -30,8 +29,7 @@ module.exports = {
// logger.debug(`pageNumber ${pageNumber} is number?`, Number.isInteger(pageNumber));
const claimStartIndex = (pageNumber - 1) * CLAIMS_PER_PAGE;
const claimEndIndex = claimStartIndex + CLAIMS_PER_PAGE;
const pageOfClaims = claims.slice(claimStartIndex, claimEndIndex);
return pageOfClaims;
return claims.slice(claimStartIndex, claimEndIndex);
},
determineTotalPages (claims) {
if (!claims) {

View file

@ -0,0 +1,29 @@
const db = require('../../../../models');
const { returnPaginatedChannelClaims } = require('./channelPagination.js');
const getChannelClaims = (channelName, channelClaimId, page) => {
return new Promise((resolve, reject) => {
let longChannelClaimId;
// 1. get the long channel Id (make sure channel exists)
db.Certificate
.getLongChannelId(channelName, channelClaimId)
.then(result => {
longChannelClaimId = result;
return db
.Claim
.getAllChannelClaims(longChannelClaimId);
})
.then(channelClaimsArray => {
// 3. format the data for the view, including pagination
let paginatedChannelViewData = returnPaginatedChannelClaims(channelName, longChannelClaimId, channelClaimsArray, page);
// 4. return all the channel information and contents
resolve(paginatedChannelViewData);
})
.catch(error => {
reject(error);
});
});
};
module.exports = getChannelClaims;

View file

@ -1,5 +1,5 @@
const { getChannelClaims } = require('../../controllers/serveController.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const getChannelClaims = require('./getChannelClaims.js');
const NO_CHANNEL = 'NO_CHANNEL';
@ -16,12 +16,15 @@ const channelClaims = ({ ip, originalUrl, body, params }, res) => {
const page = params.page;
getChannelClaims(channelName, channelClaimId, page)
.then(data => {
if (data === NO_CHANNEL) {
return res.status(404).json({success: false, message: 'No matching channel was found'});
}
res.status(200).json({success: true, data});
})
.catch(error => {
if (error === NO_CHANNEL) {
return res.status(404).json({
success: false,
message: 'No matching channel was found',
});
}
handleErrorResponse(originalUrl, ip, error, res);
});
};

View file

@ -0,0 +1,28 @@
const db = require('../../../../models');
const getChannelData = (channelName, channelClaimId) => {
return new Promise((resolve, reject) => {
let longChannelClaimId;
// 1. get the long channel Id (make sure channel exists)
db.Certificate
.getLongChannelId(channelName, channelClaimId)
.then(fullClaimId => {
longChannelClaimId = fullClaimId;
return db
.Certificate
.getShortChannelIdFromLongChannelId(fullClaimId, channelName);
})
.then(shortChannelClaimId => {
resolve({
channelName,
longChannelClaimId,
shortChannelClaimId,
});
})
.catch(error => {
reject(error);
});
});
};
module.exports = getChannelData;

View file

@ -1,5 +1,6 @@
const { getChannelData } = require('../../controllers/serveController.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const getChannelData = require('./getChannelData.js');
const NO_CHANNEL = 'NO_CHANNEL';
@ -13,14 +14,20 @@ const channelData = ({ ip, originalUrl, body, params }, res) => {
const channelName = params.channelName;
let channelClaimId = params.channelClaimId;
if (channelClaimId === 'none') channelClaimId = null;
getChannelData(channelName, channelClaimId, 0)
getChannelData(channelName, channelClaimId)
.then(data => {
if (data === NO_CHANNEL) {
return res.status(404).json({success: false, message: 'No matching channel was found'});
}
res.status(200).json({success: true, data});
res.status(200).json({
success: true,
data
});
})
.catch(error => {
if (error === NO_CHANNEL) {
return res.status(404).json({
success: false,
message: 'No matching channel was found',
});
}
handleErrorResponse(originalUrl, ip, error, res);
});
};

View file

@ -1,5 +1,5 @@
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const db = require('../../models');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models');
/*

View file

@ -0,0 +1,31 @@
const db = require('../../../../models/index');
const { publishing: { primaryClaimAddress, additionalClaimAddresses } } = require('../../../../../config/siteConfig.js');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
const claimAvailability = (name) => {
const claimAddresses = additionalClaimAddresses || [];
claimAddresses.push(primaryClaimAddress);
// find any records where the name is used
return db.Claim
.findAll({
attributes: ['address'],
where : {
name,
address: {
[Op.or]: claimAddresses,
},
},
})
.then(result => {
if (result.length >= 1) {
throw new Error('That claim is already in use');
}
return name;
})
.catch(error => {
throw error;
});
};
module.exports = claimAvailability;

View file

@ -1,6 +1,6 @@
const { claimNameIsAvailable } = require('../../controllers/publishController.js');
const { sendGATimingEvent } = require('../../helpers/googleAnalytics.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const checkClaimAvailability = require('./checkClaimAvailability.js');
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
/*
@ -10,7 +10,7 @@ const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const claimAvailability = ({ ip, originalUrl, params: { name } }, res) => {
const gaStartTime = Date.now();
claimNameIsAvailable(name)
checkClaimAvailability(name)
.then(result => {
res.status(200).json(result);
sendGATimingEvent('end-to-end', 'claim name availability', name, gaStartTime, Date.now());

View file

@ -0,0 +1,56 @@
const logger = require('winston');
const db = require('../../../../models');
const updateBlockedList = (req, res) => {
return fetch('https://api.lbry.io/file/list_blocked')
.then(response => {
return response.json();
})
.then(jsonResponse => {
if (!jsonResponse.data) {
throw new Error('no data in list_blocked response');
}
if (!jsonResponse.data.outpoints) {
throw new Error('no outpoints in list_blocked response');
}
return jsonResponse.data.outpoints;
})
.then(outpoints => {
logger.info('number of blocked outpoints:', outpoints.length);
let updatePromises = [];
outpoints.forEach(outpoint => {
// logger.debug('outpoint:', outpoint);
updatePromises.push(db.Claim
.findOne({
where: {
outpoint,
},
})
.then(Claim => {
if (Claim) {
const { claimId, name } = Claim;
logger.debug(`creating record in Blocked for ${name}#${claimId}`);
const blocked = {
claimId,
name,
outpoint,
};
return db.upsert(db.Blocked, blocked, blocked, 'Blocked')
}
})
.catch(error => {
logger.error(error);
}));
});
return Promise.all(updatePromises);
})
.then(() => {
logger.info('finished updating blocked content list');
res.status(200).json({success: true, message: 'finished updating blocked content list'});
})
.catch((error) => {
logger.error(error);
});
};
module.exports = updateBlockedList;

View file

@ -1,5 +1,5 @@
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const db = require('../../models');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models');
/*

View file

@ -0,0 +1,5 @@
module.exports = (fileInfo, getResult) => {
fileInfo.fileName = getResult.file_name;
fileInfo.filePath = getResult.download_path;
return fileInfo;
};

View file

@ -0,0 +1,13 @@
module.exports = ({ name, claimId, outpoint, height, address, nsfw, contentType }) => {
return {
name,
claimId,
outpoint,
height,
address,
fileName: '',
filePath: '',
fileType: contentType,
nsfw,
};
};

View file

@ -1,7 +1,8 @@
const { getClaim } = require('../../helpers/lbryApi.js');
const { addGetResultsToFileData, createFileData } = require('../../helpers/publishHelpers.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const db = require('../../models');
const { getClaim } = require('../../../../lbrynet');
const addGetResultsToFileData = require('./addGetResultsToFileData.js');
const createFileData = require('./createFileData.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models');
/*

View file

@ -1,5 +1,5 @@
const { getClaimList } = require('../../helpers/lbryApi.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const { getClaimList } = require('../../../../lbrynet');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
/*

View file

@ -0,0 +1,31 @@
const logger = require('winston');
const db = require('../../../../models');
const getClaimIdByChannel = (channelName, channelClaimId, claimName) => {
return new Promise((resolve, reject) => {
db.Certificate
.getLongChannelId(channelName, channelClaimId)
.then(longChannelId => {
return db.Claim.getClaimIdByLongChannelId(longChannelId, claimName);
})
.then(longClaimId => {
resolve(longClaimId);
})
.catch(error => {
reject(error);
});
});
};
const getClaimId = (channelName, channelClaimId, name, claimId) => {
if (channelName) {
logger.debug(`getClaimIdByChannel(${channelName}, ${channelClaimId}, ${name})`);
return getClaimIdByChannel(channelName, channelClaimId, name);
} else {
logger.debug(`db.Claim.getLongClaimId(${name}, ${claimId})`);
return db.Claim.getLongClaimId(name, claimId);
}
};
module.exports = getClaimId;

View file

@ -0,0 +1,53 @@
const db = require('../../../../models');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const getClaimId = require('./getClaimId.js');
const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM';
const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
/*
route to get a long claim id
*/
const claimLongId = ({ ip, originalUrl, body, params }, res) => {
const channelName = body.channelName;
const channelClaimId = body.channelClaimId;
const claimName = body.claimName;
let claimId = body.claimId;
getClaimId(channelName, channelClaimId, claimName, claimId)
.then(fullClaimId => {
claimId = fullClaimId;
return db.Blocked.isNotBlocked(fullClaimId, claimName);
})
.then(() => {
res.status(200).json({success: true, data: claimId});
})
.catch(error => {
if (error === NO_CLAIM) {
return res.status(404).json({
success: false,
message: 'No matching claim id could be found for that url',
});
}
if (error === NO_CHANNEL) {
return res.status(404).json({
success: false,
message: 'No matching channel id could be found for that url',
});
}
if (error === BLOCKED_CLAIM) {
return res.status(410).json({
success: false,
message: 'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more details, see https://lbry.io/faq/dmca',
});
}
handleErrorResponse(originalUrl, ip, error, res);
});
};
module.exports = claimLongId;

View file

@ -0,0 +1,75 @@
const logger = require('winston');
const db = require('../../../../models/index');
const authenticateChannelCredentials = (channelName, channelId, userPassword) => {
return new Promise((resolve, reject) => {
// hoisted variables
let channelData;
// build the params for finding the channel
let channelFindParams = {};
if (channelName) channelFindParams['channelName'] = channelName;
if (channelId) channelFindParams['channelClaimId'] = channelId;
// find the channel
db.Channel
.findOne({
where: channelFindParams,
})
.then(channel => {
if (!channel) {
logger.debug('no channel found');
throw new Error('Authentication failed, you do not have access to that channel');
}
channelData = channel.get();
logger.debug('channel data:', channelData);
return db.User.findOne({
where: { userName: channelData.channelName.substring(1) },
});
})
.then(user => {
if (!user) {
logger.debug('no user found');
throw new Error('Authentication failed, you do not have access to that channel');
}
return user.comparePassword(userPassword);
})
.then(isMatch => {
if (!isMatch) {
logger.debug('incorrect password');
throw new Error('Authentication failed, you do not have access to that channel');
}
logger.debug('...password was a match...');
resolve(channelData);
})
.catch(error => {
reject(error);
});
});
};
const authenticateUser = (channelName, channelId, channelPassword, user) => {
// case: no channelName or channel Id are provided (anonymous), regardless of whether user token is provided
if (!channelName && !channelId) {
return {
channelName : null,
channelClaimId: null,
};
}
// case: channelName or channel Id are provided with user token
if (user) {
if (channelName && channelName !== user.channelName) {
throw new Error('the provided channel name does not match user credentials');
}
if (channelId && channelId !== user.channelClaimId) {
throw new Error('the provided channel id does not match user credentials');
}
return {
channelName : user.channelName,
channelClaimId: user.channelClaimId,
};
}
// case: channelName or channel Id are provided with password instead of user token
if (!channelPassword) throw new Error('no channel password provided');
return authenticateChannelCredentials(channelName, channelId, channelPassword);
};
module.exports = authenticateUser;

View file

@ -0,0 +1,40 @@
const logger = require('winston');
const { details, publishing } = require('../../../../../config/siteConfig.js');
const createBasicPublishParams = (filePath, name, title, description, license, nsfw, thumbnail) => {
logger.debug(`Creating Publish Parameters`);
// provide defaults for title
if (title === null || title.trim() === '') {
title = name;
}
// provide default for description
if (description === null || description.trim() === '') {
description = '';
}
// provide default for license
if (license === null || license.trim() === '') {
license = ' '; // default to empty string
}
// create the publish params
const publishParams = {
name,
file_path: filePath,
bid : 0.01,
metadata : {
description,
title,
author : details.title,
language: 'en',
license,
nsfw,
},
claim_address: publishing.primaryClaimAddress,
};
// add thumbnail to channel if video
if (thumbnail) {
publishParams['metadata']['thumbnail'] = thumbnail;
}
return publishParams;
};
module.exports = createBasicPublishParams;

View file

@ -0,0 +1,28 @@
const logger = require('winston');
const { details, publishing } = require('../../../../../config/siteConfig.js');
const createThumbnailPublishParams = (thumbnailFilePath, claimName, license, nsfw) => {
if (!thumbnailFilePath) {
return;
}
logger.debug(`Creating Thumbnail Publish Parameters`);
// create the publish params
return {
name : `${claimName}-thumb`,
file_path: thumbnailFilePath,
bid : 0.01,
metadata : {
title : `${claimName} thumbnail`,
description: `a thumbnail for ${claimName}`,
author : details.title,
language : 'en',
license,
nsfw,
},
claim_address: publishing.primaryClaimAddress,
channel_name : publishing.thumbnailChannel,
channel_id : publishing.thumbnailChannelId,
};
};
module.exports = createThumbnailPublishParams;

View file

@ -0,0 +1,13 @@
const logger = require('winston');
const fs = require('fs');
const deleteFile = (filePath) => {
fs.unlink(filePath, err => {
if (err) {
return logger.error(`error deleting temporary file ${filePath}`);
}
logger.debug(`successfully deleted ${filePath}`);
});
};
module.exports = deleteFile;

View file

@ -1,9 +1,17 @@
const { createBasicPublishParams, createThumbnailPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles } = require('../../helpers/publishHelpers.js');
const { claimNameIsAvailable, publish } = require('../../controllers/publishController.js');
const { authenticateUser } = require('../../auth/authentication.js');
const { sendGATimingEvent } = require('../../helpers/googleAnalytics.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const { details: { host } } = require('../../../config/siteConfig.js');
const { details: { host } } = require('../../../../../config/siteConfig.js');
const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const checkClaimAvailability = require('../availability/checkClaimAvailability.js');
const publish = require('./publish.js');
const createBasicPublishParams = require('./createBasicPublishParams.js');
const createThumbnailPublishParams = require('./createThumbnailPublishParams.js');
const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js');
const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js');
const authenticateUser = require('./authentication.js');
/*
@ -29,7 +37,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user }, res) => {
Promise
.all([
authenticateUser(channelName, channelId, channelPassword, user),
claimNameIsAvailable(name),
checkClaimAvailability(name),
createBasicPublishParams(filePath, name, title, description, license, nsfw, thumbnail),
createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw),
])
@ -39,7 +47,7 @@ const claimPublish = ({ body, files, headers, ip, originalUrl, user }, res) => {
publishParams['channel_name'] = channelName;
publishParams['channel_id'] = channelClaimId;
}
// publish the thumbnail
// publish the thumbnail, if one exists
if (thumbnailPublishParams) {
publish(thumbnailPublishParams, thumbnailFileName, thumbnailFileType);
}

View file

@ -0,0 +1,27 @@
const parsePublishApiRequestBody = ({name, nsfw, license, title, description, thumbnail}) => {
// validate name
if (!name) {
throw new Error('no name field found in request');
}
const invalidNameCharacters = /[^A-Za-z0-9,-]/.exec(name);
if (invalidNameCharacters) {
throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
}
// optional parameters
nsfw = (nsfw === 'true');
license = license || null;
title = title || null;
description = description || null;
thumbnail = thumbnail || null;
// return results
return {
name,
nsfw,
license,
title,
description,
thumbnail,
};
};
module.exports = parsePublishApiRequestBody;

View file

@ -0,0 +1,34 @@
const validateFileTypeAndSize = require('./validateFileTypeAndSize.js');
const parsePublishApiRequestFiles = ({file, thumbnail}) => {
// make sure a file was provided
if (!file) {
throw new Error('no file with key of [file] found in request');
}
if (!file.path) {
throw new Error('no file path found');
}
if (!file.type) {
throw new Error('no file type found');
}
if (!file.size) {
throw new Error('no file type found');
}
// validate the file name
if (/'/.test(file.name)) {
throw new Error('apostrophes are not allowed in the file name');
}
// validate the file
validateFileTypeAndSize(file);
// return results
return {
fileName : file.name,
filePath : file.path,
fileType : file.type,
thumbnailFileName: (thumbnail ? thumbnail.name : null),
thumbnailFilePath: (thumbnail ? thumbnail.path : null),
thumbnailFileType: (thumbnail ? thumbnail.type : null),
};
};
module.exports = parsePublishApiRequestFiles;

View file

@ -0,0 +1,92 @@
const logger = require('winston');
const db = require('../../../../models');
const { publishClaim } = require('../../../../lbrynet');
const deleteFile = require('./deleteFile.js');
const publish = (publishParams, fileName, fileType) => {
return new Promise((resolve, reject) => {
let publishResults, certificateId, channelName;
// publish the file
return publishClaim(publishParams)
.then(tx => {
logger.info(`Successfully published ${publishParams.name} ${fileName}`, tx);
publishResults = tx;
// get the channel information
if (publishParams.channel_name) {
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 not published in a channel');
return null;
}
})
.then(channel => {
// set channel information
certificateId = null;
channelName = null;
if (channel) {
certificateId = channel.channelClaimId;
channelName = channel.channelName;
}
logger.debug(`certificateId: ${certificateId}`);
})
.then(() => {
// create the File record
const fileRecord = {
name : publishParams.name,
claimId : publishResults.claim_id,
title : publishParams.metadata.title,
description: publishParams.metadata.description,
address : publishParams.claim_address,
outpoint : `${publishResults.txid}:${publishResults.nout}`,
height : 0,
fileName,
filePath : publishParams.file_path,
fileType,
nsfw : publishParams.metadata.nsfw,
};
// create the Claim record
const claimRecord = {
name : publishParams.name,
claimId : publishResults.claim_id,
title : publishParams.metadata.title,
description: publishParams.metadata.description,
address : publishParams.claim_address,
thumbnail : publishParams.metadata.thumbnail,
outpoint : `${publishResults.txid}:${publishResults.nout}`,
height : 0,
contentType: fileType,
nsfw : publishParams.metadata.nsfw,
amount : publishParams.bid,
certificateId,
channelName,
};
// upsert criteria
const upsertCriteria = {
name : publishParams.name,
claimId: publishResults.claim_id,
};
// upsert the records
return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]);
})
.then(([file, claim]) => {
logger.debug('File and Claim records successfully created');
return Promise.all([file.setClaim(claim), claim.setFile(file)]);
})
.then(() => {
logger.debug('File and Claim records successfully associated');
resolve(publishResults); // resolve the promise with the result from lbryApi publishClaim;
})
.catch(error => {
logger.error('PUBLISH ERROR', error);
deleteFile(publishParams.file_path); // delete the local file
reject(error);
});
});
};
module.exports = publish;

View file

@ -0,0 +1,33 @@
const logger = require('winston');
const validateFileTypeAndSize = (file) => {
// check file type and size
switch (file.type) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
if (file.size > 10000000) {
logger.debug('publish > file validation > .jpeg/.jpg/.png was too big');
throw new Error('Sorry, images are limited to 10 megabytes.');
}
break;
case 'image/gif':
if (file.size > 50000000) {
logger.debug('publish > file validation > .gif was too big');
throw new Error('Sorry, .gifs are limited to 50 megabytes.');
}
break;
case 'video/mp4':
if (file.size > 50000000) {
logger.debug('publish > file validation > .mp4 was too big');
throw new Error('Sorry, videos are limited to 50 megabytes.');
}
break;
default:
logger.debug('publish > file validation > unrecognized file type');
throw new Error('The ' + file.type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.');
}
return file;
};
module.exports = validateFileTypeAndSize;

View file

@ -1,5 +1,5 @@
const { resolveUri } = require('../../helpers/lbryApi.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const { resolveUri } = require('../../../../lbrynet/index');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
/*

View file

@ -1,5 +1,5 @@
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const db = require('../../models');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models');
/*

View file

@ -1,5 +1,5 @@
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const db = require('../../models');
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
const db = require('../../../../models');
/*

View file

@ -1,7 +1,12 @@
const { sendGAServeEvent } = require('../../helpers/googleAnalytics');
const { determineResponseType, logRequestData, getClaimIdAndServeAsset } = require('../../helpers/serveHelpers.js');
const lbryUri = require('../../helpers/lbryUri.js');
const handleShowRender = require('../../render/build/handleShowRender.js');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../utils/lbryUri.js');
const determineResponseType = require('../utils/determineResponseType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
const logRequestData = require('../utils/logRequestData.js');
const SERVE = 'SERVE';
/*

View file

@ -1,12 +1,12 @@
const { sendGAServeEvent } = require('../../helpers/googleAnalytics');
const {
determineResponseType,
flipClaimNameAndIdForBackwardsCompatibility,
logRequestData,
getClaimIdAndServeAsset,
} = require('../../helpers/serveHelpers.js');
const lbryUri = require('../../helpers/lbryUri.js');
const handleShowRender = require('../../render/build/handleShowRender.js');
const { sendGAServeEvent } = require('../../../utils/googleAnalytics');
const handleShowRender = require('../../../render/build/handleShowRender.js');
const lbryUri = require('../utils/lbryUri.js');
const determineResponseType = require('../utils/determineResponseType.js');
const getClaimIdAndServeAsset = require('../utils/getClaimIdAndServeAsset.js');
const flipClaimNameAndId = require('../utils/flipClaimNameAndId.js');
const logRequestData = require('../utils/logRequestData.js');
const SERVE = 'SERVE';
@ -46,8 +46,9 @@ const serverAssetByIdentifierAndClaim = (req, res) => {
} catch (error) {
return res.status(400).json({success: false, message: error.message});
}
// for backwards compatability, flip claim name and claim id if necessary
if (!isChannel) {
[claimId, claimName] = flipClaimNameAndIdForBackwardsCompatibility(claimId, claimName);
[claimId, claimName] = flipClaimNameAndId(claimId, claimName);
}
// log the request data for debugging
logRequestData(responseType, claimName, channelName, claimId);

View file

@ -0,0 +1,37 @@
const logger = require('winston');
const SERVE = 'SERVE';
const SHOW = 'SHOW';
function clientAcceptsHtml ({accept}) {
return accept && accept.match(/text\/html/);
};
function requestIsFromBrowser (headers) {
return headers['user-agent'] && headers['user-agent'].match(/Mozilla/);
};
function clientWantsAsset ({accept, range}) {
const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/) && !accept.match(/text\/\*/);
const videoIsWanted = accept && range;
return imageIsWanted || videoIsWanted;
};
const determineResponseType = (hasFileExtension, headers) => {
let responseType;
if (hasFileExtension) {
responseType = SERVE; // assume a serve request if file extension is present
if (clientAcceptsHtml(headers)) { // if the request comes from a browser, change it to a show request
responseType = SHOW;
}
} else {
responseType = SHOW;
if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url
logger.debug('Show request came from browser but wants an image/video. Changing response to serve...');
responseType = SERVE;
}
}
return responseType;
};
module.exports = determineResponseType;

View file

@ -0,0 +1,23 @@
function isValidClaimId (claimId) {
return ((claimId.length === 40) && !/[^A-Za-z0-9]/g.test(claimId));
};
function isValidShortId (claimId) {
return claimId.length === 1; // it should really evaluate the short url itself
};
function isValidShortIdOrClaimId (input) {
return (isValidClaimId(input) || isValidShortId(input));
};
const flipClaimNameAndId = (identifier, name) => {
// this is a patch for backwards compatability with '/name/claimId' url format
if (isValidShortIdOrClaimId(name) && !isValidShortIdOrClaimId(identifier)) {
const tempName = name;
name = identifier;
identifier = tempName;
}
return [identifier, name];
};
module.exports = flipClaimNameAndId;

View file

@ -0,0 +1,58 @@
const logger = require('winston');
const db = require('../../../models');
const getClaimId = require('../../api/claim/longId/getClaimId.js');
const { handleErrorResponse } = require('../../utils/errorHandlers.js');
const getLocalFileRecord = require('./getLocalFileRecord.js');
const serveFile = require('./serveFile.js');
const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM';
const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
const NO_FILE = 'NO_FILE';
const getClaimIdAndServeAsset = (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) => {
getClaimId(channelName, channelClaimId, claimName, claimId)
.then(fullClaimId => {
claimId = fullClaimId;
return db.Blocked.isNotBlocked(fullClaimId, claimName);
})
.then(() => {
return getLocalFileRecord(claimId, claimName);
})
.then(fileRecord => {
serveFile(fileRecord, res);
})
.catch(error => {
if (error === NO_CLAIM) {
logger.debug('no claim found');
return res.status(404).json({
success: false,
message: 'No matching claim id could be found for that url',
});
}
if (error === NO_CHANNEL) {
logger.debug('no channel found');
return res.status(404).json({
success: false,
message: 'No matching channel id could be found for that url',
});
}
if (error === BLOCKED_CLAIM) {
logger.debug('claim was blocked');
return res.status(451).json({
success: false,
message: 'In response to a complaint we received under the US Digital Millennium Copyright Act, we have blocked access to this content from our applications. For more details, see https://lbry.io/faq/dmca',
});
}
if (error === NO_FILE) {
logger.debug('claim was blocked');
return res.status(307).redirect(`/api/claim/get/${name}/${claimId}`);
}
handleErrorResponse(originalUrl, ip, error, res);
});
};
module.exports = getClaimIdAndServeAsset;

View file

@ -0,0 +1,15 @@
const db = require('../../../models');
const NO_FILE = 'NO_FILE';
const getLocalFileRecord = (claimId, name) => {
return db.File.findOne({where: {claimId, name}})
.then(file => {
if (!file) {
return NO_FILE;
}
return file.dataValues;
});
};
module.exports = getLocalFileRecord;

View file

@ -0,0 +1,10 @@
const logger = require('winston');
const logRequestData = (responseType, claimName, channelName, claimId) => {
logger.debug('responseType ===', responseType);
logger.debug('claim name === ', claimName);
logger.debug('channel name ===', channelName);
logger.debug('claim id ===', claimId);
};
module.exports = logRequestData;

View file

@ -0,0 +1,14 @@
const logger = require('winston');
const serveFile = ({ filePath, fileType }, res) => {
logger.verbose(`serving file: ${filePath}`);
const sendFileOptions = {
headers: {
'X-Content-Type-Options': 'nosniff',
'Content-Type' : fileType || 'image/jpeg',
},
};
res.status(200).sendFile(filePath, sendFileOptions);
};
module.exports = serveFile;

View file

@ -1,4 +1,4 @@
const speechPassport = require('../../speechPassport');
const speechPassport = require('../../../speechPassport/index');
const login = (req, res, next) => {
speechPassport.authenticate('local-login', (err, user, info) => {

View file

@ -1,134 +0,0 @@
const logger = require('winston');
const db = require('../models');
const lbryApi = require('../helpers/lbryApi.js');
const publishHelpers = require('../helpers/publishHelpers.js');
const { publishing: { primaryClaimAddress, additionalClaimAddresses } } = require('../../config/siteConfig.js');
const Sequelize = require('sequelize');
const Op = Sequelize.Op;
module.exports = {
publish (publishParams, fileName, fileType) {
return new Promise((resolve, reject) => {
let publishResults, certificateId, channelName;
// publish the file
return lbryApi.publishClaim(publishParams)
.then(tx => {
logger.info(`Successfully published ${publishParams.name} ${fileName}`, tx);
publishResults = tx;
// get the channel information
if (publishParams.channel_name) {
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 not published in a channel');
return null;
}
})
.then(channel => {
// set channel information
certificateId = null;
channelName = null;
if (channel) {
certificateId = channel.channelClaimId;
channelName = channel.channelName;
}
logger.debug(`certificateId: ${certificateId}`);
})
.then(() => {
// create the File record
const fileRecord = {
name : publishParams.name,
claimId : publishResults.claim_id,
title : publishParams.metadata.title,
description: publishParams.metadata.description,
address : publishParams.claim_address,
outpoint : `${publishResults.txid}:${publishResults.nout}`,
height : 0,
fileName,
filePath : publishParams.file_path,
fileType,
nsfw : publishParams.metadata.nsfw,
};
// create the Claim record
const claimRecord = {
name : publishParams.name,
claimId : publishResults.claim_id,
title : publishParams.metadata.title,
description: publishParams.metadata.description,
address : publishParams.claim_address,
thumbnail : publishParams.metadata.thumbnail,
outpoint : `${publishResults.txid}:${publishResults.nout}`,
height : 0,
contentType: fileType,
nsfw : publishParams.metadata.nsfw,
amount : publishParams.bid,
certificateId,
channelName,
};
// upsert criteria
const upsertCriteria = {
name : publishParams.name,
claimId: publishResults.claim_id,
};
// upsert the records
return Promise.all([db.upsert(db.File, fileRecord, upsertCriteria, 'File'), db.upsert(db.Claim, claimRecord, upsertCriteria, 'Claim')]);
})
.then(([file, claim]) => {
logger.debug('File and Claim records successfully created');
return Promise.all([file.setClaim(claim), claim.setFile(file)]);
})
.then(() => {
logger.debug('File and Claim records successfully associated');
resolve(publishResults); // resolve the promise with the result from lbryApi.publishClaim;
})
.catch(error => {
logger.error('PUBLISH ERROR', error);
publishHelpers.deleteTemporaryFile(publishParams.file_path); // delete the local file
reject(error);
});
});
},
claimNameIsAvailable (name) {
const claimAddresses = additionalClaimAddresses || [];
claimAddresses.push(primaryClaimAddress);
// find any records where the name is used
return db.Claim
.findAll({
attributes: ['address'],
where : {
name,
address: {
[Op.or]: claimAddresses,
},
},
})
.then(result => {
if (result.length >= 1) {
throw new Error('That claim is already in use');
};
return name;
})
.catch(error => {
throw error;
});
},
checkChannelAvailability (name) {
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;
});
},
};

View file

@ -1,117 +0,0 @@
const db = require('../models');
const logger = require('winston');
const { returnPaginatedChannelClaims } = require('../helpers/channelPagination.js');
const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM';
const NO_FILE = 'NO_FILE';
module.exports = {
getClaimId (channelName, channelClaimId, name, claimId) {
if (channelName) {
return module.exports.getClaimIdByChannel(channelName, channelClaimId, name);
} else {
return module.exports.getClaimIdByClaim(name, claimId);
}
},
getClaimIdByClaim (claimName, claimId) {
logger.debug(`getClaimIdByClaim(${claimName}, ${claimId})`);
return new Promise((resolve, reject) => {
db.Claim.getLongClaimId(claimName, claimId)
.then(longClaimId => {
if (!longClaimId) {
resolve(NO_CLAIM);
}
resolve(longClaimId);
})
.catch(error => {
reject(error);
});
});
},
getClaimIdByChannel (channelName, channelClaimId, claimName) {
logger.debug(`getClaimIdByChannel(${channelName}, ${channelClaimId}, ${claimName})`);
return new Promise((resolve, reject) => {
db.Certificate.getLongChannelId(channelName, channelClaimId) // 1. get the long channel id
.then(longChannelId => {
if (!longChannelId) {
return [null, null];
}
return Promise.all([longChannelId, db.Claim.getClaimIdByLongChannelId(longChannelId, claimName)]); // 2. get the long claim id
})
.then(([longChannelId, longClaimId]) => {
if (!longChannelId) {
return resolve(NO_CHANNEL);
}
if (!longClaimId) {
return resolve(NO_CLAIM);
}
resolve(longClaimId);
})
.catch(error => {
reject(error);
});
});
},
getChannelData (channelName, channelClaimId, page) {
return new Promise((resolve, reject) => {
// 1. get the long channel Id (make sure channel exists)
db.Certificate.getLongChannelId(channelName, channelClaimId)
.then(longChannelClaimId => {
if (!longChannelClaimId) {
return [null, null, null];
}
// 2. get the short ID and all claims for that channel
return Promise.all([longChannelClaimId, db.Certificate.getShortChannelIdFromLongChannelId(longChannelClaimId, channelName)]);
})
.then(([longChannelClaimId, shortChannelClaimId]) => {
if (!longChannelClaimId) {
return resolve(NO_CHANNEL);
}
// 3. return all the channel information
resolve({
channelName,
longChannelClaimId,
shortChannelClaimId,
});
})
.catch(error => {
reject(error);
});
});
},
getChannelClaims (channelName, channelClaimId, page) {
return new Promise((resolve, reject) => {
// 1. get the long channel Id (make sure channel exists)
db.Certificate.getLongChannelId(channelName, channelClaimId)
.then(longChannelClaimId => {
if (!longChannelClaimId) {
return [null, null, null];
}
// 2. get the short ID and all claims for that channel
return Promise.all([longChannelClaimId, db.Claim.getAllChannelClaims(longChannelClaimId)]);
})
.then(([longChannelClaimId, channelClaimsArray]) => {
if (!longChannelClaimId) {
return resolve(NO_CHANNEL);
}
// 3. format the data for the view, including pagination
let paginatedChannelViewData = returnPaginatedChannelClaims(channelName, longChannelClaimId, channelClaimsArray, page);
// 4. return all the channel information and contents
resolve(paginatedChannelViewData);
})
.catch(error => {
reject(error);
});
});
},
getLocalFileRecord (claimId, name) {
return db.File.findOne({where: {claimId, name}})
.then(file => {
if (!file) {
return NO_FILE;
}
return file.dataValues;
});
},
};

View file

@ -1,19 +0,0 @@
const logger = require('winston');
const db = require('../models');
module.exports = {
getRecentClaims () {
logger.debug('retrieving most recent claims');
return new Promise((resolve, reject) => {
// get the raw requests data
db.File.getRecentClaims()
.then(results => {
resolve(results);
})
.catch(error => {
logger.error('sequelize error', error);
reject(error);
});
});
},
};

View file

@ -21,8 +21,8 @@ module.exports = {
message = error.message;
} else {
message = error;
};
};
}
}
return [status, message];
},
useObjectPropertiesIfNoKeys: function (err) {

View file

@ -1,10 +0,0 @@
module.exports = {
serializeSpeechUser (user, done) { // returns user data to be serialized into session
console.log('serializing user');
done(null, user);
},
deserializeSpeechUser (user, done) { // deserializes session and populates additional info to req.user
console.log('deserializing user');
done(null, user);
},
};

View file

@ -1,17 +0,0 @@
const logger = require('winston');
module.exports = (config) => {
// get the config file
for (let configCategoryKey in config) {
if (config.hasOwnProperty(configCategoryKey)) {
// get the final variables for each config category
const configVariables = config[configCategoryKey];
for (let configVarKey in configVariables) {
if (configVariables.hasOwnProperty(configVarKey)) {
// print each variable
logger.debug(`CONFIG CHECK: ${configCategoryKey}.${configVarKey} === ${configVariables[configVarKey]}`);
}
}
}
}
};

View file

@ -1,176 +0,0 @@
const logger = require('winston');
const fs = require('fs');
const { details, publishing } = require('../../config/siteConfig.js');
module.exports = {
parsePublishApiRequestBody ({name, nsfw, license, title, description, thumbnail}) {
// validate name
if (!name) {
throw new Error('no name field found in request');
}
const invalidNameCharacters = /[^A-Za-z0-9,-]/.exec(name);
if (invalidNameCharacters) {
throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
}
// optional parameters
nsfw = (nsfw === 'true');
license = license || null;
title = title || null;
description = description || null;
thumbnail = thumbnail || null;
// return results
return {
name,
nsfw,
license,
title,
description,
thumbnail,
};
},
parsePublishApiRequestFiles ({file, thumbnail}) {
// make sure a file was provided
if (!file) {
throw new Error('no file with key of [file] found in request');
}
if (!file.path) {
throw new Error('no file path found');
}
if (!file.type) {
throw new Error('no file type found');
}
if (!file.size) {
throw new Error('no file type found');
}
// validate the file name
if (/'/.test(file.name)) {
throw new Error('apostrophes are not allowed in the file name');
}
// validate the file
module.exports.validateFileTypeAndSize(file);
// return results
return {
fileName : file.name,
filePath : file.path,
fileType : file.type,
thumbnailFileName: (thumbnail ? thumbnail.name : null),
thumbnailFilePath: (thumbnail ? thumbnail.path : null),
thumbnailFileType: (thumbnail ? thumbnail.type : null),
};
},
validateFileTypeAndSize (file) {
// check file type and size
switch (file.type) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
if (file.size > 10000000) {
logger.debug('publish > file validation > .jpeg/.jpg/.png was too big');
throw new Error('Sorry, images are limited to 10 megabytes.');
}
break;
case 'image/gif':
if (file.size > 50000000) {
logger.debug('publish > file validation > .gif was too big');
throw new Error('Sorry, .gifs are limited to 50 megabytes.');
}
break;
case 'video/mp4':
if (file.size > 50000000) {
logger.debug('publish > file validation > .mp4 was too big');
throw new Error('Sorry, videos are limited to 50 megabytes.');
}
break;
default:
logger.debug('publish > file validation > unrecognized file type');
throw new Error('The ' + file.type + ' content type is not supported. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.');
}
return file;
},
createBasicPublishParams (filePath, name, title, description, license, nsfw, thumbnail) {
logger.debug(`Creating Publish Parameters`);
// provide defaults for title
if (title === null || title.trim() === '') {
title = name;
}
// provide default for description
if (description === null || description.trim() === '') {
description = '';
}
// provide default for license
if (license === null || license.trim() === '') {
license = ' '; // default to empty string
}
// create the publish params
const publishParams = {
name,
file_path: filePath,
bid : 0.01,
metadata : {
description,
title,
author : details.title,
language: 'en',
license,
nsfw,
},
claim_address: publishing.primaryClaimAddress,
};
// add thumbnail to channel if video
if (thumbnail) {
publishParams['metadata']['thumbnail'] = thumbnail;
}
return publishParams;
},
createThumbnailPublishParams (thumbnailFilePath, claimName, license, nsfw) {
if (!thumbnailFilePath) {
return;
}
logger.debug(`Creating Thumbnail Publish Parameters`);
// create the publish params
return {
name : `${claimName}-thumb`,
file_path: thumbnailFilePath,
bid : 0.01,
metadata : {
title : `${claimName} thumbnail`,
description: `a thumbnail for ${claimName}`,
author : details.title,
language : 'en',
license,
nsfw,
},
claim_address: publishing.primaryClaimAddress,
channel_name : publishing.thumbnailChannel,
channel_id : publishing.thumbnailChannelId,
};
},
deleteTemporaryFile (filePath) {
fs.unlink(filePath, err => {
if (err) {
logger.error(`error deleting temporary file ${filePath}`);
throw err;
}
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,
};
},
};

View file

@ -1,25 +0,0 @@
module.exports = {
returnShortId: function (claimsArray, longId) {
let claimIndex;
let shortId = longId.substring(0, 1); // default short id is the first letter
let shortIdLength = 0;
// find the index of this claim id
claimIndex = claimsArray.findIndex(element => {
return element.claimId === longId;
});
if (claimIndex < 0) {
throw new Error('claim id not found in claims list');
}
// get an array of all claims with lower height
let possibleMatches = claimsArray.slice(0, claimIndex);
// remove certificates with the same prefixes until none are left.
while (possibleMatches.length > 0) {
shortIdLength += 1;
shortId = longId.substring(0, shortIdLength);
possibleMatches = possibleMatches.filter(element => {
return (element.claimId && (element.claimId.substring(0, shortIdLength) === shortId));
});
}
return shortId;
},
};

View file

@ -1,109 +0,0 @@
const logger = require('winston');
const { getClaimId, getLocalFileRecord } = require('../controllers/serveController.js');
const { handleErrorResponse } = require('./errorHandlers.js');
const SERVE = 'SERVE';
const SHOW = 'SHOW';
const NO_FILE = 'NO_FILE';
const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM';
function clientAcceptsHtml ({accept}) {
return accept && accept.match(/text\/html/);
};
function requestIsFromBrowser (headers) {
return headers['user-agent'] && headers['user-agent'].match(/Mozilla/);
};
function clientWantsAsset ({accept, range}) {
const imageIsWanted = accept && accept.match(/image\/.*/) && !accept.match(/text\/html/) && !accept.match(/text\/\*/);
const videoIsWanted = accept && range;
return imageIsWanted || videoIsWanted;
};
function isValidClaimId (claimId) {
return ((claimId.length === 40) && !/[^A-Za-z0-9]/g.test(claimId));
};
function isValidShortId (claimId) {
return claimId.length === 1; // it should really evaluate the short url itself
};
function isValidShortIdOrClaimId (input) {
return (isValidClaimId(input) || isValidShortId(input));
};
function serveAssetToClient (claimId, name, res) {
return getLocalFileRecord(claimId, name)
.then(fileRecord => {
// check that a local record was found
if (fileRecord === NO_FILE) {
return res.status(307).redirect(`/api/claim/get/${name}/${claimId}`);
}
// serve the file
const {filePath, fileType} = fileRecord;
logger.verbose(`serving file: ${filePath}`);
const sendFileOptions = {
headers: {
'X-Content-Type-Options': 'nosniff',
'Content-Type' : fileType || 'image/jpeg',
},
};
res.status(200).sendFile(filePath, sendFileOptions);
})
.catch(error => {
throw error;
});
};
module.exports = {
getClaimIdAndServeAsset (channelName, channelClaimId, claimName, claimId, originalUrl, ip, res) {
// get the claim Id and then serve the asset
getClaimId(channelName, channelClaimId, claimName, claimId)
.then(fullClaimId => {
if (fullClaimId === NO_CLAIM) {
return res.status(404).json({success: false, message: 'no claim id could be found'});
} else if (fullClaimId === NO_CHANNEL) {
return res.status(404).json({success: false, message: 'no channel id could be found'});
}
serveAssetToClient(fullClaimId, claimName, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success');
})
.catch(error => {
handleErrorResponse(originalUrl, ip, error, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail');
});
},
determineResponseType (hasFileExtension, headers) {
let responseType;
if (hasFileExtension) {
responseType = SERVE; // assume a serve request if file extension is present
if (clientAcceptsHtml(headers)) { // if the request comes from a browser, change it to a show request
responseType = SHOW;
}
} else {
responseType = SHOW;
if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url
logger.debug('Show request came from browser but wants an image/video. Changing response to serve...');
responseType = SERVE;
}
}
return responseType;
},
flipClaimNameAndIdForBackwardsCompatibility (identifier, name) {
// this is a patch for backwards compatability with '/name/claim_id' url format
if (isValidShortIdOrClaimId(name) && !isValidShortIdOrClaimId(identifier)) {
const tempName = name;
name = identifier;
identifier = tempName;
}
return [identifier, name];
},
logRequestData (responseType, claimName, channelName, claimId) {
logger.debug('responseType ===', responseType);
logger.debug('claim name === ', claimName);
logger.debug('channel name ===', channelName);
logger.debug('claim id ===', claimId);
},
};

View file

@ -1,38 +0,0 @@
const logger = require('winston');
const db = require('../models');
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);
});
},
};

View file

@ -1,24 +1,9 @@
const axios = require('axios');
const logger = require('winston');
const { api: { apiHost, apiPort } } = require('../../config/lbryConfig.js');
const lbryApiUri = 'http://' + apiHost + ':' + apiPort;
const { chooseGaLbrynetPublishLabel, sendGATimingEvent } = require('./googleAnalytics.js');
const handleLbrynetResponse = ({ data }, resolve, reject) => {
logger.debug('lbry api data:', data);
if (data.result) {
// check for an error
if (data.result.error) {
logger.debug('Lbrynet api error:', data.result.error);
reject(new Error(data.result.error));
return;
};
resolve(data.result);
return;
}
// fallback in case it just timed out
reject(JSON.stringify(data));
};
const lbrynetUri = 'http://' + apiHost + ':' + apiPort;
const { chooseGaLbrynetPublishLabel, sendGATimingEvent } = require('../utils/googleAnalytics.js');
const handleLbrynetResponse = require('./utils/handleLbrynetResponse.js');
module.exports = {
publishClaim (publishParams) {
@ -26,7 +11,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'publish',
params: publishParams,
})
@ -44,7 +29,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'get',
params: { uri, timeout: 20 },
})
@ -62,7 +47,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'claim_list',
params: { name: claimName },
})
@ -80,7 +65,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'resolve',
params: { uri },
})
@ -102,7 +87,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'settings_get',
})
.then(({ data }) => {
@ -124,7 +109,7 @@ module.exports = {
const gaStartTime = Date.now();
return new Promise((resolve, reject) => {
axios
.post(lbryApiUri, {
.post(lbrynetUri, {
method: 'channel_new',
params: {
channel_name: name,

View file

@ -0,0 +1,19 @@
const logger = require('winston');
const handleLbrynetResponse = ({ data }, resolve, reject) => {
logger.debug('lbry api data:', data);
if (data.result) {
// check for an error
if (data.result.error) {
logger.debug('Lbrynet api error:', data.result.error);
reject(new Error(data.result.error));
return;
};
resolve(data.result);
return;
}
// fallback in case it just timed out
reject(JSON.stringify(data));
};
module.exports = handleLbrynetResponse;

50
server/models/blocked.js Normal file
View file

@ -0,0 +1,50 @@
const logger = require('winston');
const BLOCKED_CLAIM = 'BLOCKED_CLAIM';
module.exports = (sequelize, { STRING }) => {
const Blocked = sequelize.define(
'Blocked',
{
claimId: {
type : STRING,
allowNull: false,
},
name: {
type : STRING,
allowNull: false,
},
outpoint: {
type : STRING,
allowNull: false,
},
},
{
freezeTableName: true,
}
);
Blocked.isNotBlocked = function (claimId, name) {
logger.debug(`checking to see if ${name}#${claimId} is not blocked`);
return new Promise((resolve, reject) => {
this.findOne({
where: {
claimId,
name,
},
})
.then(result => {
if (result) {
return reject(BLOCKED_CLAIM);
}
resolve(true);
})
.catch(error => {
logger.error(error);
reject(BLOCKED_CLAIM);
});
});
};
return Blocked;
};

View file

@ -1,5 +1,15 @@
const logger = require('winston');
const { returnShortId } = require('../helpers/sequelizeHelpers.js');
const returnShortId = require('./utils/returnShortId.js');
const NO_CHANNEL = 'NO_CHANNEL';
function isLongChannelId (channelId) {
return (channelId && (channelId.length === 40));
}
function isShortChannelId (channelId) {
return (channelId && (channelId.length < 40));
}
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
const Certificate = sequelize.define(
@ -110,7 +120,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
.then(result => {
switch (result.length) {
case 0:
throw new Error('No channel(s) found with that channel name');
return reject(NO_CHANNEL);
default:
return resolve(returnShortId(result, longChannelId));
}
@ -121,6 +131,25 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Certificate.validateLongChannelId = function (name, claimId) {
logger.debug(`validateLongChannelId(${name}, ${claimId})`);
return new Promise((resolve, reject) => {
this.findOne({
where: {name, claimId},
})
.then(result => {
if (!result) {
return reject(NO_CHANNEL);
}
resolve(claimId);
})
.catch(error => {
logger.error(error);
reject(NO_CHANNEL);
});
});
};
Certificate.getLongChannelIdFromShortChannelId = function (channelName, channelClaimId) {
logger.debug(`getLongChannelIdFromShortChannelId(${channelName}, ${channelClaimId})`);
return new Promise((resolve, reject) => {
@ -137,13 +166,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
.then(result => {
switch (result.length) {
case 0:
return resolve(null);
default: // note results must be sorted
return reject(NO_CHANNEL);
default:
return resolve(result[0].claimId);
}
})
.catch(error => {
reject(error);
logger.error(error);
reject(NO_CHANNEL);
});
});
};
@ -159,43 +189,26 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
.then(result => {
switch (result.length) {
case 0:
return resolve(null);
return reject(NO_CHANNEL);
default:
return resolve(result[0].claimId);
}
})
.catch(error => {
reject(error);
});
});
};
Certificate.validateLongChannelId = function (name, claimId) {
logger.debug(`validateLongChannelId(${name}, ${claimId})`);
return new Promise((resolve, reject) => {
this.findOne({
where: {name, claimId},
})
.then(result => {
if (!result) {
return resolve(null);
};
resolve(claimId);
})
.catch(error => {
reject(error);
logger.error(error);
reject(NO_CHANNEL);
});
});
};
Certificate.getLongChannelId = function (channelName, channelClaimId) {
logger.debug(`getLongChannelId(${channelName}, ${channelClaimId})`);
if (channelClaimId && (channelClaimId.length === 40)) { // if a full channel id is provided
if (isLongChannelId(channelClaimId)) {
return this.validateLongChannelId(channelName, channelClaimId);
} else if (channelClaimId && channelClaimId.length < 40) { // if a short channel id is provided
} else if (isShortChannelId(channelClaimId)) {
return this.getLongChannelIdFromShortChannelId(channelName, channelClaimId);
} else {
return this.getLongChannelIdFromChannelName(channelName); // if no channel id provided
return this.getLongChannelIdFromChannelName(channelName);
}
};

View file

@ -1,7 +1,9 @@
const logger = require('winston');
const { returnShortId } = require('../helpers/sequelizeHelpers.js');
const returnShortId = require('./utils/returnShortId.js');
const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('../../config/siteConfig.js');
const NO_CLAIM = 'NO_CLAIM';
function determineFileExtensionFromContentType (contentType) {
switch (contentType) {
case 'image/jpeg':
@ -17,14 +19,14 @@ function determineFileExtensionFromContentType (contentType) {
logger.debug('setting unknown file type as file extension jpeg');
return 'jpeg';
}
};
}
function determineThumbnail (storedThumbnail, defaultThumbnail) {
if (storedThumbnail === '') {
return defaultThumbnail;
}
return storedThumbnail;
};
}
function prepareClaimData (claim) {
// logger.debug('preparing claim data based on resolved data:', claim);
@ -32,7 +34,15 @@ function prepareClaimData (claim) {
claim['fileExt'] = determineFileExtensionFromContentType(claim.contentType);
claim['host'] = host;
return claim;
};
}
function isLongClaimId (claimId) {
return (claimId && (claimId.length === 40));
}
function isShortClaimId (claimId) {
return (claimId && (claimId.length < 40));
}
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
const Claim = sequelize.define(
@ -265,6 +275,27 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
});
};
Claim.validateLongClaimId = function (name, claimId) {
return new Promise((resolve, reject) => {
this.findOne({
where: {
name,
claimId,
},
})
.then(result => {
if (!result) {
return reject(NO_CLAIM);
}
resolve(claimId);
})
.catch(error => {
logger.error(error);
reject(NO_CLAIM);
});
});
};
Claim.getLongClaimIdFromShortClaimId = function (name, shortId) {
return new Promise((resolve, reject) => {
this
@ -279,13 +310,14 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
.then(result => {
switch (result.length) {
case 0:
return resolve(null);
default: // note results must be sorted
return reject(NO_CLAIM);
default:
return resolve(result[0].claimId);
}
})
.catch(error => {
reject(error);
logger.error(error);
reject(NO_CLAIM);
});
});
};
@ -295,48 +327,31 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
this
.findAll({
where: { name },
order: [['effectiveAmount', 'DESC'], ['height', 'ASC']], // note: maybe height and effective amount need to switch?
order: [['effectiveAmount', 'DESC'], ['height', 'ASC']],
})
.then(result => {
logger.debug('length of result', result.length);
switch (result.length) {
case 0:
return resolve(null);
return reject(NO_CLAIM);
default:
return resolve(result[0].dataValues.claimId);
}
})
.catch(error => {
reject(error);
});
});
};
Claim.validateLongClaimId = function (name, claimId) {
return new Promise((resolve, reject) => {
this.findOne({
where: {name, claimId},
})
.then(result => {
if (!result) {
return resolve(null);
};
resolve(claimId);
})
.catch(error => {
reject(error);
logger.error(error);
reject(NO_CLAIM);
});
});
};
Claim.getLongClaimId = function (claimName, claimId) {
logger.debug(`getLongClaimId(${claimName}, ${claimId})`);
if (claimId && (claimId.length === 40)) { // if a full claim id is provided
// logger.debug(`getLongClaimId(${claimName}, ${claimId})`);
if (isLongClaimId(claimId)) {
return this.validateLongClaimId(claimName, claimId);
} else if (claimId && claimId.length < 40) {
return this.getLongClaimIdFromShortClaimId(claimName, claimId); // if a short claim id is provided
} else if (isShortClaimId(claimId)) {
return this.getLongClaimIdFromShortClaimId(claimName, claimId);
} else {
return this.getTopFreeClaimIdByClaimName(claimName); // if no claim id is provided
return this.getTopFreeClaimIdByClaimName(claimName);
}
};

View file

@ -4,6 +4,7 @@ const Claim = require('./claim.js');
const File = require('./file.js');
const Request = require('./request.js');
const User = require('./user.js');
const Blocked = require('./blocked.js');
const Sequelize = require('sequelize');
const logger = require('winston');
@ -42,6 +43,7 @@ db['Claim'] = sequelize.import('Claim', Claim);
db['File'] = sequelize.import('File', File);
db['Request'] = sequelize.import('Request', Request);
db['User'] = sequelize.import('User', User);
db['Blocked'] = sequelize.import('Blocked', Blocked);
// run model.association for each model in the db object that has an association
logger.info('associating db models...');

View file

@ -0,0 +1,25 @@
const returnShortId = (claimsArray, longId) => {
let claimIndex;
let shortId = longId.substring(0, 1); // default short id is the first letter
let shortIdLength = 0;
// find the index of this claim id
claimIndex = claimsArray.findIndex(element => {
return element.claimId === longId;
});
if (claimIndex < 0) {
throw new Error('claim id not found in claims list');
}
// get an array of all claims with lower height
let possibleMatches = claimsArray.slice(0, claimIndex);
// remove certificates with the same prefixes until none are left.
while (possibleMatches.length > 0) {
shortIdLength += 1;
shortId = longId.substring(0, shortIdLength);
possibleMatches = possibleMatches.filter(element => {
return (element.claimId && (element.claimId.substring(0, shortIdLength) === shortId));
});
}
return shortId;
};
module.exports = returnShortId;

View file

@ -1,33 +0,0 @@
const { getClaimId } = require('../../controllers/serveController.js');
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM';
/*
route to get a long claim id
*/
const claimLongId = ({ ip, originalUrl, body, params }, res) => {
const channelName = body.channelName;
const channelClaimId = body.channelClaimId;
const claimName = body.claimName;
const claimId = body.claimId;
getClaimId(channelName, channelClaimId, claimName, claimId)
.then(result => {
if (result === NO_CHANNEL) {
return res.status(404).json({success: false, message: 'No matching channel could be found'});
}
if (result === NO_CLAIM) {
return res.status(404).json({success: false, message: 'No matching claim id could be found'});
}
res.status(200).json({success: true, data: result});
})
.catch(error => {
handleErrorResponse(originalUrl, ip, error, res);
});
};
module.exports = claimLongId;

View file

@ -1,18 +1,19 @@
const channelAvailability = require('./channelAvailability');
const channelClaims = require('./channelClaims');
const channelData = require('./channelData');
const channelShortId = require('./channelShortId');
const claimAvailability = require('./claimAvailability');
const claimData = require('./claimData');
const claimGet = require('./claimGet');
const claimLongId = require('./claimLongId');
const claimPublish = require('./claimPublish');
const claimResolve = require('./claimResolve');
const claimShortId = require('./claimShortId');
const claimList = require('./claimList');
const fileAvailability = require('./fileAvailability');
const channelAvailability = require('../../controllers/api/channel/availability');
const channelClaims = require('../../controllers/api/channel/claims');
const channelData = require('../../controllers/api/channel/data');
const channelShortId = require('../../controllers/api/channel/shortId');
const claimAvailability = require('../../controllers/api/claim/availability');
const claimBlockedList = require('../../controllers/api/claim/blockedList');
const claimData = require('../../controllers/api/claim/data/');
const claimGet = require('../../controllers/api/claim/get');
const claimList = require('../../controllers/api/claim/list');
const claimLongId = require('../../controllers/api/claim/longId');
const claimPublish = require('../../controllers/api/claim/publish');
const claimResolve = require('../../controllers/api/claim/resolve');
const claimShortId = require('../../controllers/api/claim/shortId');
const fileAvailability = require('../../controllers/api/file/availability');
const multipartMiddleware = require('../../helpers/multipartMiddleware');
const multipartMiddleware = require('../utils/multipartMiddleware');
module.exports = (app) => {
// channel routes
@ -21,14 +22,15 @@ module.exports = (app) => {
app.get('/api/channel/data/:channelName/:channelClaimId', channelData);
app.get('/api/channel/claims/:channelName/:channelClaimId/:page', channelClaims);
// claim routes
app.get('/api/claim/list/:name', claimList);
app.get('/api/claim/get/:name/:claimId', claimGet);
app.get('/api/claim/availability/:name', claimAvailability);
app.get('/api/claim/resolve/:name/:claimId', claimResolve);
app.post('/api/claim/publish', multipartMiddleware, claimPublish);
app.get('/api/claim/short-id/:longId/:name', claimShortId);
app.post('/api/claim/long-id', claimLongId);
app.get('/api/claim/blocked-list/', claimBlockedList);
app.get('/api/claim/data/:claimName/:claimId', claimData);
app.get('/api/claim/get/:name/:claimId', claimGet);
app.get('/api/claim/list/:name', claimList);
app.post('/api/claim/long-id', claimLongId);
app.post('/api/claim/publish', multipartMiddleware, claimPublish);
app.get('/api/claim/resolve/:name/:claimId', claimResolve);
app.get('/api/claim/short-id/:longId/:name', claimShortId);
// file routes
app.get('/api/file/availability/:name/:claimId', fileAvailability);
};

View file

@ -1,5 +1,5 @@
const serveAssetByClaim = require('./serveAssetByClaim');
const serveAssetByIdentifierAndClaim = require('./serveAssetByIdentifierAndClaim');
const serveAssetByClaim = require('../../controllers/assets/serveByClaim');
const serveAssetByIdentifierAndClaim = require('../../controllers/assets/serveByIdentifierAndClaim');
module.exports = (app, db) => {
app.get('/:identifier/:claim', serveAssetByIdentifierAndClaim);

View file

@ -1,8 +1,8 @@
const speechPassport = require('../../speechPassport');
const handleSignupRequest = require('./signup');
const handleLoginRequest = require('./login');
const handleLogoutRequest = require('./logout');
const handleUserRequest = require('./user');
const handleSignupRequest = require('../../controllers/auth/signup');
const handleLoginRequest = require('../../controllers/auth/login');
const handleLogoutRequest = require('../../controllers/auth/logout');
const handleUserRequest = require('../../controllers/auth/user');
module.exports = (app) => {
app.post('/signup', speechPassport.authenticate('local-signup'), handleSignupRequest);

View file

@ -1,4 +1,4 @@
const handlePageRequest = require('./sendReactApp');
const handlePageRequest = require('../../controllers/pages/sendReactApp');
module.exports = (app) => {
app.get('*', handlePageRequest);

View file

@ -1,6 +1,6 @@
const handlePageRequest = require('./sendReactApp');
const handleEmbedRequest = require('./sendEmbedPage');
const redirect = require('./redirect');
const handlePageRequest = require('../../controllers/pages/sendReactApp');
const handleEmbedRequest = require('../../controllers/pages/sendEmbedPage');
const redirect = require('../../controllers/utils/redirect');
module.exports = (app) => {
app.get('/', handlePageRequest);

View file

@ -1,7 +0,0 @@
const handlePageRender = require('../../render/build/handlePageRender.js');
const sendReactApp = (req, res) => {
handlePageRender(req, res);
};
module.exports = sendReactApp;

View file

@ -1,5 +1,5 @@
const multipart = require('connect-multiparty');
const { publishing: { uploadDirectory } } = require('../../config/siteConfig.js');
const { publishing: { uploadDirectory } } = require('../../../config/siteConfig.js');
const multipartMiddleware = multipart({uploadDir: uploadDirectory});
module.exports = multipartMiddleware;

View file

@ -1,10 +1,11 @@
const passport = require('passport');
const localLoginStrategy = require('./local-login.js');
const localSignupStrategy = require('./local-signup.js');
const { serializeSpeechUser, deserializeSpeechUser } = require('../helpers/authHelpers.js');
const localLoginStrategy = require('./utils/local-login.js');
const localSignupStrategy = require('./utils/local-signup.js');
const serializeUser = require('./utils/serializeUser.js');
const deserializeUser = require('./utils/deserializeUser.js');
passport.deserializeUser(deserializeSpeechUser);
passport.serializeUser(serializeSpeechUser);
passport.deserializeUser(deserializeUser);
passport.serializeUser(serializeUser);
passport.use('local-login', localLoginStrategy);
passport.use('local-signup', localSignupStrategy);

View file

@ -0,0 +1,7 @@
const deserializeUser = (user, done) => {
// deserializes session and populates additional info to req.user
console.log('deserializing user');
done(null, user);
};
module.exports = deserializeUser;

View file

@ -1,6 +1,6 @@
const PassportLocalStrategy = require('passport-local').Strategy;
const logger = require('winston');
const db = require('../models');
const db = require('../../models');
const returnUserAndChannelInfo = (userInstance) => {
return new Promise((resolve, reject) => {

View file

@ -1,7 +1,7 @@
const PassportLocalStrategy = require('passport-local').Strategy;
const lbryApi = require('../helpers/lbryApi.js');
const { createChannel } = require('../../lbrynet');
const logger = require('winston');
const db = require('../models');
const db = require('../../models');
module.exports = new PassportLocalStrategy(
{
@ -14,7 +14,7 @@ module.exports = new PassportLocalStrategy(
// server-side validaton of inputs (username, password)
// create the channel and retrieve the metadata
return lbryApi.createChannel(`@${username}`)
return createChannel(`@${username}`)
.then(tx => {
// create user record
const userData = {

View file

@ -0,0 +1,7 @@
const serializeUser = (user, done) => {
// returns user data to be serialized into session
console.log('serializing user');
done(null, user);
};
module.exports = serializeUser;

View file

@ -1,27 +1,34 @@
const chai = require('chai');
const expect = chai.expect;
describe('publishHelpers.js', function () {
const publishHelpers = require('../../server/helpers/publishHelpers.js');
describe('publish utils', function () {
describe('#parsePublishApiRequestBody()', function () {
const parsePublishApiRequestBody = require('../../../server/controllers/api/claim/publish/parsePublishApiRequestBody.js');
it('should throw an error if no body', function () {
expect(publishHelpers.parsePublishApiRequestBody.bind(this, null)).to.throw();
expect(parsePublishApiRequestBody.bind(this, null)).to.throw();
});
it('should throw an error if no body.name', function () {
const bodyNoName = {};
expect(publishHelpers.parsePublishApiRequestBody.bind(this, bodyNoName)).to.throw();
expect(parsePublishApiRequestBody.bind(this, bodyNoName)).to.throw();
});
});
describe('#parsePublishApiRequestFiles()', function () {
const parsePublishApiRequestFiles = require('../../../server/controllers/api/claim/publish/parsePublishApiRequestFiles.js');
it('should throw an error if no files', function () {
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, null)).to.throw();
expect(parsePublishApiRequestFiles.bind(this, null)).to.throw();
});
it('should throw an error if no files.file', function () {
const filesNoFile = {};
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesNoFile)).to.throw();
expect(parsePublishApiRequestFiles.bind(this, filesNoFile)).to.throw();
});
it('should throw an error if file.size is too large', function () {
const filesTooBig = {
file: {
@ -31,8 +38,9 @@ describe('publishHelpers.js', function () {
size: 10000001,
},
};
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesTooBig)).to.throw();
expect(parsePublishApiRequestFiles.bind(this, filesTooBig)).to.throw();
});
it('should throw error if not an accepted file type', function () {
const filesWrongType = {
file: {
@ -42,8 +50,9 @@ describe('publishHelpers.js', function () {
size: 10000000,
},
};
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesWrongType)).to.throw();
expect(parsePublishApiRequestFiles.bind(this, filesWrongType)).to.throw();
});
it('should throw NO error if no problems', function () {
const filesNoProblems = {
file: {
@ -53,7 +62,7 @@ describe('publishHelpers.js', function () {
size: 10000000,
},
};
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesNoProblems)).to.not.throw();
expect(parsePublishApiRequestFiles.bind(this, filesNoProblems)).to.not.throw();
});
});