removed duplicate templates
This commit is contained in:
parent
859654e6e4
commit
cda9efa800
17 changed files with 374 additions and 388 deletions
|
@ -1,105 +1,8 @@
|
|||
const lbryApi = require('../helpers/lbryApi.js');
|
||||
const db = require('../models');
|
||||
const logger = require('winston');
|
||||
const { serveFile, showFile, showFileLite } = require('../helpers/serveHelpers.js');
|
||||
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
||||
|
||||
const SERVE = 'SERVE';
|
||||
const SHOW = 'SHOW';
|
||||
const SHOWLITE = 'SHOWLITE';
|
||||
const DEFAULT_THUMBNAIL = 'https://spee.ch/assets/img/video_thumb_default.png';
|
||||
const NO_CHANNEL = 'NO_CHANNEL';
|
||||
const NO_CLAIM = 'NO_CLAIM';
|
||||
|
||||
function checkForLocalAssetByClaimId (claimId, name) {
|
||||
logger.debug(`checkForLocalAssetsByClaimId(${claimId}, ${name}`);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.File
|
||||
.findOne({where: { name, claimId }})
|
||||
.then(result => {
|
||||
if (result) {
|
||||
resolve(result.dataValues);
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addGetResultsToFileRecord (fileInfo, getResult) {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
fileInfo.fileType = getResult.mime_type;
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
function createFileRecord ({ name, claimId, outpoint, height, address, nsfw }) {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
height,
|
||||
address,
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
fileType: '',
|
||||
nsfw,
|
||||
};
|
||||
}
|
||||
|
||||
function getAssetByLongClaimId (fullClaimId, name) {
|
||||
logger.debug('...getting asset by claim Id...');
|
||||
return new Promise((resolve, reject) => {
|
||||
// 1. check locally for claim
|
||||
checkForLocalAssetByClaimId(fullClaimId, name)
|
||||
.then(dataValues => {
|
||||
// if a result was found, return early with the result
|
||||
if (dataValues) {
|
||||
logger.debug('found a local file for this name and claimId');
|
||||
resolve(dataValues);
|
||||
return;
|
||||
}
|
||||
logger.debug('no local file found for this name and claimId');
|
||||
// 2. if no local claim, resolve and get the claim
|
||||
db.Claim
|
||||
.resolveClaim(name, fullClaimId)
|
||||
.then(resolveResult => {
|
||||
// if no result, return early (claim doesn't exist or isn't free)
|
||||
if (!resolveResult) {
|
||||
resolve(NO_CLAIM);
|
||||
return;
|
||||
}
|
||||
logger.debug('resolve result >> ', resolveResult.dataValues);
|
||||
let fileRecord = {};
|
||||
// get the claim
|
||||
lbryApi.getClaim(`${name}#${fullClaimId}`)
|
||||
.then(getResult => {
|
||||
logger.debug('getResult >>', getResult);
|
||||
fileRecord = createFileRecord(resolveResult);
|
||||
fileRecord = addGetResultsToFileRecord(fileRecord, getResult);
|
||||
// insert a record in the File table & Update Claim table
|
||||
return db.File.create(fileRecord);
|
||||
})
|
||||
.then(() => {
|
||||
logger.debug('File record successfully updated');
|
||||
resolve(fileRecord);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function chooseThumbnail (claimInfo, defaultThumbnail) {
|
||||
if (!claimInfo.thumbnail || claimInfo.thumbnail.trim() === '') {
|
||||
|
@ -109,42 +12,38 @@ function chooseThumbnail (claimInfo, defaultThumbnail) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
getAssetByClaim (claimName, claimId) {
|
||||
logger.debug(`getAssetByClaim(${claimName}, ${claimId})`);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.Claim.getLongClaimId(claimName, claimId) // 1. get the long claim id
|
||||
.then(result => { // 2. get the asset using the long claim id
|
||||
logger.debug('long claim id ===', result);
|
||||
if (result === NO_CLAIM) {
|
||||
logger.debug('resolving NO_CLAIM');
|
||||
resolve(NO_CLAIM);
|
||||
return;
|
||||
getClaimId (channelName, channelId, name, claimId) {
|
||||
if (channelName) {
|
||||
return module.exports.getClaimIdByChannel(channelName, channelId, name);
|
||||
} else {
|
||||
return module.exports.getClaimIdByClaim(name, claimId);
|
||||
}
|
||||
resolve(getAssetByLongClaimId(result, claimName));
|
||||
},
|
||||
getClaimIdByClaim (claimName, claimId) {
|
||||
logger.debug(`getClaimIdByClaim(${claimName}, ${claimId})`);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.Claim.getLongClaimId(claimName, claimId) // get the long claim id
|
||||
.then(result => {
|
||||
resolve(result); // resolves with NO_CLAIM or valid claim id
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
getAssetByChannel (channelName, channelId, claimName) {
|
||||
logger.debug('getting asset by channel');
|
||||
getClaimIdByChannel (channelName, channelId, claimName) {
|
||||
logger.debug(`getClaimIdByChannel(${channelName}, ${channelId}, ${claimName})`);
|
||||
return new Promise((resolve, reject) => {
|
||||
db.Certificate.getLongChannelId(channelName, channelId) // 1. get the long channel id
|
||||
.then(result => { // 2. get the long claim Id
|
||||
.then(result => {
|
||||
if (result === NO_CHANNEL) {
|
||||
resolve(NO_CHANNEL);
|
||||
resolve(result); // resolves NO_CHANNEL
|
||||
return;
|
||||
}
|
||||
return db.Claim.getClaimIdByLongChannelId(result, claimName);
|
||||
return db.Claim.getClaimIdByLongChannelId(result, claimName); // 2. get the long claim id
|
||||
})
|
||||
.then(result => { // 3. get the asset using the long claim id
|
||||
logger.debug('asset claim id =', result);
|
||||
if (result === NO_CHANNEL || result === NO_CLAIM) {
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
resolve(getAssetByLongClaimId(result, claimName));
|
||||
.then(result => {
|
||||
resolve(result); // resolves with NO_CLAIM or valid claim id
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
|
@ -172,7 +71,7 @@ module.exports = {
|
|||
})
|
||||
.then(result => { // 4. add extra data not available from Claim table
|
||||
if (result === NO_CHANNEL) {
|
||||
resolve(NO_CHANNEL);
|
||||
resolve(result);
|
||||
return;
|
||||
}
|
||||
if (result) {
|
||||
|
@ -197,57 +96,23 @@ module.exports = {
|
|||
});
|
||||
});
|
||||
},
|
||||
serveOrShowAsset (fileInfo, extension, method, headers, originalUrl, ip, res) {
|
||||
// add file extension to the file info
|
||||
if (extension === 'gifv') {
|
||||
fileInfo['fileExt'] = 'gifv';
|
||||
} else {
|
||||
fileInfo['fileExt'] = fileInfo.fileName.substring(fileInfo.fileName.lastIndexOf('.') + 1);
|
||||
getLocalFileRecord (claimId, name) {
|
||||
return db.File.findOne({where: {claimId, name}})
|
||||
.then(file => {
|
||||
if (!file) {
|
||||
return null;
|
||||
}
|
||||
// add a record to the stats table
|
||||
postToStats(method, originalUrl, ip, fileInfo.name, fileInfo.claimId, 'success');
|
||||
// serve or show
|
||||
switch (method) {
|
||||
case SERVE:
|
||||
serveFile(fileInfo, res);
|
||||
sendGoogleAnalytics(method, headers, ip, originalUrl);
|
||||
return fileInfo;
|
||||
case SHOWLITE:
|
||||
return db.Claim.resolveClaim(fileInfo.name, fileInfo.claimId)
|
||||
.then(claimRecord => {
|
||||
fileInfo['title'] = claimRecord.title;
|
||||
fileInfo['description'] = claimRecord.description;
|
||||
showFileLite(fileInfo, res);
|
||||
return fileInfo;
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('throwing serverOrShowAsset SHOWLITE error...');
|
||||
throw error;
|
||||
return file.dataValues;
|
||||
});
|
||||
case SHOW:
|
||||
return db.Claim
|
||||
.getShortClaimIdFromLongClaimId(fileInfo.claimId, fileInfo.name)
|
||||
.then(shortId => {
|
||||
fileInfo['shortId'] = shortId;
|
||||
return db.Claim.resolveClaim(fileInfo.name, fileInfo.claimId);
|
||||
})
|
||||
.then(resolveResult => {
|
||||
logger.debug('resolve result >>', resolveResult.dataValues);
|
||||
fileInfo['thumbnail'] = chooseThumbnail(resolveResult, DEFAULT_THUMBNAIL);
|
||||
fileInfo['title'] = resolveResult.title;
|
||||
fileInfo['description'] = resolveResult.description;
|
||||
if (resolveResult.certificateId) { fileInfo['certificateId'] = resolveResult.certificateId };
|
||||
if (resolveResult.channelName) { fileInfo['channelName'] = resolveResult.channelName };
|
||||
showFile(fileInfo, res);
|
||||
return fileInfo;
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('throwing serverOrShowAsset SHOW error...');
|
||||
throw error;
|
||||
});
|
||||
default:
|
||||
logger.error('I did not recognize that method');
|
||||
break;
|
||||
},
|
||||
getClaimRecord (claimId, name) {
|
||||
return db.Claim.findOne({where: {claimId, name}})
|
||||
.then(claim => {
|
||||
if (!claim) {
|
||||
throw new Error('no record found in Claim table');
|
||||
}
|
||||
claim.dataValues.thumbnail = chooseThumbnail(claim.dataValues.thumbnail, DEFAULT_THUMBNAIL);
|
||||
return claim.dataValues;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
@ -13,10 +13,10 @@ module.exports = {
|
|||
return new Handlebars.SafeString(gaCode);
|
||||
},
|
||||
addOpenGraph (title, mimeType, showUrl, source, description, thumbnail) {
|
||||
if (title === null || title.trim() === '') {
|
||||
if (!title || title.trim() === '') {
|
||||
title = 'Spee.ch';
|
||||
}
|
||||
if (description === null || description.trim() === '') {
|
||||
if (!description || description.trim() === '') {
|
||||
description = 'Open-source, decentralized image and video sharing.';
|
||||
}
|
||||
const ogTitle = `<meta property="og:title" content="${title}" >`;
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
const logger = require('winston');
|
||||
const SERVE = 'SERVE';
|
||||
const SHOW = 'SHOW';
|
||||
const SHOWLITE = 'SHOWLITE';
|
||||
// const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
||||
|
||||
function createOpenGraphInfo ({ fileType, claimId, name, fileName, fileExt }) {
|
||||
function createOpenGraphInfo ({ claimId, name, fileExt }) {
|
||||
return {
|
||||
embedUrl : `https://spee.ch/embed/${claimId}/${name}`,
|
||||
showUrl : `https://spee.ch/${claimId}/${name}`,
|
||||
|
@ -10,36 +14,50 @@ function createOpenGraphInfo ({ fileType, claimId, name, fileName, fileExt }) {
|
|||
}
|
||||
|
||||
module.exports = {
|
||||
serveFile ({ fileName, fileType, filePath }, res) {
|
||||
logger.verbose(`serving file ${fileName}`);
|
||||
// set default options
|
||||
let options = {
|
||||
headers: {
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'Content-Type' : fileType,
|
||||
},
|
||||
};
|
||||
// adjust default options as needed
|
||||
switch (fileType) {
|
||||
case 'image/jpeg':
|
||||
case 'image/gif':
|
||||
case 'image/png':
|
||||
case 'video/mp4':
|
||||
break;
|
||||
serveOrShowAsset (method, fileInfo, claimInfo, shortId, res) {
|
||||
// add file extension to the file info
|
||||
claimInfo['fileExt'] = fileInfo.fileName.substring(fileInfo.fileName.lastIndexOf('.') + 1);
|
||||
// serve or show
|
||||
switch (method) {
|
||||
case SERVE:
|
||||
module.exports.serveFile(fileInfo, claimInfo, shortId, res);
|
||||
return fileInfo;
|
||||
case SHOWLITE:
|
||||
module.exports.showFileLite(fileInfo, claimInfo, shortId, res);
|
||||
return fileInfo;
|
||||
case SHOW:
|
||||
module.exports.showFile(fileInfo, claimInfo, shortId, res);
|
||||
return fileInfo;
|
||||
default:
|
||||
logger.warn('sending file with unknown type as .jpeg');
|
||||
options['headers']['Content-Type'] = 'image/jpeg';
|
||||
logger.error('I did not recognize that method');
|
||||
break;
|
||||
}
|
||||
// send the file
|
||||
res.status(200).sendFile(filePath, options);
|
||||
},
|
||||
showFile (fileInfo, res) {
|
||||
const openGraphInfo = createOpenGraphInfo(fileInfo);
|
||||
res.status(200).render('show', { layout: 'show', fileInfo, openGraphInfo });
|
||||
},
|
||||
showFileLite (fileInfo, res) {
|
||||
const openGraphInfo = createOpenGraphInfo(fileInfo);
|
||||
res.status(200).render('showLite', { layout: 'showlite', fileInfo, openGraphInfo });
|
||||
serveFile ({ filePath }, { claimId, name, contentType }, shortId, res) {
|
||||
logger.verbose(`serving ${name}#${claimId}`);
|
||||
// set response options
|
||||
const headerContentType = contentType || 'image/jpeg';
|
||||
const options = {
|
||||
headers: {
|
||||
'X-Content-Type-Options': 'nosniff',
|
||||
'Content-Type' : headerContentType,
|
||||
},
|
||||
};
|
||||
// send the file
|
||||
if (filePath) {
|
||||
res.status(200).sendFile(filePath, options);
|
||||
} else {
|
||||
// 'get' the file
|
||||
// send the file
|
||||
res.status(307).redirect(`/api/get/${name}/${claimId}`);
|
||||
}
|
||||
},
|
||||
showFile (fileInfo, claimInfo, shortId, res) {
|
||||
const openGraphInfo = createOpenGraphInfo(claimInfo);
|
||||
res.status(200).render('show', { layout: 'show', claimInfo, shortId, openGraphInfo });
|
||||
},
|
||||
showFileLite (fileInfo, claimInfo, shortId, res) {
|
||||
const openGraphInfo = createOpenGraphInfo(claimInfo);
|
||||
res.status(200).render('showLite', { layout: 'showlite', claimInfo, shortId, openGraphInfo });
|
||||
},
|
||||
};
|
||||
|
|
|
@ -171,10 +171,32 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
|
|||
});
|
||||
};
|
||||
|
||||
Certificate.validateLongChannelId = function (name, claimId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.findOne({
|
||||
where: {name, claimId},
|
||||
})
|
||||
.then(result => {
|
||||
switch (result.length) {
|
||||
case 0:
|
||||
return resolve(NO_CHANNEL);
|
||||
case 1:
|
||||
return resolve(result[0]);
|
||||
default:
|
||||
logger.warn(`more than one entry matches that name (${name}) and certificate Id (${claimId})`);
|
||||
return resolve(result[0]);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Certificate.getLongChannelId = function (channelName, channelId) {
|
||||
logger.debug(`getLongChannelId(${channelName}, ${channelId})`);
|
||||
if (channelId && (channelId.length === 40)) { // if a full channel id is provided
|
||||
return new Promise((resolve, reject) => resolve(channelId));
|
||||
return this.validateLongChannelId(channelName, channelId);
|
||||
} else if (channelId && channelId.length < 40) { // if a short channel id is provided
|
||||
return this.getLongChannelIdFromShortChannelId(channelName, channelId);
|
||||
} else {
|
||||
|
|
|
@ -274,10 +274,34 @@ 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 => {
|
||||
// logger.debug('validateLongClaimId result:', result.dataValues);
|
||||
logger.debug('validateLongClaimId result.length:', result.dataValues.length);
|
||||
switch (result.dataValues.length) {
|
||||
case 0:
|
||||
return resolve(NO_CLAIM);
|
||||
case 1:
|
||||
return resolve(claimId);
|
||||
default:
|
||||
logger.warn(`more than one entry matches that name (${name}) and claimID (${claimId})`);
|
||||
return resolve(claimId);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Claim.getLongClaimId = function (claimName, claimId) {
|
||||
logger.debug(`getLongClaimId(${claimName}, ${claimId})`);
|
||||
if (claimId && (claimId.length === 40)) { // if a full claim id is provided
|
||||
return new Promise((resolve, reject) => resolve(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 {
|
||||
|
|
|
@ -10,6 +10,77 @@ const errorHandlers = require('../helpers/errorHandlers.js');
|
|||
const { postToStats } = require('../controllers/statsController.js');
|
||||
const { authenticateOrSkip } = require('../auth/authentication.js');
|
||||
|
||||
function addGetResultsToFileRecord (fileInfo, getResult) {
|
||||
fileInfo.fileName = getResult.file_name;
|
||||
fileInfo.filePath = getResult.download_path;
|
||||
return fileInfo;
|
||||
}
|
||||
|
||||
function createFileRecord ({ name, claimId, outpoint, height, address, nsfw, contentType }) {
|
||||
return {
|
||||
name,
|
||||
claimId,
|
||||
outpoint,
|
||||
height,
|
||||
address,
|
||||
fileName: '',
|
||||
filePath: '',
|
||||
fileType: contentType,
|
||||
nsfw,
|
||||
};
|
||||
}
|
||||
|
||||
// function getAssetByLongClaimId (fullClaimId, name) {
|
||||
// logger.debug('...getting asset by claim Id...');
|
||||
// return new Promise((resolve, reject) => {
|
||||
// // 1. check locally for claim
|
||||
// checkForLocalAssetByClaimId(fullClaimId, name)
|
||||
// .then(dataValues => {
|
||||
// // if a result was found, return early with the result
|
||||
// if (dataValues) {
|
||||
// logger.debug('found a local file for this name and claimId');
|
||||
// resolve(dataValues);
|
||||
// return;
|
||||
// }
|
||||
// logger.debug('no local file found for this name and claimId');
|
||||
// // 2. if no local claim, resolve and get the claim
|
||||
// db.Claim
|
||||
// .resolveClaim(name, fullClaimId)
|
||||
// .then(resolveResult => {
|
||||
// // if no result, return early (claim doesn't exist or isn't free)
|
||||
// if (!resolveResult) {
|
||||
// resolve(NO_CLAIM);
|
||||
// return;
|
||||
// }
|
||||
// logger.debug('resolve result >> ', resolveResult.dataValues);
|
||||
// let fileRecord = {};
|
||||
// // get the claim
|
||||
// lbryApi.getClaim(`${name}#${fullClaimId}`)
|
||||
// .then(getResult => {
|
||||
// logger.debug('getResult >>', getResult);
|
||||
// fileRecord = createFileRecord(resolveResult);
|
||||
// fileRecord = addGetResultsToFileRecord(fileRecord, getResult);
|
||||
// // insert a record in the File table & Update Claim table
|
||||
// return db.File.create(fileRecord);
|
||||
// })
|
||||
// .then(() => {
|
||||
// logger.debug('File record successfully updated');
|
||||
// resolve(fileRecord);
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
// })
|
||||
// .catch(error => {
|
||||
// reject(error);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
module.exports = (app) => {
|
||||
// route to run a claim_list request on the daemon
|
||||
app.get('/api/claim_list/:name', ({ ip, originalUrl, params }, res) => {
|
||||
|
@ -27,9 +98,26 @@ module.exports = (app) => {
|
|||
if (!params.name || !params.claimId) {
|
||||
res.status(400).json({success: false, message: 'provide a claimId and/or a name'});
|
||||
}
|
||||
getClaim(`${params.name}#${params.claimId}`)
|
||||
.then(result => {
|
||||
res.status(200).json({status: 'success', message: result});
|
||||
let fileRecord;
|
||||
// 1. resolve the claim
|
||||
db.Claim.resolveClaim(params.name, params.claimId)
|
||||
.then(resolveResult => {
|
||||
if (!resolveResult) {
|
||||
throw new Error('No matching uri found in Claim table');
|
||||
}
|
||||
fileRecord = createFileRecord(resolveResult);
|
||||
// 2. get the claim
|
||||
return getClaim(`${params.name}#${params.claimId}`);
|
||||
})
|
||||
.then(getResult => {
|
||||
res.status(200).json({status: 'success', message: getResult});
|
||||
logger.debug('response was sent to the client');
|
||||
fileRecord = addGetResultsToFileRecord(fileRecord, getResult);
|
||||
// 3. insert a record for the claim into the File table
|
||||
return db.File.create(fileRecord);
|
||||
})
|
||||
.then(() => {
|
||||
logger.debug('File record successfully created');
|
||||
})
|
||||
.catch(error => {
|
||||
errorHandlers.handleApiError('get', originalUrl, ip, error, res);
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
const logger = require('winston');
|
||||
const { getAssetByClaim, getChannelContents, getAssetByChannel, serveOrShowAsset } = require('../controllers/serveController.js');
|
||||
const { getClaimId, getChannelContents, getLocalFileRecord, getClaimRecord } = require('../controllers/serveController.js');
|
||||
const { serveOrShowAsset } = require('../helpers/serveHelpers.js');
|
||||
const { handleRequestError } = require('../helpers/errorHandlers.js');
|
||||
const db = require('../models');
|
||||
|
||||
const SERVE = 'SERVE';
|
||||
const SHOW = 'SHOW';
|
||||
const SHOWLITE = 'SHOWLITE';
|
||||
const CHANNEL = 'CHANNEL';
|
||||
const CLAIM = 'CLAIM';
|
||||
// const CHANNEL = 'CHANNEL';
|
||||
// const CLAIM = 'CLAIM';
|
||||
const CLAIM_ID_CHAR = ':';
|
||||
const CHANNEL_CHAR = '@';
|
||||
const CLAIMS_PER_PAGE = 10;
|
||||
|
@ -25,17 +27,6 @@ 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');
|
||||
}
|
||||
}
|
||||
|
||||
function getPage (query) {
|
||||
if (query.p) {
|
||||
return parseInt(query.p);
|
||||
|
@ -86,7 +77,6 @@ module.exports = (app) => {
|
|||
app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => {
|
||||
let identifier = params.identifier;
|
||||
let name = params.name;
|
||||
let claimOrChannel;
|
||||
let channelName = null;
|
||||
let claimId = null;
|
||||
let channelId = null;
|
||||
|
@ -123,7 +113,6 @@ module.exports = (app) => {
|
|||
// parse identifier for whether it is a channel, short url, or claim_id
|
||||
if (identifier.charAt(0) === '@') {
|
||||
channelName = identifier;
|
||||
claimOrChannel = CHANNEL;
|
||||
const channelIdIndex = channelName.indexOf(CLAIM_ID_CHAR);
|
||||
if (channelIdIndex !== -1) {
|
||||
channelId = channelName.substring(channelIdIndex + 1);
|
||||
|
@ -133,13 +122,11 @@ module.exports = (app) => {
|
|||
} else {
|
||||
claimId = identifier;
|
||||
logger.debug('claim id =', claimId);
|
||||
claimOrChannel = CLAIM;
|
||||
}
|
||||
// 1. retrieve the asset and information
|
||||
getAsset(claimOrChannel, channelName, channelId, name, claimId)
|
||||
// 2. serve or show
|
||||
// get the claim id
|
||||
getClaimId(channelName, channelId, name, claimId)
|
||||
.then(result => {
|
||||
logger.debug('getAsset result:', result);
|
||||
logger.debug('getClaimId result:', result);
|
||||
if (result === NO_CLAIM) {
|
||||
res.status(200).render('noClaim');
|
||||
return;
|
||||
|
@ -147,7 +134,15 @@ module.exports = (app) => {
|
|||
res.status(200).render('noChannel');
|
||||
return;
|
||||
}
|
||||
return serveOrShowAsset(result, fileExtension, method, headers, originalUrl, ip, res);
|
||||
// check for local file info and resolve the claim
|
||||
return Promise.all([getLocalFileRecord(result, name), getClaimRecord(result, name), db.Claim.getShortClaimIdFromLongClaimId(result, name)]);
|
||||
})
|
||||
.then(([fileInfo, claimInfo, shortClaimId]) => {
|
||||
logger.debug(`file record:`, fileInfo);
|
||||
logger.debug('claim record:', claimInfo);
|
||||
logger.debug('short url:', shortClaimId);
|
||||
// serve or show
|
||||
return serveOrShowAsset(method, fileInfo, claimInfo, shortClaimId, res);
|
||||
})
|
||||
// 3. update the file
|
||||
.then(fileInfoForUpdate => {
|
||||
|
@ -229,18 +224,28 @@ module.exports = (app) => {
|
|||
}
|
||||
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
|
||||
// get the claim id
|
||||
getClaimId(null, null, name, null)
|
||||
.then(result => {
|
||||
logger.debug('getAsset result', result);
|
||||
logger.debug('getClaimId result:', result);
|
||||
if (result === NO_CLAIM) {
|
||||
res.status(200).render('noClaim');
|
||||
} else {
|
||||
return serveOrShowAsset(result, fileExtension, method, headers, originalUrl, ip, res);
|
||||
return;
|
||||
} else if (result === NO_CHANNEL) {
|
||||
res.status(200).render('noChannel');
|
||||
return;
|
||||
}
|
||||
// check for local file info and resolve the claim
|
||||
return Promise.all([getLocalFileRecord(result, name), getClaimRecord(result, name), db.Claim.getShortClaimIdFromLongClaimId(result, name)]);
|
||||
})
|
||||
// 3. update the database
|
||||
.then(([fileInfo, claimInfo, shortClaimId]) => {
|
||||
logger.debug(`fileInfo:`, fileInfo);
|
||||
logger.debug('claimInfo:', claimInfo);
|
||||
logger.debug('shortClaimId:', shortClaimId);
|
||||
// serve or show
|
||||
return serveOrShowAsset(method, fileInfo, claimInfo, shortClaimId, res);
|
||||
})
|
||||
// 3. update the file
|
||||
.then(fileInfoForUpdate => {
|
||||
// if needed, this is where we would update the file
|
||||
})
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
<link rel="stylesheet" href="/assets/css/general.css" type="text/css">
|
||||
<link rel="stylesheet" href="/assets/css/mediaQueries.css" type="text/css">
|
||||
<meta property="fb:app_id" content="1371961932852223">
|
||||
{{#unless fileInfo.nsfw}}
|
||||
{{{addTwitterCard fileInfo.fileType openGraphInfo.source openGraphInfo.embedUrl openGraphInfo.directFileUrl}}}
|
||||
{{{addOpenGraph fileInfo.title fileInfo.fileType openGraphInfo.showUrl openGraphInfo.source fileInfo.description fileInfo.thumbnail}}}
|
||||
{{#unless claimInfo.nsfw}}
|
||||
{{{addTwitterCard claimInfo.contentType openGraphInfo.source openGraphInfo.embedUrl openGraphInfo.directFileUrl}}}
|
||||
{{{addOpenGraph claimInfo.title claimInfo.contentType openGraphInfo.showUrl openGraphInfo.source claimInfo.description claimInfo.thumbnail}}}
|
||||
{{/unless}}
|
||||
<!--google font-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Roboto:300" rel="stylesheet">
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
<link rel="stylesheet" href="/assets/css/general.css" type="text/css">
|
||||
<link rel="stylesheet" href="/assets/css/mediaQueries.css" type="text/css">
|
||||
<meta property="fb:app_id" content="1371961932852223">
|
||||
{{#unless fileInfo.nsfw}}
|
||||
{{{addTwitterCard fileInfo.fileType openGraphInfo.source openGraphInfo.embedUrl openGraphInfo.directFileUrl}}}
|
||||
{{{addOpenGraph fileInfo.title fileInfo.fileType openGraphInfo.showUrl openGraphInfo.source fileInfo.description fileInfo.thumbnail}}}
|
||||
{{#unless claimInfo.nsfw}}
|
||||
{{{addTwitterCard claimInfo.contentType openGraphInfo.source openGraphInfo.embedUrl openGraphInfo.directFileUrl}}}
|
||||
{{{addOpenGraph claimInfo.title claimInfo.contentType openGraphInfo.showUrl openGraphInfo.source claimInfo.description claimInfo.thumbnail}}}
|
||||
{{/unless}}
|
||||
<!-- google analytics -->
|
||||
{{ googleAnalytics }}
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
|
||||
{{#ifConditional fileInfo.fileExt '===' 'gifv'}}
|
||||
{{#ifConditional claimInfo.contentType '===' 'video/mp4'}}
|
||||
{{#ifConditional claimInfo.fileExt '===' 'gifv'}}
|
||||
<video class="gifv-show" autoplay loop muted>
|
||||
<source src="/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}">
|
||||
<source src="/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}">
|
||||
{{!--fallback--}}
|
||||
Your browser does not support the <code>video</code> element.
|
||||
</video>
|
||||
{{else}}
|
||||
|
||||
<video id="video-player" class="video-show video" controls poster="{{fileInfo.thumbnail}}">
|
||||
<source src="/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}">
|
||||
<video id="video-player" class="video-show video" controls poster="{{claimInfo.thumbnail}}">
|
||||
<source src="/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}">
|
||||
{{!--fallback--}}
|
||||
Your browser does not support the <code>video</code> element.
|
||||
</video>
|
||||
|
@ -23,7 +22,63 @@
|
|||
</script>
|
||||
{{/ifConditional}}
|
||||
{{else}}
|
||||
<a href="/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}">
|
||||
<img class="image-show" src="/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}" />
|
||||
<a href="/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}">
|
||||
<img class="image-show" src="/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}" />
|
||||
</a>
|
||||
{{/ifConditional}}
|
||||
|
||||
<div id="asset-holder">
|
||||
<div id="search-message" hidden="true">
|
||||
<p>We're currently combing the blockchain for your asset!</p>
|
||||
</div>
|
||||
<div id="failure-message" hidden="true">
|
||||
<p>Hmmm, looks like no peers have your content. How anti-social!</p>
|
||||
</div>
|
||||
<div id="asset" hidden="true">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
const getAssetFunctions = {
|
||||
showAsset: function () {
|
||||
const searchMessage = document.getElementById('search-message');
|
||||
const asset = document.getElementById('asset');
|
||||
searchMessage.hidden = true;
|
||||
asset.hidden = false;
|
||||
},
|
||||
showFailureMessage: function (msg) {
|
||||
console.log(msg);
|
||||
const searchMessage = document.getElementById('search-message');
|
||||
const failureMessage = document.getElementById('failure-message');
|
||||
searchMessage.hidden = true;
|
||||
failureMessage.hidden = false;
|
||||
},
|
||||
displayAsset: function(contentType, source) {
|
||||
|
||||
},
|
||||
getAsset: function(claimName, claimId) {
|
||||
console.log(`getting ${claimName}#${claimId}}`)
|
||||
var uri = `/api/get/${claimName}/${claimId}`;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
console.log('get returned successfully', xhr.response);
|
||||
this.showAsset();
|
||||
} else {
|
||||
console.log('get failed:', xhr.response);
|
||||
}
|
||||
} else {
|
||||
console.log('xhr.readyState', xhr.readyState);
|
||||
this.showFailureMessage(xhr.readyState.message);
|
||||
}
|
||||
};
|
||||
// Initiate a multipart/form-data upload
|
||||
xhr.send();
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
|
@ -1,28 +1,28 @@
|
|||
{{#if fileInfo.channelName}}
|
||||
{{#if claimInfo.channelName}}
|
||||
<div class="row row--padded row--wide row--no-top">
|
||||
<div class="column column--2 column--med-10">
|
||||
<span class="text">Channel:</span>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
<span class="text"><a href="/{{fileInfo.channelName}}:{{fileInfo.certificateId}}">{{fileInfo.channelName}}</a></span>
|
||||
<span class="text"><a href="/{{claimInfo.channelName}}:{{claimInfo.certificateId}}">{{claimInfo.channelName}}</a></span>
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{#if fileInfo.description}}
|
||||
{{#if claimInfo.description}}
|
||||
<div class="row row--padded row--wide row--no-top">
|
||||
<span class="text">{{fileInfo.description}}</span>
|
||||
<span class="text">{{claimInfo.description}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="row row--wide">
|
||||
<div id="show-short-link">
|
||||
<div class="column column--2 column--med-10">
|
||||
<a class="link--primary" href="/{{fileInfo.shortId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}"><span class="text">Link:</span></a>
|
||||
<a class="link--primary" href="/{{shortId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}"><span class="text">Link:</span></a>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
<div class="row row--short row--wide">
|
||||
<div class="column column--7">
|
||||
<div class="input-error" id="input-error-copy-short-link" hidden="true"></div>
|
||||
<input type="text" id="short-link" class="input-disabled input-text--full-width" readonly spellcheck="false" value="https://spee.ch/{{fileInfo.shortId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}" onclick="select()"/>
|
||||
<input type="text" id="short-link" class="input-disabled input-text--full-width" readonly spellcheck="false" value="https://spee.ch/{{shortId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}" onclick="select()"/>
|
||||
</div><div class="column column--1"></div><div class="column column--2">
|
||||
<button class="button--primary" data-elementtocopy="short-link" onclick="copyToClipboard(event)">copy</button>
|
||||
</div>
|
||||
|
@ -36,10 +36,10 @@
|
|||
<div class="row row--short row--wide">
|
||||
<div class="column column--7">
|
||||
<div class="input-error" id="input-error-copy-embed-text" hidden="true"></div>
|
||||
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
|
||||
<input type="text" id="embed-text" class="input-disabled input-text--full-width" readonly onclick="select()" spellcheck="false" value='<video width="100%" controls poster="{{fileInfo.thumbnail}}" src="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}"/></video>'/>
|
||||
{{#ifConditional claimInfo.contentType '===' 'video/mp4'}}
|
||||
<input type="text" id="embed-text" class="input-disabled input-text--full-width" readonly onclick="select()" spellcheck="false" value='<video width="100%" controls poster="{{claimInfo.thumbnail}}" src="https://spee.ch/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}"/></video>'/>
|
||||
{{else}}
|
||||
<input type="text" id="embed-text" class="input-disabled input-text--full-width" readonly onclick="select()" spellcheck="false" value='<img src="https://spee.ch/{{fileInfo.claimId}}/{{fileInfo.name}}.{{fileInfo.fileExt}}"/>'/>
|
||||
<input type="text" id="embed-text" class="input-disabled input-text--full-width" readonly onclick="select()" spellcheck="false" value='<img src="https://spee.ch/{{claimInfo.claimId}}/{{claimInfo.name}}.{{claimInfo.fileExt}}"/>'/>
|
||||
{{/ifConditional}}
|
||||
</div><div class="column column--1"></div><div class="column column--2">
|
||||
<button class="button--primary" data-elementtocopy="embed-text" onclick="copyToClipboard(event)">copy</button>
|
||||
|
@ -55,10 +55,10 @@
|
|||
<span class="text">Share:</span>
|
||||
</div><div class="column column--7 column--med-10">
|
||||
<div class="row row--short row--wide flex-container--row flex-container--space-between-bottom flex-container--wrap">
|
||||
<a class="link--primary" target="_blank" href="https://twitter.com/intent/tweet?text=https://spee.ch/{{fileInfo.shortId}}/{{fileInfo.name}}">twitter</a>
|
||||
<a class="link--primary" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://spee.ch/{{fileInfo.shortId}}/{{fileInfo.name}}">facebook</a>
|
||||
<a class="link--primary" target="_blank" href="http://tumblr.com/widgets/share/tool?canonicalUrl=https://spee.ch/{{fileInfo.shortId}}/{{fileInfo.name}}">tumblr</a>
|
||||
<a class="link--primary" target="_blank" href="https://www.reddit.com/submit?url=https://spee.ch/{{fileInfo.shortId}}/{{fileInfo.name}}&title={{fileInfo.name}}">reddit</a>
|
||||
<a class="link--primary" target="_blank" href="https://twitter.com/intent/tweet?text=https://spee.ch/{{shortId}}/{{claimInfo.name}}">twitter</a>
|
||||
<a class="link--primary" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=https://spee.ch/{{shortId}}/{{claimInfo.name}}">facebook</a>
|
||||
<a class="link--primary" target="_blank" href="http://tumblr.com/widgets/share/tool?canonicalUrl=https://spee.ch/{{shortId}}/{{claimInfo.name}}">tumblr</a>
|
||||
<a class="link--primary" target="_blank" href="https://www.reddit.com/submit?url=https://spee.ch/{{shortId}}/{{claimInfo.name}}&title={{claimInfo.name}}">reddit</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,29 +73,29 @@
|
|||
<div class="column column--2 column--med-10">
|
||||
<span class="text">Name:</span>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
{{fileInfo.name}}
|
||||
{{claimInfo.name}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="show-claim-id">
|
||||
<div class="column column--2 column--med-10">
|
||||
<span class="text">Claim Id:</span>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
{{fileInfo.claimId}}
|
||||
{{claimInfo.claimId}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="show-claim-id">
|
||||
<div class="column column--2 column--med-10">
|
||||
<span class="text">File Name:</span>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
{{fileInfo.fileName}}
|
||||
{{claimInfo.fileName}}
|
||||
</div>
|
||||
</div>
|
||||
<div id="show-claim-id">
|
||||
<div class="column column--2 column--med-10">
|
||||
<span class="text">File Type:</span>
|
||||
</div><div class="column column--8 column--med-10">
|
||||
{{#if fileInfo.fileType}}
|
||||
{{fileInfo.fileType}}
|
||||
{{#if claimInfo.contentType}}
|
||||
{{claimInfo.contentType}}
|
||||
{{else}}
|
||||
unknown
|
||||
{{/if}}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
<div id="asset-holder">
|
||||
<div id="peer-info">
|
||||
<p id="peer-search-placeholder">Searching for peers for this asset...</p>
|
||||
<p id="peer-search-success" hidden="true">Number of peers with content:<span id="peer-number">?</span></p>
|
||||
<p id="peer-search-failure" hidden="true">Unfortunately, no peers could be located with that asset</p>
|
||||
</div>
|
||||
<div id="asset">
|
||||
<div id="video-display" hidden="true">
|
||||
|
||||
</div>
|
||||
<div id="gifv-display" hidden="true">
|
||||
|
||||
</div>
|
||||
<div id="gif-display" hidden="true">
|
||||
|
||||
</div>
|
||||
<div id="static-image-display" hidden="true">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
const getAssetFunctions = {
|
||||
showPeerListSuccess: function (numberOfPeers) {
|
||||
const peerSearchPlaceholder = document.getElementById('peer-search-placeholder');
|
||||
const peerSearchSuccess = document.getElementById('peer-search-success');
|
||||
const peerSearchNumber = document.getElementById('peer-search-number');
|
||||
peerSearchPlaceholder.hidden = true;
|
||||
peerSearchSuccess.hidden = false;
|
||||
peerSearchNumber.innerText = numberOfPeers;
|
||||
},
|
||||
showPeerListFailure: function () {
|
||||
const peerSearchPlaceholder = document.getElementById('peer-search-placeholder');
|
||||
const peerSearchFailure = document.getElementById('peer-search-failure');
|
||||
peerSearchPlaceholder.hidden = true;
|
||||
peerSearchFailure.hidden = false;
|
||||
},
|
||||
showAsset: function (claimName, claimId) {
|
||||
|
||||
},
|
||||
showFailureMessage: function (msg) {
|
||||
console.log(msg);
|
||||
},
|
||||
getAsset: function(claimName, claimId) {
|
||||
console.log(`getting ${claimName}#${claimId}}`)
|
||||
var uri = `/api/get/${claimName}/${claimId}`;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
console.log('get returned successfully')
|
||||
this.showAsset(claimName, claimId);
|
||||
} else {
|
||||
console.log('get failed:', xhr.response);
|
||||
}
|
||||
} else {
|
||||
console.log('xhr.readyState', xhr.readyState);
|
||||
this.showFailureMessage(xhr.readyState.message);
|
||||
}
|
||||
};
|
||||
// Initiate a multipart/form-data upload
|
||||
xhr.send();
|
||||
},
|
||||
getPeerList: function(claimName, claimId) {
|
||||
var uri = `/api/peer_list/${claimName}/${claimId}`;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", uri, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState == 4) {
|
||||
if (xhr.status == 200) {
|
||||
console.log('peer list retrieved:', JSON.parse(xhr.response).message);
|
||||
this.showPeerListSuccess(JSON.parse(xhr.response).message);
|
||||
this.getAsset(claimName, claimId);
|
||||
} else {
|
||||
console.log(xhr.response);
|
||||
console.log('unfortunately no peers could be found');
|
||||
this.showPeerListFailure(JSON.parse(xhr.response).message);
|
||||
}
|
||||
} else {
|
||||
console.log('xhr.readyState', xhr.readyState);
|
||||
}
|
||||
};
|
||||
// Initiate a multipart/form-data upload
|
||||
xhr.send();
|
||||
}
|
||||
}
|
||||
|
||||
getAssetFunctions.getPeerList({{fileInfo.claimName}}, {{fileInfo.claimId}})
|
||||
</script>
|
|
@ -1,12 +1,12 @@
|
|||
<div class="row row--tall row--padded">
|
||||
<div class="column column--10">
|
||||
<!-- title -->
|
||||
<span class="text--large">{{fileInfo.title}}</span>
|
||||
<span class="text--large">{{claimInfo.title}}</span>
|
||||
</div>
|
||||
<div class="column column--5 column--sml-10 align-content-top">
|
||||
<!-- asset -->
|
||||
<div class="row row--padded">
|
||||
{{> getAsset}}
|
||||
{{> asset}}
|
||||
</div>
|
||||
</div><div class="column column--5 column--sml-10 align-content-top">
|
||||
<!-- details -->
|
|
@ -1,7 +1,7 @@
|
|||
<div class="row row--tall row--padded">
|
||||
<div class="column column--10">
|
||||
<!-- title -->
|
||||
<span class="text--large">{{fileInfo.title}}</span>
|
||||
<span class="text--large">{{claimInfo.title}}</span>
|
||||
</div>
|
||||
<div class="column column--5 column--sml-10 align-content-top">
|
||||
<!-- asset -->
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
{{> getAsset }}
|
||||
<a class="link--primary fine-print" href="/{{fileInfo.claimId}}/{{fileInfo.name}}">hosted via spee<h</a>
|
2
views/showLite-local.handlebars
Normal file
2
views/showLite-local.handlebars
Normal file
|
@ -0,0 +1,2 @@
|
|||
{{> asset }}
|
||||
<a class="link--primary fine-print" href="/{{claimInfo.claimId}}/{{claimInfo.name}}">hosted via spee<h</a>
|
|
@ -1,2 +1,2 @@
|
|||
{{> asset }}
|
||||
<a class="link--primary fine-print" href="/{{fileInfo.claimId}}/{{fileInfo.name}}">hosted via spee<h</a>
|
||||
<a class="link--primary fine-print" href="/{{claimInfo.claimId}}/{{claimInfo.name}}">hosted via spee<h</a>
|
Loading…
Reference in a new issue