412 blocked content #428
2
index.js
|
@ -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({
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
|
@ -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;
|
|
@ -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');
|
||||
|
||||
/*
|
||||
|
|
@ -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) {
|
29
server/controllers/api/channel/claims/getChannelClaims.js
Normal 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;
|
|
@ -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);
|
||||
});
|
||||
};
|
28
server/controllers/api/channel/data/getChannelData.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
good catch. I updated so any specific errors will get thrown (like 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.
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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');
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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) => {
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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) => {
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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;
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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)
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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)
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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 => {
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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;
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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);
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
})
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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 => {
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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({
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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,
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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,
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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,
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
});
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
})
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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 => {
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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);
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
});
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
});
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
};
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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.
|
||||
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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;
|
||||
You could just return an empty array, unless you specifically need You could just return an empty array, unless you specifically need `xxx[3] === null`
good catch. I updated so any specific errors will get thrown (like 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,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);
|
||||
});
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
|
||||
const db = require('../../models');
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
const db = require('../../../../models');
|
||||
|
||||
/*
|
||||
|
|
@ -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;
|
|
@ -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());
|
56
server/controllers/api/claim/blockedList/index.js
Normal 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')
|
||||
This is probably because I don't understand This is probably because I don't understand `upsert` that well but why `blocked, blocked`?
the second argument is the content to insert and the third argument is the conditional for the 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;
|
|
@ -1,5 +1,5 @@
|
|||
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
|
||||
const db = require('../../models');
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
const db = require('../../../../models');
|
||||
|
||||
/*
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = (fileInfo, getResult) => {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
return fileInfo;
|
||||
};
|
13
server/controllers/api/claim/get/createFileData.js
Normal 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,
|
||||
};
|
||||
};
|
|
@ -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');
|
||||
|
||||
/*
|
||||
|
|
@ -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');
|
||||
|
||||
/*
|
||||
|
31
server/controllers/api/claim/longId/getClaimId.js
Normal 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;
|
53
server/controllers/api/claim/longId/index.js
Normal 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;
|
75
server/controllers/api/claim/publish/authentication.js
Normal 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;
|
|
@ -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;
|
|
@ -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;
|
13
server/controllers/api/claim/publish/deleteFile.js
Normal 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;
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
|
@ -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;
|
92
server/controllers/api/claim/publish/publish.js
Normal 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;
|
|
@ -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;
|
|
@ -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');
|
||||
|
||||
/*
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
|
||||
const db = require('../../models');
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
const db = require('../../../../models');
|
||||
|
||||
/*
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
const { handleErrorResponse } = require('../../helpers/errorHandlers.js');
|
||||
const db = require('../../models');
|
||||
const { handleErrorResponse } = require('../../../utils/errorHandlers.js');
|
||||
const db = require('../../../../models');
|
||||
|
||||
/*
|
||||
|
|
@ -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';
|
||||
|
||||
/*
|
|
@ -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);
|
37
server/controllers/assets/utils/determineResponseType.js
Normal 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;
|
23
server/controllers/assets/utils/flipClaimNameAndId.js
Normal 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;
|
58
server/controllers/assets/utils/getClaimIdAndServeAsset.js
Normal 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;
|
15
server/controllers/assets/utils/getLocalFileRecord.js
Normal 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;
|
10
server/controllers/assets/utils/logRequestData.js
Normal 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;
|
14
server/controllers/assets/utils/serveFile.js
Normal 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;
|
|
@ -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) => {
|
|
@ -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;
|
||||
});
|
||||
},
|
||||
};
|
|
@ -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;
|
||||
});
|
||||
},
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
|
@ -21,8 +21,8 @@ module.exports = {
|
|||
message = error.message;
|
||||
} else {
|
||||
message = error;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
return [status, message];
|
||||
},
|
||||
useObjectPropertiesIfNoKeys: function (err) {
|
|
@ -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);
|
||||
},
|
||||
};
|
|
@ -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]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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,
|
||||
};
|
||||
},
|
||||
};
|
|
@ -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;
|
||||
},
|
||||
};
|
|
@ -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);
|
||||
},
|
||||
};
|
|
@ -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);
|
||||
});
|
||||
},
|
||||
};
|
|
@ -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,
|
19
server/lbrynet/utils/handleLbrynetResponse.js
Normal 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
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -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...');
|
||||
|
|
25
server/models/utils/returnShortId.js
Normal 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;
|
|
@ -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;
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const handlePageRequest = require('./sendReactApp');
|
||||
const handlePageRequest = require('../../controllers/pages/sendReactApp');
|
||||
|
||||
module.exports = (app) => {
|
||||
app.get('*', handlePageRequest);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
const handlePageRender = require('../../render/build/handlePageRender.js');
|
||||
|
||||
const sendReactApp = (req, res) => {
|
||||
handlePageRender(req, res);
|
||||
};
|
||||
|
||||
module.exports = sendReactApp;
|
|
@ -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;
|
|
@ -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);
|
||||
|
||||
|
|
7
server/speechPassport/utils/deserializeUser.js
Normal 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;
|
|
@ -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) => {
|
|
@ -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 = {
|
7
server/speechPassport/utils/serializeUser.js
Normal 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;
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
You could just return an empty array, unless you specifically need
xxx[3] === null