const logger = require('winston'); const { getAssetByClaim, getChannelContents, getAssetByChannel, serveOrShowAsset } = require('../controllers/serveController.js'); const { handleRequestError } = require('../helpers/errorHandlers.js'); const SERVE = 'SERVE'; const SHOW = 'SHOW'; const SHOWLITE = 'SHOWLITE'; const CHANNEL = 'CHANNEL'; const CLAIM = 'CLAIM'; const CHANNELID_INDICATOR = ':'; function isValidClaimId (claimId) { return ((claimId.length === 40) && !/[^A-Za-z0-9]/g.test(claimId)); } function isValidShortId (claimId) { return claimId.length === 1; // really it should evaluate the short url itself } function isValidShortIdOrClaimId (input) { return (isValidClaimId(input) || isValidShortId(input)); } function getAsset (claimType, channelName, channelId, name, claimId) { switch (claimType) { case CHANNEL: return getAssetByChannel(channelName, channelId, name); case CLAIM: return getAssetByClaim(name, claimId); default: return new Error('that claim type was not found'); } } module.exports = (app) => { // route to serve a specific asset app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => { let identifier = params.identifier; let name = params.name; let claimType; let channelName = null; let claimId = null; let channelId = null; let method; let fileExtension; // parse the name const positionOfExtension = name.indexOf('.'); if (positionOfExtension >= 0) { fileExtension = name.substring(positionOfExtension + 1); name = name.substring(0, positionOfExtension); /* patch because twitter player preview adds '>' before file extension */ if (name.indexOf('>') >= 0) { name = name.substring(0, name.indexOf('>')); } /* end patch */ logger.debug('file extension =', fileExtension); if (headers['accept'] && headers['accept'].split(',').includes('text/html')) { method = SHOWLITE; } else { method = SERVE; } } else { method = SHOW; } /* patch for backwards compatability with spee.ch/name/claim_id */ if (isValidShortIdOrClaimId(name) && !isValidShortIdOrClaimId(identifier)) { let tempName = name; name = identifier; identifier = tempName; } /* end patch */ 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) === '@') { channelName = identifier; claimType = CHANNEL; const channelIdIndex = channelName.indexOf(CHANNELID_INDICATOR); if (channelIdIndex !== -1) { channelId = channelName.substring(channelIdIndex + 1); channelName = channelName.substring(0, channelIdIndex); } logger.debug('channel name =', channelName); } else { claimId = identifier; logger.debug('claim id =', claimId); claimType = CLAIM; } // 1. retrieve the asset and information getAsset(claimType, channelName, channelId, name, claimId) // 2. serve or show .then(fileInfo => { if (!fileInfo) { res.status(200).render('noClaims'); } else { return serveOrShowAsset(fileInfo, fileExtension, 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 fileExtension; let channelName = null; let channelId = null; if (name.charAt(0) === '@') { channelName = name; const channelIdIndex = channelName.indexOf(CHANNELID_INDICATOR); if (channelIdIndex !== -1) { channelId = channelName.substring(channelIdIndex + 1); channelName = channelName.substring(0, channelIdIndex); } logger.debug('channel name =', channelName); logger.debug('channel Id =', channelId); // 1. retrieve the channel contents getChannelContents(channelName, channelId) // 2. respond to the request .then(channelContents => { if (!channelContents) { res.status(200).render('noChannel'); } else { const handlebarsData = { channelName, channelContents, }; logger.debug(handlebarsData); res.status(200).render('channel', handlebarsData); } }) .catch(error => { handleRequestError('serve', originalUrl, ip, error, res); }); } else { if (name.indexOf('.') !== -1) { method = SERVE; if (headers['accept'] && headers['accept'].split(',').includes('text/html')) { method = SHOWLITE; } fileExtension = name.substring(name.indexOf('.') + 1); name = name.substring(0, name.indexOf('.')); logger.debug('file extension =', fileExtension); } else { method = SHOW; if (headers['accept'] && !headers['accept'].split(',').includes('text/html')) { method = SERVE; } } logger.debug('claim name = ', name); logger.debug('method =', method); // 1. retrieve the asset and information getAsset(CLAIM, null, null, name, null) // 2. respond to the request .then(fileInfo => { if (!fileInfo) { res.status(200).render('noClaims'); } else { return serveOrShowAsset(fileInfo, fileExtension, method, headers, originalUrl, ip, res); } }) // 3. update the database .then(fileInfoForUpdate => { // if needed, this is where we would update the file }) .catch(error => { handleRequestError('serve', originalUrl, ip, error, res); }); } }); };