412 blocked content #428

Merged
bones7242 merged 13 commits from 412-blocked_content into master 2018-05-01 01:07:59 +02:00
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 @@
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
const db = require('../../../../models');
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
const getChannelData = (channelName, channelClaimId) => {
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
return new Promise((resolve, reject) => {
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
let longChannelClaimId;
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
// 1. get the long channel Id (make sure channel exists)
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
db.Certificate
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.getLongChannelId(channelName, channelClaimId)
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.then(fullClaimId => {
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
longChannelClaimId = fullClaimId;
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
return db
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.Certificate
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.getShortChannelIdFromLongChannelId(fullClaimId, channelName);
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
})
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.then(shortChannelClaimId => {
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
resolve({
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
channelName,
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
longChannelClaimId,
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
shortChannelClaimId,
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
});
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
})
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
.catch(error => {
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
reject(error);
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
});
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
});
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
};
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.
module.exports = getChannelData;
neb-b commented 2018-04-30 05:11:42 +02:00 (Migrated from github.com)
Review

You could just return an empty array, unless you specifically need xxx[3] === null

You could just return an empty array, unless you specifically need `xxx[3] === null`
bones7242 commented 2018-05-01 01:07:45 +02:00 (Migrated from github.com)
Review

good catch. I updated so any specific errors will get thrown (like NO_CHANNEL), like I did with the claims controllers. That streamlines this chain a lot better.

good catch. I updated so any specific errors will get thrown (like `NO_CHANNEL`), like I did with the claims controllers. That streamlines this chain a lot better.

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')
neb-b commented 2018-04-30 05:13:48 +02:00 (Migrated from github.com)
Review

This is probably because I don't understand upsert that well but why blocked, blocked?

This is probably because I don't understand `upsert` that well but why `blocked, blocked`?
bones7242 commented 2018-05-01 00:10:42 +02:00 (Migrated from github.com)
Review

the second argument is the content to insert and the third argument is the conditional for the upsert. So in this case, update/insert the blocked record based on whether that exact blocked record already exists . In this call to upsert it is duplicative to have two separate arguments, but in other uses it is helpful to have search criteria that doesn't match the inserted criteria specifically. I.e. upsert blocked where {claimId: 'abc123...xyz}

the second argument is the content to insert and the third argument is the conditional for the `upsert`. So in this case, update/insert the `blocked` record based on whether that exact `blocked` record already exists . In this call to `upsert` it is duplicative to have two separate arguments, but in other uses it is helpful to have search criteria that doesn't match the inserted criteria specifically. I.e. upsert `blocked` where `{claimId: 'abc123...xyz}`
}
})
.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();
});
});