Merge branch 'switch-url-structure'

This commit is contained in:
bill bittner 2017-08-04 11:55:48 -07:00
commit a44745d3e9
23 changed files with 228 additions and 363 deletions

View file

@ -1,26 +1,10 @@
const logger = require('winston'); const logger = require('winston');
const db = require('../models'); const db = require('../models');
const lbryApi = require('../helpers/libraries/lbryApi.js'); const lbryApi = require('../helpers/lbryApi.js');
const publishHelpers = require('../helpers/libraries/publishHelpers.js'); const publishHelpers = require('../helpers/publishHelpers.js');
function upsert (Model, values, condition) {
return Model
.findOne({ where: condition })
.then(function (obj) {
if (obj) { // update
logger.silly(`updating ${values.name}:${values.claimId} in File db`);
return obj.update(values);
} else { // insert
logger.silly(`creating ${values.name}:${values.claimId} in File db`);
return Model.create(values);
}
}).catch(function (error) {
logger.error('Sequelize findOne error', error);
});
}
function checkNameAvailability (name) { function checkNameAvailability (name) {
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// find any records where the name is used // find any records where the name is used
db.File db.File
.findAll({ where: { name } }) .findAll({ where: { name } })
@ -50,12 +34,11 @@ function checkNameAvailability (name) {
reject(error); reject(error);
}); });
}); });
return deferred;
}; };
module.exports = { module.exports = {
publish (publishParams, fileName, fileType) { publish (publishParams, fileName, fileType) {
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 1. make sure the name is available // 1. make sure the name is available
checkNameAvailability(publishParams.name) checkNameAvailability(publishParams.name)
.then(result => { .then(result => {
@ -66,7 +49,7 @@ module.exports = {
.then(result => { .then(result => {
logger.info(`Successfully published ${fileName}`, result); logger.info(`Successfully published ${fileName}`, result);
// 3. update old record or create new one (update is in case the claim has been published before by this daemon) // 3. update old record or create new one (update is in case the claim has been published before by this daemon)
upsert( db.upsert(
db.File, db.File,
{ {
name : publishParams.name, name : publishParams.name,
@ -108,7 +91,6 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
checkNameAvailability (name) { checkNameAvailability (name) {
return checkNameAvailability(name); return checkNameAvailability(name);

View file

@ -1,15 +1,9 @@
const lbryApi = require('../helpers/libraries/lbryApi.js'); const lbryApi = require('../helpers/lbryApi.js');
const db = require('../models'); const db = require('../models');
const logger = require('winston'); const logger = require('winston');
const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js'); const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js');
const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js'); const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js');
const serveHelpers = require('../helpers/libraries/serveHelpers.js'); const serveHelpers = require('../helpers/serveHelpers.js');
// function checkForLocalAssetByShortUrl (shortUrl, name) {
// }
// function checkForLocalAssetByChannel (channelName, name) {
// }
function checkForLocalAssetByClaimId (claimId, name) { function checkForLocalAssetByClaimId (claimId, name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -48,11 +42,6 @@ function getAssetByClaimId (fullClaimId, name) {
// 2. if a result was found, resolve the result // 2. if a result was found, resolve the result
if (dataValues) { if (dataValues) {
logger.debug('found a local file for this claimId'); logger.debug('found a local file for this claimId');
// trigger an update check for the file
/*
serveHelpers.updateFileIfNeeded(dataValues.name, dataValues.claimId, dataValues.outpoint, dataValues.height);
*/
// resolve promise
resolve(dataValues); resolve(dataValues);
// 2. if not found locally, make a get request // 2. if not found locally, make a get request
} else { } else {
@ -67,6 +56,9 @@ function getAssetByClaimId (fullClaimId, name) {
let fileInfo = formatGetResultsToFileInfo(getResult); let fileInfo = formatGetResultsToFileInfo(getResult);
fileInfo['address'] = resolveResult.claim.address; fileInfo['address'] = resolveResult.claim.address;
fileInfo['height'] = resolveResult.claim.height; fileInfo['height'] = resolveResult.claim.height;
// insert a record in the File table
db.File.create(fileInfo);
// resolve the promise
resolve(fileInfo); resolve(fileInfo);
}) })
.catch(error => { .catch(error => {
@ -91,16 +83,16 @@ function getAssetByClaimId (fullClaimId, name) {
module.exports = { module.exports = {
getAssetByChannel (channelName, name) { getAssetByChannel (channelName, name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// temporarily throw error
reject(new Error('channel names are not currently supported'));
// get the claim id // get the claim id
// get teh asset by claim Id // get teh asset by claim Id
}); });
}, },
getAssetByShortUrl: function (shortUrl, name) { getAssetByShortUrl: function (shortUrl, name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// get the full claimId // get the full claimId
serveHelpers.getClaimIdByShortUrl(shortUrl, name) serveHelpers.getClaimIdFromShortUrl(shortUrl, name)
// get the asset by the claimId // get the asset by the claimId
.then(claimId => { .then(claimId => {
resolve(getAssetByClaimId(claimId, name)); resolve(getAssetByClaimId(claimId, name));
@ -113,45 +105,25 @@ module.exports = {
getAssetByClaimId (fullClaimId, name) { getAssetByClaimId (fullClaimId, name) {
return getAssetByClaimId(fullClaimId, name); return getAssetByClaimId(fullClaimId, name);
}, },
serveClaimByName (claimName) { getAssetByName (name) {
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 1. get the top free, public claims // 1. get a list of the free public claims
getAllFreePublicClaims(claimName) getAllFreePublicClaims(name)
// 2. check locally for the top claim
.then(freePublicClaimList => { .then(freePublicClaimList => {
// check to make sure some claims were found // if no claims were found, return null
if (!freePublicClaimList) { if (!freePublicClaimList) {
resolve(null); resolve(null);
return; return;
} }
const name = freePublicClaimList[0].name; // parse the result
const claimId = freePublicClaimList[0].claim_id; const claimId = freePublicClaimList[0].claim_id;
const uri = `${name}#${claimId}`; // get the asset
const height = freePublicClaimList[0].height; resolve(getAssetByClaimId(claimId, name));
const address = freePublicClaimList[0].address;
// 2. check to see if the file is available locally
db.File
.findOne({ where: { name, claimId } })
.then(claim => {
// 3. if a matching record is found locally, serve it
if (claim) {
// serve the file
resolve(claim.dataValues);
// trigger update if needed
serveHelpers.updateFileIfNeeded(uri, claim.dataValues.outpoint, claim.dataValues.height);
// 3. otherwise use daemon to retrieve it
} else {
// get the claim and serve it
serveHelpers.getClaimAndHandleResponse(uri, address, height, resolve, reject);
}
})
.catch(error => {
reject(error);
});
}) })
.catch(error => { .catch(error => {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
}; };

View file

@ -1,162 +0,0 @@
const lbryApi = require('../helpers/libraries/lbryApi.js');
const db = require('../models');
const logger = require('winston');
const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js');
const isFreePublicClaim = require('../helpers/functions/isFreePublicClaim.js');
const serveHelpers = require('../helpers/libraries/serveHelpers.js');
module.exports = {
showClaimByName (claimName) {
const deferred = new Promise((resolve, reject) => {
// 1. get the top free, public claims
getAllFreePublicClaims(claimName)
.then(freePublicClaimList => {
// check to make sure some claims were found
if (!freePublicClaimList) {
resolve(null);
return;
}
const name = freePublicClaimList[0].name;
const claimId = freePublicClaimList[0].claim_id;
const uri = `${name}#${claimId}`;
const height = freePublicClaimList[0].height;
const address = freePublicClaimList[0].address;
// 2. check to see if the file is available locally
db.File
.findOne({ where: { name, claimId } })
.then(result => {
// 3. if a matching record is found locally, serve it
if (result) {
// return the data for the file to be served
result.dataValues['fileExt'] = result.fileName.substring(result.fileName.lastIndexOf('.'));
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
// trigger update if needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.height);
// 3. otherwise use daemon to retrieve it
} else {
// get the claim and serve it
serveHelpers.getClaimAndHandleResponse(uri, address, height, resolve, reject);
}
})
.catch(error => {
reject(error);
});
})
.catch(error => {
reject(error);
});
});
return deferred;
},
showClaimByClaimId (name, claimId) {
logger.debug(`Getting claim name: ${name} by claimid: ${claimId}`);
const deferred = new Promise((resolve, reject) => {
// 1. check locally for the claim
const uri = `${name}#${claimId}`;
db.File
.findOne({ where: { name, claimId } })
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
logger.debug('local result found');
// return the data for the file to be served
result.dataValues['fileExt'] = result.fileName.substring(result.fileName.lastIndexOf('.'));
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
// update the file, as needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
logger.debug('no local result found');
lbryApi
.resolveUri(uri)
.then(result => {
logger.debug('resolve returned successfully');
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
serveHelpers.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
logger.debug('get request returned');
serveHelpers.getShortUrlByClaimId(name, claimId)
.then(shortUrl => {
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null, null);
}
})
.catch(error => {
logger.debug('resolve returned an error');
reject(error);
});
}
})
.catch(error => reject(error));
});
return deferred;
},
showClaimByShortUrl (name, shortUrl) {
const deferred = new Promise((resolve, reject) => {
let uri;
let claimId;
// 1. validate the claim id & retrieve the full claim id if needed
serveHelpers.getClaimIdByShortUrl(name, shortUrl)
.then(result => {
// 2. check locally for the claim
uri = `${name}#${result}`;
claimId = result;
return db.File.findOne({ where: { name, claimId } });
})
.then(result => {
// 3. if a match is found locally, serve that claim
if (result) {
// return the data for the file to be served
result.dataValues['fileExt'] = result.fileName.substring(result.fileName.lastIndexOf('.'));
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
// update the file, as needed
serveHelpers.updateFileIfNeeded(uri, result.dataValues.outpoint, result.dataValues.outpoint);
// 3. if a match was not found locally, use the daemon to retrieve the claim & return the db data once it is created
} else {
lbryApi
.resolveUri(uri)
.then(result => {
if (result.claim && isFreePublicClaim(result.claim)) { // check to see if the claim is free & public
// get claim and serve
serveHelpers.getClaimAndReturnResponse(uri, result.claim.address, result.claim.height)
.then(result => {
logger.debug('returned');
result.dataValues['shortUrl'] = shortUrl;
resolve(result.dataValues);
})
.catch(error => reject(error));
} else {
logger.debug('Resolve did not return a free, public claim');
resolve(null, null);
}
})
.catch(error => reject(error));
}
})
.catch(error => reject(error));
});
return deferred;
},
showAllClaims (claimName) {
return getAllFreePublicClaims(claimName);
},
};

View file

@ -42,7 +42,7 @@ module.exports = {
const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true }); const visitor = ua(googleApiKey, visitorId, { strictCidFormat: false, https: true });
let params; let params;
switch (action) { switch (action) {
case 'serve': case 'SERVE':
params = { params = {
ec : 'serve', ec : 'serve',
ea : originalUrl, ea : originalUrl,
@ -51,7 +51,7 @@ module.exports = {
ul : headers['accept-language'], ul : headers['accept-language'],
}; };
break; break;
case 'publish': case 'PUBLISH':
params = { params = {
ec : 'publish', ec : 'publish',
ea : originalUrl, ea : originalUrl,
@ -70,7 +70,7 @@ module.exports = {
}, },
getStatsSummary (startDate) { getStatsSummary (startDate) {
logger.debug('retrieving request records'); logger.debug('retrieving request records');
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// get the raw Requests data // get the raw Requests data
db.Request db.Request
.findAll({ .findAll({
@ -140,7 +140,6 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
getTrendingClaims (startDate) { getTrendingClaims (startDate) {
logger.debug('retrieving trending requests'); logger.debug('retrieving trending requests');

View file

@ -1,5 +1,5 @@
const logger = require('winston'); const logger = require('winston');
const { postToStats } = require('../../controllers/statsController.js'); const { postToStats } = require('../controllers/statsController.js');
module.exports = { module.exports = {
handleRequestError (action, originalUrl, ip, error, res) { handleRequestError (action, originalUrl, ip, error, res) {

View file

@ -1,5 +1,5 @@
const isFreePublicClaim = require('./isFreePublicClaim.js'); const isFreePublicClaim = require('./isFreePublicClaim.js');
const lbryApi = require('../libraries/lbryApi.js'); const lbryApi = require('../lbryApi.js');
const logger = require('winston'); const logger = require('winston');
function filterForFreePublicClaims (claimsListArray) { function filterForFreePublicClaims (claimsListArray) {
@ -29,7 +29,7 @@ function orderClaims (claimsListArray) {
} }
module.exports = (claimName) => { module.exports = (claimName) => {
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// make a call to the daemon to get the claims list // make a call to the daemon to get the claims list
lbryApi lbryApi
.getClaimsList(claimName) .getClaimsList(claimName)
@ -58,5 +58,4 @@ module.exports = (claimName) => {
reject(error); reject(error);
}); });
}); });
return deferred;
}; };

View file

@ -3,8 +3,8 @@ const logger = require('winston');
module.exports = { module.exports = {
getWalletList () { getWalletList () {
logger.debug('getting wallet list'); logger.debug('lbryApi >> getting wallet list');
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'wallet_list', method: 'wallet_list',
@ -17,11 +17,10 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
publishClaim (publishParams) { publishClaim (publishParams) {
logger.debug(`Publishing claim to "${publishParams.name}"`); logger.debug(`lbryApi >> Publishing claim to "${publishParams.name}"`);
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'publish', method: 'publish',
@ -35,11 +34,10 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
getClaim (uri) { getClaim (uri) {
logger.debug(`Getting Claim for "${uri}"`); logger.debug(`lbryApi >> Getting Claim for "${uri}"`);
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'get', method: 'get',
@ -59,15 +57,13 @@ module.exports = {
resolve(data.result); resolve(data.result);
}) })
.catch(error => { .catch(error => {
logger.debug("axios.post 'get' error");
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
getClaimsList (claimName) { getClaimsList (claimName) {
logger.debug(`Getting Claim List for "${claimName}"`); logger.debug(`lbryApi >> Getting claim_list for "${claimName}"`);
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'claim_list', method: 'claim_list',
@ -80,11 +76,10 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
resolveUri (uri) { resolveUri (uri) {
logger.debug(`Resolving URI for "${uri}"`); logger.debug(`lbryApi >> Resolving URI for "${uri}"`);
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'resolve', method: 'resolve',
@ -101,11 +96,10 @@ module.exports = {
reject(error); reject(error);
}); });
}); });
return deferred;
}, },
getDownloadDirectory () { getDownloadDirectory () {
logger.debug('Retrieving the download directory path from lbry daemon...'); logger.debug('lbryApi >> Retrieving the download directory path from lbry daemon...');
const deferred = new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios axios
.post('http://localhost:5279/lbryapi', { .post('http://localhost:5279/lbryapi', {
method: 'settings_get', method: 'settings_get',
@ -118,9 +112,9 @@ module.exports = {
} }
}) })
.catch((error) => { .catch((error) => {
reject(error); logger.error('Unable to retrieve daemon download directory. Restart spee.ch once the daemon is ready. Using default "/home/lbry/Downloads".', error);
resolve('/home/lbry/Downloads/');
}); });
}); });
return deferred;
}, },
}; };

View file

@ -1,5 +1,5 @@
const logger = require('winston'); const logger = require('winston');
const db = require('../../models'); const db = require('../models');
const lbryApi = require('./lbryApi'); const lbryApi = require('./lbryApi');
function determineShortUrl (claimId, claimList) { function determineShortUrl (claimId, claimList) {
@ -10,6 +10,7 @@ function determineShortUrl (claimId, claimList) {
claimList = claimList.filter(claim => { // remove this claim from the claim list claimList = claimList.filter(claim => { // remove this claim from the claim list
return claim.claim_id !== claimId; return claim.claim_id !== claimId;
}); });
logger.debug('claim list length:', claimList.length);
if (claimList.length === 0) { // if there are no other claims, return the first letter of the claim id if (claimList.length === 0) { // if there are no other claims, return the first letter of the claim id
return claimId.substring(0, 1); return claimId.substring(0, 1);
} else { } else {
@ -91,9 +92,9 @@ module.exports = {
logger.debug('showing file lite'); logger.debug('showing file lite');
res.status(200).render('showLite', { layout: 'show', fileInfo }); res.status(200).render('showLite', { layout: 'show', fileInfo });
}, },
getClaimIdByShortUrl (shortUrl, name) { getClaimIdFromShortUrl (shortUrl, name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
logger.debug('getting claims list from lbrynet'); logger.debug('getting claim_id from short url');
// use the daemon to check for claims list // use the daemon to check for claims list
lbryApi.getClaimsList(name) lbryApi.getClaimsList(name)
.then(({ claims }) => { .then(({ claims }) => {
@ -112,7 +113,6 @@ module.exports = {
const filteredClaimsList = claims.filter(claim => { const filteredClaimsList = claims.filter(claim => {
return regex.test(claim.claim_id); return regex.test(claim.claim_id);
}); });
logger.debug('filtered claims list', filteredClaimsList);
switch (filteredClaimsList.length) { switch (filteredClaimsList.length) {
case 0: case 0:
reject(new Error('That is an invalid short url')); reject(new Error('That is an invalid short url'));
@ -133,8 +133,9 @@ module.exports = {
}); });
}); });
}, },
getShortUrlFromClaimId (name, claimId) { getShortUrlFromClaimId (claimId, name) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
logger.debug('finding short url from claim_id');
// get a list of all the claims // get a list of all the claims
lbryApi.getClaimsList(name) lbryApi.getClaimsList(name)
// find the smallest possible unique url for this claim // find the smallest possible unique url for this claim

View file

@ -37,6 +37,22 @@ Object.keys(db).forEach(modelName => {
} }
}); });
db['upsert'] = (Model, values, condition) => {
return Model
.findOne({ where: condition })
.then(function (obj) {
if (obj) { // update
logger.silly(`updating ${values.name}:${values.claimId} in File db`);
return obj.update(values);
} else { // insert
logger.silly(`creating ${values.name}:${values.claimId} in File db`);
return Model.create(values);
}
}).catch(function (error) {
logger.error('Sequelize findOne error', error);
});
};
db.sequelize = sequelize; db.sequelize = sequelize;
db.Sequelize = Sequelize; db.Sequelize = Sequelize;

View file

@ -171,6 +171,9 @@ button.copy-button {
border-bottom: 1px solid grey; border-bottom: 1px solid grey;
font-weight: bold; font-weight: bold;
} }
.stats-table-url {
word-wrap: break-word;
}
@media (max-width: 750px) { @media (max-width: 750px) {

View file

@ -29,7 +29,7 @@ function validateFile(file) {
} }
// validation function that checks to make sure the claim name is not already claimed // validation function that checks to make sure the claim name is not already claimed
function isNameAvailable (name) { function isNameAvailable (name) {
var deferred = new Promise(function(resolve, reject) { return new Promise(function(resolve, reject) {
// make sure the claim name is still available // make sure the claim name is still available
var xhttp; var xhttp;
xhttp = new XMLHttpRequest(); xhttp = new XMLHttpRequest();
@ -50,7 +50,6 @@ function isNameAvailable (name) {
}; };
xhttp.send(); xhttp.send();
}); });
return deferred;
} }
// validation function that checks to make sure the claim name is valid // validation function that checks to make sure the claim name is valid
function validateClaimName (name) { function validateClaimName (name) {
@ -92,7 +91,7 @@ function checkClaimName(name){
} }
// validation function which checks all aspects of the publish submission // validation function which checks all aspects of the publish submission
function validateSubmission(stagedFiles, name){ function validateSubmission(stagedFiles, name){
var deferred = new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
// make sure only 1 file was selected // make sure only 1 file was selected
if (!stagedFiles) { if (!stagedFiles) {
reject(new FileError("Please select a file")); reject(new FileError("Please select a file"));
@ -119,5 +118,4 @@ function validateSubmission(stagedFiles, name){
reject(error); reject(error);
}); });
}); });
return deferred;
} }

View file

@ -2,16 +2,16 @@ const logger = require('winston');
const multipart = require('connect-multiparty'); const multipart = require('connect-multiparty');
const multipartMiddleware = multipart(); const multipartMiddleware = multipart();
const publishController = require('../controllers/publishController.js'); const publishController = require('../controllers/publishController.js');
const lbryApi = require('../helpers/libraries/lbryApi.js'); const lbryApi = require('../helpers/lbryApi.js');
const { createPublishParams, validateFile } = require('../helpers/libraries/publishHelpers.js'); const { createPublishParams, validateFile } = require('../helpers/publishHelpers.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/errorHandlers.js');
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
module.exports = (app, hostedContentPath) => { module.exports = (app, hostedContentPath) => {
// route to run a claim_list request on the daemon // route to run a claim_list request on the daemon
app.get('/api/claim_list/:name', ({ headers, ip, originalUrl, params }, res) => { app.get('/api/claim_list/:name', ({ headers, ip, originalUrl, params }, res) => {
// google analytics // google analytics
sendGoogleAnalytics('serve', headers, ip, originalUrl); sendGoogleAnalytics('SERVE', headers, ip, originalUrl);
// serve the content // serve the content
lbryApi lbryApi
.getClaimsList(params.name) .getClaimsList(params.name)
@ -43,7 +43,7 @@ module.exports = (app, hostedContentPath) => {
// route to run a resolve request on the daemon // route to run a resolve request on the daemon
app.get('/api/resolve/:uri', ({ headers, ip, originalUrl, params }, res) => { app.get('/api/resolve/:uri', ({ headers, ip, originalUrl, params }, res) => {
// google analytics // google analytics
sendGoogleAnalytics('serve', headers, ip, originalUrl); sendGoogleAnalytics('SERVE', headers, ip, originalUrl);
// serve content // serve content
lbryApi lbryApi
.resolveUri(params.uri) .resolveUri(params.uri)
@ -59,7 +59,7 @@ module.exports = (app, hostedContentPath) => {
// route to run a publish request on the daemon // route to run a publish request on the daemon
app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => { app.post('/api/publish', multipartMiddleware, ({ body, files, headers, ip, originalUrl }, res) => {
// google analytics // google analytics
sendGoogleAnalytics('publish', headers, ip, originalUrl); sendGoogleAnalytics('PUBLISH', headers, ip, originalUrl);
// validate that a file was provided // validate that a file was provided
const file = files.speech || files.null; const file = files.speech || files.null;
const name = body.name || file.name.substring(0, file.name.indexOf('.')); const name = body.name || file.name.substring(0, file.name.indexOf('.'));

View file

@ -1,5 +1,5 @@
const { postToStats, getTrendingClaims } = require('../controllers/statsController.js'); const { postToStats, getTrendingClaims } = require('../controllers/statsController.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/errorHandlers.js');
module.exports = app => { module.exports = app => {
// route for the home page // route for the home page
@ -17,7 +17,7 @@ module.exports = app => {
}); });
}); });
// a catch-all route if someone visits a page that does not exist // a catch-all route if someone visits a page that does not exist
app.use('*', ({ originalUrl, ip }, res) => { app.use('*', ({ headers, originalUrl, ip }, res) => {
// post to stats // post to stats
postToStats('show', originalUrl, ip, null, null, 'Error: 404'); postToStats('show', originalUrl, ip, null, null, 'Error: 404');
// send response // send response

View file

@ -1,5 +1,5 @@
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/errorHandlers.js');
const { showAllClaims } = require('../controllers/showController.js'); const getAllFreePublicClaims = require('../helpers/functions/getAllFreePublicClaims.js');
const { postToStats, getStatsSummary, getTrendingClaims } = require('../controllers/statsController.js'); const { postToStats, getStatsSummary, getTrendingClaims } = require('../controllers/statsController.js');
module.exports = (app) => { module.exports = (app) => {
@ -38,7 +38,7 @@ module.exports = (app) => {
// route to display all free public claims at a given name // route to display all free public claims at a given name
app.get('/:name/all', ({ ip, originalUrl, params }, res) => { app.get('/:name/all', ({ ip, originalUrl, params }, res) => {
// get and render the content // get and render the content
showAllClaims(params.name) getAllFreePublicClaims(params.name)
.then(orderedFreePublicClaims => { .then(orderedFreePublicClaims => {
if (!orderedFreePublicClaims) { if (!orderedFreePublicClaims) {
res.status(307).render('noClaims'); res.status(307).render('noClaims');

View file

@ -1,15 +1,15 @@
const logger = require('winston'); const logger = require('winston');
const { serveFile, showFile, showFileLite, getShortUrlFromClaimId } = require('../helpers/libraries/serveHelpers.js'); const { serveFile, showFile, showFileLite, getShortUrlFromClaimId } = require('../helpers/serveHelpers.js');
const { getAssetByChannel, getAssetByShortUrl, getAssetByClaimId } = require('../controllers/serveController.js'); const { getAssetByChannel, getAssetByShortUrl, getAssetByClaimId, getAssetByName } = require('../controllers/serveController.js');
const { postToStats } = require('../controllers/statsController.js'); const { handleRequestError } = require('../helpers/errorHandlers.js');
const { handleRequestError } = require('../helpers/libraries/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const SERVE = 'SERVE'; const SERVE = 'SERVE';
const SHOW = 'SHOW'; const SHOW = 'SHOW';
const SHOWLITE = 'SHOWLITE'; const SHOWLITE = 'SHOWLITE';
const CHANNEL = 'CHANNEL'; const CHANNEL = 'CHANNEL';
const SHORTURL = 'SHORTURL'; const SHORTURL = 'SHORTURL';
const CLAIMID = 'CLAIMID'; const CLAIMID = 'CLAIMID';
const NAME = 'NAME';
function getAsset (claimType, channelName, shortUrl, fullClaimId, name) { function getAsset (claimType, channelName, shortUrl, fullClaimId, name) {
switch (claimType) { switch (claimType) {
@ -19,21 +19,96 @@ function getAsset (claimType, channelName, shortUrl, fullClaimId, name) {
return getAssetByShortUrl(shortUrl, name); return getAssetByShortUrl(shortUrl, name);
case CLAIMID: case CLAIMID:
return getAssetByClaimId(fullClaimId, name); return getAssetByClaimId(fullClaimId, name);
case NAME:
return getAssetByName(name);
default: default:
return new Error('that claim type was not found'); return new Error('that claim type was not found');
} }
} }
function serveOrShowAsset (fileInfo, method, headers, originalUrl, ip, res) {
// add file extension to the file info
fileInfo['fileExt'] = fileInfo.fileName.substring(fileInfo.fileName.lastIndexOf('.'));
// serve or show
switch (method) {
case SERVE:
serveFile(fileInfo, res);
sendGoogleAnalytics(method, headers, ip, originalUrl);
postToStats('serve', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
return fileInfo;
case SHOWLITE:
showFileLite(fileInfo, res);
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
return fileInfo;
case SHOW:
return getShortUrlFromClaimId(fileInfo.claimId, fileInfo.name)
.then(shortUrl => {
fileInfo['shortUrl'] = shortUrl;
showFile(fileInfo, res);
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
return fileInfo;
})
.catch(error => {
console.log('thowing error...');
throw error;
});
default:
logger.error('I did not recognize that method');
break;
}
}
function isValidClaimId (claimId) {
return ((claimId.length === 40) && !/[^A-Za-z0-9]/g.test(claimId));
}
function isValidShortUrl (claimId) {
return claimId.length === 1; // really it should evaluate the short url itself
}
function isValidShortUrlOrClaimId (input) {
return (isValidClaimId(input) || isValidShortUrl(input));
}
module.exports = (app) => { module.exports = (app) => {
// route to serve a specific asset // route to serve a specific asset
app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => { app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => {
const identifier = params.identifier; let identifier = params.identifier;
let name = params.name; let name = params.name;
// parse identifier for whether it is a channel, short url, or claim_id
let claimType; let claimType;
let channelName = null; let channelName = null;
let shortUrl = null; let shortUrl = null;
let fullClaimId = null; let fullClaimId = null;
let method;
let extension;
// parse the name
const positionOfExtension = name.indexOf('.');
if (positionOfExtension >= 0) {
extension = name.substring(positionOfExtension);
name = name.substring(0, positionOfExtension);
logger.debug('file extension =', extension);
if (headers['accept'] && headers['accept'].split(',').includes('text/html')) {
method = SHOWLITE;
} else {
method = SERVE;
}
} else {
if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) {
method = SERVE;
} else {
method = SHOW;
}
}
/* start: temporary patch for backwards compatability spee.ch/name/claim_id */
if (isValidShortUrlOrClaimId(name) && !isValidShortUrlOrClaimId(identifier)) {
let tempName = name;
name = identifier;
identifier = tempName;
}
/* end */
logger.debug('claim name =', name);
logger.debug('method =', method);
// parse identifier for whether it is a channel, short url, or claim_id
if (identifier.charAt(0) === '@') { if (identifier.charAt(0) === '@') {
channelName = identifier.substring(1); channelName = identifier.substring(1);
logger.debug('channel name =', channelName); logger.debug('channel name =', channelName);
@ -47,21 +122,42 @@ module.exports = (app) => {
logger.debug('short url =', shortUrl); logger.debug('short url =', shortUrl);
claimType = SHORTURL; claimType = SHORTURL;
} else { } else {
logger.error('that url does not compute'); logger.error('The URL provided could not be parsed');
res.send('that url is invalid'); res.send('that url is invalid');
return; return;
}; };
// parse the name // 1. retrieve the asset and information
getAsset(claimType, channelName, shortUrl, fullClaimId, name)
// 2. serve or show
.then(fileInfo => {
if (!fileInfo) {
res.status(200).render('noClaims');
} else {
return serveOrShowAsset(fileInfo, method, headers, originalUrl, ip, res);
}
})
// 3. update the file
.then(fileInfoForUpdate => {
// if needed, this is where we would update the file
})
.catch(error => {
handleRequestError('serve', originalUrl, ip, error, res);
});
});
// route to serve the winning asset at a claim
app.get('/:name', ({ headers, ip, originalUrl, params }, res) => {
// parse name param
let name = params.name;
let method; let method;
let desiredExtension; let fileExtension;
if (name.indexOf('.') !== -1) { if (name.indexOf('.') !== -1) {
method = SERVE; method = SERVE;
if (headers['accept'] && headers['accept'].split(',').includes('text/html')) { if (headers['accept'] && headers['accept'].split(',').includes('text/html')) {
method = SHOWLITE; method = SHOWLITE;
} }
desiredExtension = name.substring(name.indexOf('.')); fileExtension = name.substring(name.indexOf('.'));
name = name.substring(0, name.indexOf('.')); name = name.substring(0, name.indexOf('.'));
logger.debug('file extension =', desiredExtension); logger.debug('file extension =', fileExtension);
} else { } else {
method = SHOW; method = SHOW;
if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) { if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) {
@ -71,55 +167,21 @@ module.exports = (app) => {
logger.debug('claim name = ', name); logger.debug('claim name = ', name);
logger.debug('method =', method); logger.debug('method =', method);
// 1. retrieve the asset and information // 1. retrieve the asset and information
getAsset(claimType, channelName, shortUrl, fullClaimId, name) getAsset(NAME, null, null, null, name)
// 2. serve or show // 2. serve or show
.then(fileInfo => { .then(fileInfo => {
logger.debug('asset was retrieved');
// add file extension to the file info
fileInfo['fileExt'] = fileInfo.fileName.substring(fileInfo.fileName.lastIndexOf('.'));
// test logging
logger.debug(fileInfo);
// serve or show
if (!fileInfo) { if (!fileInfo) {
res.status(200).render('noClaims'); res.status(200).render('noClaims');
return; } else {
} return serveOrShowAsset(fileInfo, method, headers, originalUrl, ip, res);
switch (method) {
case SERVE:
serveFile(fileInfo, res);
break;
case SHOWLITE:
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
showFileLite(fileInfo, res);
break;
case SHOW:
postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
getShortUrlFromClaimId(fileInfo.claimId, fileInfo.name)
.then(shortUrl => {
fileInfo['shortUrl'] = shortUrl;
showFile(fileInfo, res);
})
.catch(error => {
console.log('thowing error...');
throw error;
});
break;
default:
logger.error('I did not recognize that method');
break;
} }
}) })
// 3. update the database // 3. update the database
.then(() => { .then(fileInfoForUpdate => {
logger.debug('update db / create new record'); // if needed, this is where we would update the file
// if asset was found locally, update db (resolve the claim to see if a newer version exists and then get && update db if needed)
// if asset was retrieved from lbrynet, create db record
}) })
.catch(error => { .catch(error => {
handleRequestError('serve', originalUrl, ip, error, res); handleRequestError('serve', originalUrl, ip, error, res);
}); });
}); });
// route to serve the winning asset at a claim
app.get('/:name', ({ headers, ip, originalUrl, params }, res) => {
});
}; };

View file

@ -1,7 +1,7 @@
const logger = require('winston'); const logger = require('winston');
const publishController = require('../controllers/publishController.js'); const publishController = require('../controllers/publishController.js');
const publishHelpers = require('../helpers/libraries/publishHelpers.js'); const publishHelpers = require('../helpers/publishHelpers.js');
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/errorHandlers.js');
const { postToStats } = require('../controllers/statsController.js'); const { postToStats } = require('../controllers/statsController.js');
module.exports = (app, siofu, hostedContentPath) => { module.exports = (app, siofu, hostedContentPath) => {

View file

@ -6,7 +6,7 @@ const expressHandlebars = require('express-handlebars');
const Handlebars = require('handlebars'); const Handlebars = require('handlebars');
const config = require('config'); const config = require('config');
const logger = require('winston'); const logger = require('winston');
const { getDownloadDirectory } = require('./helpers/libraries/lbryApi'); const { getDownloadDirectory } = require('./helpers/lbryApi');
const PORT = 3000; // set port const PORT = 3000; // set port
const app = express(); // create an Express application const app = express(); // create an Express application

View file

@ -5,12 +5,12 @@
<p>These are all the free, public assets at that claim. You can publish more at <a href="/">spee.ch</a>.</p> <p>These are all the free, public assets at that claim. You can publish more at <a href="/">spee.ch</a>.</p>
{{#each claims}} {{#each claims}}
<div class="all-claims-item"> <div class="all-claims-item">
<img class="all-claims-img" src="/{{this.name}}/{{this.claim_id}}" /> <img class="all-claims-img" src="/{{this.claim_id}}/{{this.name}}/" />
<div class="all-claims-details"> <div class="all-claims-details">
<ul style="list-style-type:none"> <ul style="list-style-type:none">
<li>claim: {{this.name}}</li> <li>claim: {{this.name}}</li>
<li>claim_id: {{this.claim_id}}</li> <li>claim_id: {{this.claim_id}}</li>
<li>direct link: <a href="/{{this.name}}/{{this.claim_id}}">spee.ch/{{this.name}}/{{this.claim_id}}</a></li> <li>link: <a href="/{{this.claim_id}}/{{this.name}}">spee.ch/{{this.name}}/{{this.claim_id}}</a></li>
<li>author: {{this.value.stream.metadata.author}}</li> <li>author: {{this.value.stream.metadata.author}}</li>
<li>description: {{this.value.stream.metadata.description}}</li> <li>description: {{this.value.stream.metadata.description}}</li>
<li>license: {{this.value.stream.metadata.license}}</li> <li>license: {{this.value.stream.metadata.license}}</li>

View file

@ -4,15 +4,6 @@
</div> </div>
<div class="panel links"> <div class="panel links">
<h2 class="subheader">Links</h2> <h2 class="subheader">Links</h2>
{{!--short direct link to asset--}}
<div class="share-option">
<a href="/{{fileInfo.shortUrl}}/{{fileInfo.name}}{{fileInfo.fileExt}}">Permanent Short Link</a>
(most convenient)
<div class="input-error" id="input-error-copy-short-link" hidden="true"></div>
<br/>
<input type="text" id="short-link" class="link" readonly spellcheck="false" value="https://spee.ch/{{fileInfo.shortUrl}}/{{fileInfo.name}}{{fileInfo.fileExt}}" onclick="select()"/>
<button class="copy-button" data-elementtocopy="short-link" onclick="copyToClipboard(event)">copy</button>
</div>
{{!-- link to show route for asset--}} {{!-- link to show route for asset--}}
<div class="share-option"> <div class="share-option">
<a href="/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}">Permanent Long Link</a> <a href="/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}">Permanent Long Link</a>
@ -22,6 +13,15 @@
<input type="text" id="long-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}"/> <input type="text" id="long-link" class="link" readonly onclick="select()" spellcheck="false" value="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}{{fileInfo.fileExt}}"/>
<button class="copy-button" data-elementtocopy="long-link" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="long-link" onclick="copyToClipboard(event)">copy</button>
</div> </div>
{{!--short direct link to asset--}}
<div class="share-option">
<a href="/{{fileInfo.shortUrl}}/{{fileInfo.name}}{{fileInfo.fileExt}}">Permanent Short Link</a>
(most convenient)
<div class="input-error" id="input-error-copy-short-link" hidden="true"></div>
<br/>
<input type="text" id="short-link" class="link" readonly spellcheck="false" value="https://spee.ch/{{fileInfo.shortUrl}}/{{fileInfo.name}}{{fileInfo.fileExt}}" onclick="select()"/>
<button class="copy-button" data-elementtocopy="short-link" onclick="copyToClipboard(event)">copy</button>
</div>
{{!-- html text for embedding asset--}} {{!-- html text for embedding asset--}}
<div class="share-option"> <div class="share-option">
Embed HTML Embed HTML

View file

@ -20,12 +20,12 @@
<code>https://spee.ch/:name/:claim_id.ext</code> <code>https://spee.ch/:name/:claim_id.ext</code>
<ul> <ul>
<li >Serves a specific image or video file directly</li> <li >Serves a specific image or video file directly</li>
<li >E.g. <a href="/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg">spee.ch/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg</a></li> <li >E.g. <a href="/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg">spee.ch/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg</a></li>
</ul> </ul>
<code>https://spee.ch/:name/:claim_id</code> <code>https://spee.ch/:name/:claim_id</code>
<ul> <ul>
<li >Serves an HTML page with this specific claim and additional details</li> <li >Serves an HTML page with this specific claim and additional details</li>
<li >E.g. <a href="/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0">spee.ch/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0</a></li> <li >E.g. <a href="/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive">spee.ch/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0</a></li>
</ul> </ul>
<code>https://spee.ch/:name/all</code> <code>https://spee.ch/:name/all</code>
<ul> <ul>

View file

@ -5,8 +5,8 @@
<div id="examples-detail" hidden="true"> <div id="examples-detail" hidden="true">
<div class="example"> <div class="example">
<h4>Use spee.ch to embed a specific image:</h4> <h4>Use spee.ch to embed a specific image:</h4>
<a href="/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg"><img class="example-image" src="/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg"/></a> <a href="/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg"><img class="example-image" src="/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg"/></a>
<div class="example-code">&lt;img src="https://spee.ch/doitlive/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0.jpg"/&gt;</div> <div class="example-code">&lt;img src="https://spee.ch/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg"/&gt;</div>
</div> </div>
<div class="example"> <div class="example">
<h4>Use spee.ch to serve the top free image at a claim:</h4> <h4>Use spee.ch to serve the top free image at a claim:</h4>

View file

@ -4,6 +4,10 @@
</div> </div>
<div> <div>
<h3>Site Statistics</h3> <h3>Site Statistics</h3>
<p>Serve: {{ totals.totalServe }}</p>
<p>Publish: {{ totals.totalPublish }}</p>
<p>Show: {{ totals.totalShow }}</p>
<p>Percent Success: {{ percentSuccess}}%</p>
<table> <table>
<tr> <tr>
<th>action</th> <th>action</th>
@ -15,7 +19,7 @@
{{#each records}} {{#each records}}
<tr> <tr>
<td>{{ this.action }}</td> <td>{{ this.action }}</td>
<td>{{ this.url }}</td> <td class="stats-table-url">{{ this.url }}</td>
<td class="center-text">{{ this.count }}</td> <td class="center-text">{{ this.count }}</td>
<td class="center-text">{{ this.success }}</td> <td class="center-text">{{ this.success }}</td>
<td class="center-text">{{ this.failure }}</td> <td class="center-text">{{ this.failure }}</td>
@ -29,9 +33,6 @@
<td class="totals-row center-text">{{ totals.totalFailure }}</td> <td class="totals-row center-text">{{ totals.totalFailure }}</td>
</tr> </tr>
</table> </table>
<p>Serve: {{ totals.totalServe }}</p>
<p>Publish: {{ totals.totalPublish }}</p>
<p>Show: {{ totals.totalShow }}</p>
<p>Percent Success: {{ percentSuccess}}%</p>
</div> </div>
</div> </div>