Trending images #77

Merged
bones7242 merged 9 commits from trending-images into master 2017-07-12 18:03:18 +02:00
29 changed files with 275 additions and 136 deletions

View file

@ -11,7 +11,7 @@ spee.ch is a single-serving site that reads and publishes images to and from the
* save your connection uri somewhere handy (you will need it when you start the server) * save your connection uri somewhere handy (you will need it when you start the server)
* the uri should be in the form `mysql://user:pass@host:port/dbname` * the uri should be in the form `mysql://user:pass@host:port/dbname`
* clone this repo * clone this repo
* customize `config/develpment.json` by replacing the value of `Database.PublishUploadPath` with a string representing the local path where you want uploaded files to be stored. * customize `config/develpment.json` by replacing the value of `Database.DownloadDirectory` with a string representing the local path where you want uploaded files to be stored.
* run `npm install` * run `npm install`
* to start the server, from your command line run `node server.js` while passing three environmental variables: your lbry wallet address (`LBRY_WALLET_ADDRESS`), your mysql connection uri (`MYSQL_CONNECTION_STRING`), and the environment to run (`NODE_ENV`). * to start the server, from your command line run `node server.js` while passing three environmental variables: your lbry wallet address (`LBRY_WALLET_ADDRESS`), your mysql connection uri (`MYSQL_CONNECTION_STRING`), and the environment to run (`NODE_ENV`).
* i.e. `LBRY_WALLET_ADDRESS=<your wallet address here> MYSQL_CONNECTION_STRING=<your connection uri here> NODE_ENV=development node server.js` * i.e. `LBRY_WALLET_ADDRESS=<your wallet address here> MYSQL_CONNECTION_STRING=<your connection uri here> NODE_ENV=development node server.js`

View file

@ -9,10 +9,9 @@
}, },
"Database": { "Database": {
"MySqlConnectionUri": "none", "MySqlConnectionUri": "none",
"PublishUploadPath": "none" "DownloadDirectory": "none"
}, },
"Logging": { "Logging": {
"LogLevel": "none", "LogLevel": "none"
"LogDirectory": "none"
} }
} }

View file

@ -9,10 +9,9 @@
}, },
"Database": { "Database": {
"MySqlConnectionUri": "none", "MySqlConnectionUri": "none",
"PublishUploadPath": "C:\\lbry\\speech\\hosted_content\\" "DownloadDirectory": "/home/ubuntu/Downloads/"
}, },
"Logging": { "Logging": {
"LogLevel": "silly", "LogLevel": "silly"
"LogDirectory": "C:\\lbry\\speech\\logs\\"
} }
} }

View file

@ -1,10 +1,4 @@
const fs = require('fs'); module.exports = (winston, logLevel) => {
module.exports = (winston, logLevel, logDir) => {
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
winston.configure({ winston.configure({
transports: [ transports: [
new (winston.transports.Console)({ new (winston.transports.Console)({
@ -15,16 +9,6 @@ module.exports = (winston, logLevel, logDir) => {
handleExceptions : true, handleExceptions : true,
humanReadableUnhandledException: true, humanReadableUnhandledException: true,
}), }),
new (winston.transports.File)({
filename : `${logDir}/speechLogs.log`,
level : logLevel,
json : false,
timestamp : true,
colorize : true,
prettyPrint : true,
handleExceptions : true,
humanReadableUnhandledException: true,
}),
], ],
}); });

View file

@ -9,10 +9,9 @@
}, },
"Database": { "Database": {
"MySqlConnectionUri": "none", "MySqlConnectionUri": "none",
"PublishUploadPath": "/home/lbry/Downloads/" "DownloadDirectory": "/home/lbry/Downloads/"
}, },
"Logging": { "Logging": {
"LogLevel": "verbose", "LogLevel": "verbose"
"LogDirectory": "/home/lbry/Logs"
} }
} }

View file

@ -9,10 +9,9 @@
}, },
"Database": { "Database": {
"MySqlConnectionUri": "none", "MySqlConnectionUri": "none",
"PublishUploadPath": "/home/ubuntu/Downloads/" "DownloadDirectory": "/home/ubuntu/Downloads/"
}, },
"Logging": { "Logging": {
"LogLevel": "debug", "LogLevel": "debug"
"LogDirectory": "/home/ubuntu/Logs"
} }
} }

View file

@ -98,6 +98,8 @@ function getClaimAndHandleResponse (uri, address, height, resolve, reject) {
}); });
// resolve the request // resolve the request
resolve({ resolve({
name,
claimId : claim_id,
fileName: file_name, fileName: file_name,
filePath: download_path, filePath: download_path,
fileType: mime_type, fileType: mime_type,

View file

@ -5,7 +5,7 @@ const db = require('../models');
const googleApiKey = config.get('AnalyticsConfig.GoogleId'); const googleApiKey = config.get('AnalyticsConfig.GoogleId');
module.exports = { module.exports = {
postToStats (action, url, ipAddress, result) { postToStats (action, url, ipAddress, name, claimId, fileName, fileType, nsfw, result) {
logger.silly(`creating ${action} record for statistics db`); logger.silly(`creating ${action} record for statistics db`);
// make sure the result is a string // make sure the result is a string
if (result && (typeof result !== 'string')) { if (result && (typeof result !== 'string')) {
@ -20,6 +20,11 @@ module.exports = {
action, action,
url, url,
ipAddress, ipAddress,
name,
claimId,
fileName,
fileType,
nsfw,
result, result,
}) })
.then() .then()
@ -58,20 +63,27 @@ module.exports = {
} }
}); });
}, },
getStatsSummary () { getStatsSummary (startDate) {
logger.debug('retrieving site statistics'); logger.debug('retrieving statistics');
const deferred = new Promise((resolve, reject) => { const deferred = new Promise((resolve, reject) => {
// get the raw statistics data // get the raw statistics data
db.Stats db.Stats
.findAll() .findAll({
where: {
createdAt: {
gt: startDate,
},
},
})
.then(data => { .then(data => {
const resultHashTable = {}; let resultHashTable = {};
let totalServe = 0; let totalServe = 0;
let totalPublish = 0; let totalPublish = 0;
let totalShow = 0; let totalShow = 0;
let totalCount = 0; let totalCount = 0;
let totalSuccess = 0; let totalSuccess = 0;
let totalFailure = 0; let totalFailure = 0;
let percentSuccess;
// sumarise the data // sumarise the data
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
let key = data[i].action + data[i].url; let key = data[i].action + data[i].url;
@ -114,7 +126,7 @@ module.exports = {
} }
} }
} }
const percentSuccess = Math.round(totalSuccess / totalCount * 100); percentSuccess = Math.round(totalSuccess / totalCount * 100);
// return results // return results
resolve({ records: resultHashTable, totals: { totalServe, totalPublish, totalShow, totalCount, totalSuccess, totalFailure }, percentSuccess }); resolve({ records: resultHashTable, totals: { totalServe, totalPublish, totalShow, totalCount, totalSuccess, totalFailure }, percentSuccess });
}) })
@ -125,4 +137,68 @@ module.exports = {
}); });
return deferred; return deferred;
}, },
getTrendingClaims (startDate) {
logger.debug('retrieving trending statistics');
const deferred = new Promise((resolve, reject) => {
// get the raw statistics data
db.Stats
.findAll({
where: {
createdAt: {
gt: startDate,
},
name: {
not: null,
},
claimId: {
not: null,
},
},
})
.then(data => {
let resultHashTable = {};
let sortableArray = [];
let sortedArray;
// summarise the data
for (let i = 0; i < data.length; i++) {
let key = `${data[i].name}#${data[i].claimId}`;
if (resultHashTable[key] === undefined) {
resultHashTable[key] = {
count : 0,
details: {
name : data[i].name,
claimId : data[i].claimId,
fileName: data[i].fileName,
fileType: data[i].fileType,
nsfw : data[i].nsfw,
},
};
} else {
resultHashTable[key]['count'] += 1;
}
}
for (let objKey in resultHashTable) {
if (resultHashTable.hasOwnProperty(objKey)) {
sortableArray.push([
resultHashTable[objKey]['count'],
resultHashTable[objKey]['details'],
]);
}
}
sortableArray.sort((a, b) => {
return b[0] - a[0];
});
sortedArray = sortableArray.map((a) => {
return a[1];
});
// return results
resolve(sortedArray);
})
.catch(error => {
logger.error('sequelize error', error);
reject(error);
});
});
return deferred;
},
}; };

View file

@ -5,16 +5,16 @@ module.exports = {
handleRequestError (action, originalUrl, ip, error, res) { handleRequestError (action, originalUrl, ip, error, res) {
logger.error('Request Error >>', error); logger.error('Request Error >>', error);
if (error.response) { if (error.response) {
postToStats(action, originalUrl, ip, error.response.data.error.messsage); postToStats(action, originalUrl, ip, null, null, null, null, null, error.response.data.error.messsage);
res.status(error.response.status).send(error.response.data.error.message); res.status(error.response.status).send(error.response.data.error.message);
} else if (error.code === 'ECONNREFUSED') { } else if (error.code === 'ECONNREFUSED') {
postToStats(action, originalUrl, ip, 'Connection refused. The daemon may not be running.'); postToStats(action, originalUrl, ip, null, null, null, null, null, 'Connection refused. The daemon may not be running.');
res.status(503).send('Connection refused. The daemon may not be running.'); res.status(503).send('Connection refused. The daemon may not be running.');
} else if (error.message) { } else if (error.message) {
postToStats(action, originalUrl, ip, error); postToStats(action, originalUrl, ip, null, null, null, null, null, error);
res.status(400).send(error.message); res.status(400).send(error.message);
} else { } else {
postToStats(action, originalUrl, ip, error); postToStats(action, originalUrl, ip, null, null, null, null, null, error);
res.status(400).send(error); res.status(400).send(error);
} }
}, },

View file

@ -1,4 +1,4 @@
module.exports = (sequelize, { STRING, TEXT }) => { module.exports = (sequelize, { STRING, BOOLEAN, TEXT }) => {
const Stats = sequelize.define( const Stats = sequelize.define(
'Stats', 'Stats',
{ {
@ -13,7 +13,26 @@ module.exports = (sequelize, { STRING, TEXT }) => {
ipAddress: { ipAddress: {
type : STRING, type : STRING,
allowNull: true, allowNull: true,
default : null, },
name: {
type : STRING,
allowNull: true,
},
claimId: {
type : STRING,
allowNull: true,
},
fileName: {
type : STRING,
allowNull: true,
},
fileType: {
type : STRING,
allowNull: true,
},
nsfw: {
type : BOOLEAN,
allowNull: true,
}, },
result: { result: {
type : TEXT('long'), type : TEXT('long'),

View file

@ -12,6 +12,29 @@
margin: 2px 5px 2px 5px; margin: 2px 5px 2px 5px;
} }
/* publish */
#drop-zone {
border: 1px dashed lightgrey;
padding: 1em;
height: 6em;
}
#asset-preview-holder {
width: 100%;
margin-bottom: 1em;
}
.snapshot-generator {
display: block;
height: 1px;
left: 0;
object-fit: contain;
position: fixed;
top: 0;
width: 1px;
z-index: -1;
}
/* show routes */ /* show routes */
.show-asset { .show-asset {
width: 100%; width: 100%;
@ -53,6 +76,13 @@ button.copy-button {
vertical-align: top; vertical-align: top;
} }
/* trending claims */
.asset-trending {
width: 21%;
margin: 2%;
float: left;
}
/* learn more */ /* learn more */
.learn-more { .learn-more {
text-align: center; text-align: center;
@ -115,29 +145,6 @@ button.copy-button {
list-style-type: none; list-style-type: none;
} }
/* publish */
#drop-zone {
border: 1px dashed lightgrey;
padding: 1em;
height: 6em;
}
#asset-preview-holder {
width: 100%;
margin-bottom: 1em;
}
.snapshot-generator {
display: block;
height: 1px;
left: 0;
object-fit: contain;
position: fixed;
top: 0;
width: 1px;
z-index: -1;
}
/* meme */ /* meme */
canvas { canvas {
background-color: white; background-color: white;
@ -145,13 +152,6 @@ canvas {
height: auto; height: auto;
} }
.meme-fodder-img {
width: 21%;
padding: 0px;
margin: 2% 4% 2% 0px;
float: left;
}
/* statistics */ /* statistics */
.totals-row { .totals-row {
border-top: 1px solid grey; border-top: 1px solid grey;

View file

@ -91,6 +91,13 @@ h4 {
/* other */ /* other */
.asset-small {
height: 200px;
padding: 0px;
margin: 10px;
float: left;
}
input { input {
padding: 0.3em; padding: 0.3em;
} }

View file

@ -8,11 +8,11 @@ const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
const config = require('config'); const config = require('config');
const hostedContentPath = config.get('Database.PublishUploadPath'); const hostedContentPath = config.get('Database.DownloadDirectory');
module.exports = app => { module.exports = app => {
// route to run a claim_list request on the daemon // route to return a file directly
app.get('/api/streamFile/:name', ({ params, headers }, res) => { app.get('/api/streamFile/:name', ({ params }, res) => {
const filePath = `${hostedContentPath}${params.name}`; const filePath = `${hostedContentPath}${params.name}`;
res.status(200).sendFile(filePath); res.status(200).sendFile(filePath);
}); });
@ -24,7 +24,7 @@ module.exports = app => {
lbryApi lbryApi
.getClaimsList(params.name) .getClaimsList(params.name)
.then(claimsList => { .then(claimsList => {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, null, null, null, null, 'success');
res.status(200).json(claimsList); res.status(200).json(claimsList);
}) })
.catch(error => { .catch(error => {
@ -56,7 +56,7 @@ module.exports = app => {
lbryApi lbryApi
.resolveUri(params.uri) .resolveUri(params.uri)
.then(resolvedUri => { .then(resolvedUri => {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, null, null, null, null, 'success');
res.status(200).json(resolvedUri); res.status(200).json(resolvedUri);
}) })
.catch(error => { .catch(error => {
@ -76,7 +76,7 @@ module.exports = app => {
try { try {
validateFile(file, name, license, nsfw); validateFile(file, name, license, nsfw);
} catch (error) { } catch (error) {
postToStats('publish', originalUrl, ip, error.message); postToStats('publish', originalUrl, ip, null, null, null, null, error.message);
logger.debug('rejected >>', error.message); logger.debug('rejected >>', error.message);
res.status(400).send(error.message); res.status(400).send(error.message);
return; return;
@ -91,7 +91,7 @@ module.exports = app => {
publishController publishController
.publish(publishParams, fileName, fileType) .publish(publishParams, fileName, fileType)
.then(result => { .then(result => {
postToStats('publish', originalUrl, ip, 'success'); postToStats('publish', originalUrl, ip, null, null, null, null, 'success');
res.status(200).json(result); res.status(200).json(result);
}) })
.catch(error => { .catch(error => {

View file

@ -11,7 +11,7 @@ module.exports = app => {
app.use('*', ({ originalUrl, ip }, res) => { app.use('*', ({ originalUrl, ip }, res) => {
logger.error(`404 on ${originalUrl}`); logger.error(`404 on ${originalUrl}`);
// post to stats // post to stats
postToStats('show', originalUrl, ip, 'Error: 404'); postToStats('show', originalUrl, ip, null, null, null, null, null, 'Error: 404');
// send response // send response
res.status(404).render('fourOhFour'); res.status(404).render('fourOhFour');
}); });

View file

@ -52,14 +52,14 @@ module.exports = (app) => {
if (headers['accept']) { // note: added b/c some requests errored out due to no accept param in header if (headers['accept']) { // note: added b/c some requests errored out due to no accept param in header
const mimetypes = headers['accept'].split(','); const mimetypes = headers['accept'].split(',');
if (mimetypes.includes('text/html')) { if (mimetypes.includes('text/html')) {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
res.status(200).render('showLite', { fileInfo }); res.status(200).render('showLite', { fileInfo });
} else { } else {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
serveFile(fileInfo, res); serveFile(fileInfo, res);
} }
} else { } else {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
serveFile(fileInfo, res); serveFile(fileInfo, res);
} }
}) })
@ -82,14 +82,14 @@ module.exports = (app) => {
if (headers['accept']) { // note: added b/c some requests errored out due to no accept param in header if (headers['accept']) { // note: added b/c some requests errored out due to no accept param in header
const mimetypes = headers['accept'].split(','); const mimetypes = headers['accept'].split(',');
if (mimetypes.includes('text/html')) { if (mimetypes.includes('text/html')) {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
res.status(200).render('showLite', { fileInfo }); res.status(200).render('showLite', { fileInfo });
} else { } else {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
serveFile(fileInfo, res); serveFile(fileInfo, res);
} }
} else { } else {
postToStats('serve', originalUrl, ip, 'success'); postToStats('serve', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
serveFile(fileInfo, res); serveFile(fileInfo, res);
} }
}) })

View file

@ -1,6 +1,6 @@
const errorHandlers = require('../helpers/libraries/errorHandlers.js'); const errorHandlers = require('../helpers/libraries/errorHandlers.js');
const { getClaimByClaimId, getClaimByName, getAllClaims } = require('../controllers/serveController.js'); const { getClaimByClaimId, getClaimByName, getAllClaims } = require('../controllers/serveController.js');
const { getStatsSummary, postToStats } = require('../controllers/statsController.js'); const { postToStats, getStatsSummary, getTrendingClaims } = require('../controllers/statsController.js');
module.exports = (app) => { module.exports = (app) => {
// route to show 'about' page for spee.ch // route to show 'about' page for spee.ch
@ -8,30 +8,44 @@ module.exports = (app) => {
// get and render the content // get and render the content
res.status(200).render('about'); res.status(200).render('about');
}); });
// route to show the meme-fodder meme maker // route to display a list of the trending images
app.get('/meme-fodder/play', ({ ip, originalUrl }, res) => { app.get('/trending', ({ params, headers }, res) => {
// get and render the content const startDate = new Date();
getAllClaims('meme-fodder') startDate.setDate(startDate.getDate() - 1);
.then(orderedFreePublicClaims => { getTrendingClaims(startDate)
postToStats('show', originalUrl, ip, 'success'); .then(result => {
res.status(200).render('memeFodder', { claims: orderedFreePublicClaims }); res.status(200).render('trending', { trendingAssets: result });
}) })
.catch(error => { .catch(error => {
errorHandlers.handleRequestError('show', originalUrl, ip, error, res); errorHandlers.handleRequestError(error, res);
}); });
}); });
// route to show statistics for spee.ch // route to show statistics for spee.ch
app.get('/stats', ({ ip, originalUrl }, res) => { app.get('/stats', ({ ip, originalUrl }, res) => {
// get and render the content // get and render the content
getStatsSummary() const startDate = new Date();
startDate.setDate(startDate.getDate() - 1);
getStatsSummary(startDate)
.then(result => { .then(result => {
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, null, null, null, null, null, 'success');
res.status(200).render('statistics', result); res.status(200).render('statistics', result);
}) })
.catch(error => { .catch(error => {
errorHandlers.handleRequestError(error, res); errorHandlers.handleRequestError(error, res);
}); });
}); });
// route to show the meme-fodder meme maker
app.get('/meme-fodder/play', ({ ip, originalUrl }, res) => {
// get and render the content
getAllClaims('meme-fodder')
.then(orderedFreePublicClaims => {
postToStats('show', originalUrl, ip, null, null, null, null, null, 'success');
res.status(200).render('memeFodder', { claims: orderedFreePublicClaims });
})
.catch(error => {
errorHandlers.handleRequestError('show', originalUrl, ip, error, res);
});
});
// 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
@ -41,7 +55,7 @@ module.exports = (app) => {
res.status(307).render('noClaims'); res.status(307).render('noClaims');
return; return;
} }
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, null, null, null, null, null, 'success');
res.status(200).render('allClaims', { claims: orderedFreePublicClaims }); res.status(200).render('allClaims', { claims: orderedFreePublicClaims });
}) })
.catch(error => { .catch(error => {
@ -59,7 +73,7 @@ module.exports = (app) => {
return; return;
} }
// serve the file or the show route // serve the file or the show route
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
res.status(200).render('show', { fileInfo }); res.status(200).render('show', { fileInfo });
}) })
.catch(error => { .catch(error => {
@ -77,7 +91,7 @@ module.exports = (app) => {
return; return;
} }
// serve the show route // serve the show route
postToStats('show', originalUrl, ip, 'success'); postToStats('show', originalUrl, ip, fileInfo.name, fileInfo.claimId, fileInfo.fileName, fileInfo.fileType, fileInfo.nsfw, 'success');
res.status(200).render('show', { fileInfo }); res.status(200).render('show', { fileInfo });
}) })
.catch(error => { .catch(error => {

View file

@ -27,7 +27,7 @@ module.exports = (app, siofu, hostedContentPath) => {
// listener for when file upload encounters an error // listener for when file upload encounters an error
uploader.on('error', ({ error }) => { uploader.on('error', ({ error }) => {
logger.error('an error occured while uploading', error); logger.error('an error occured while uploading', error);
postToStats('publish', '/', null, error); postToStats('publish', '/', null, null, null, null, null, error);
socket.emit('publish-status', error); socket.emit('publish-status', error);
}); });
// listener for when file has been uploaded // listener for when file has been uploaded
@ -41,18 +41,18 @@ module.exports = (app, siofu, hostedContentPath) => {
publishController publishController
.publish(publishParams, file.name, file.meta.type) .publish(publishParams, file.name, file.meta.type)
.then(result => { .then(result => {
postToStats('publish', '/', null, 'success'); postToStats('publish', '/', null, null, null, null, null, 'success');
socket.emit('publish-complete', { name: publishParams.name, result }); socket.emit('publish-complete', { name: publishParams.name, result });
}) })
.catch(error => { .catch(error => {
error = errorHandlers.handlePublishError(error); error = errorHandlers.handlePublishError(error);
postToStats('publish', '/', null, error); postToStats('publish', '/', null, null, null, null, null, error);
socket.emit('publish-failure', error); socket.emit('publish-failure', error);
}); });
} else { } else {
logger.error(`An error occurred in uploading the client's file`); logger.error(`An error occurred in uploading the client's file`);
socket.emit('publish-failure', 'File uploaded, but with errors'); socket.emit('publish-failure', 'File uploaded, but with errors');
postToStats('publish', '/', null, 'File uploaded, but with errors'); postToStats('publish', '/', null, null, null, null, null, 'File uploaded, but with errors');
// to-do: remove the file if not done automatically // to-do: remove the file if not done automatically
} }
}); });

View file

@ -7,12 +7,11 @@ const Handlebars = require('handlebars');
const config = require('config'); const config = require('config');
const winston = require('winston'); const winston = require('winston');
const hostedContentPath = config.get('Database.PublishUploadPath'); const hostedContentPath = config.get('Database.DownloadDirectory');
// configure logging // configure logging
const logLevel = config.get('Logging.LogLevel'); const logLevel = config.get('Logging.LogLevel');
const logDir = config.get('Logging.LogDirectory'); require('./config/loggerSetup.js')(winston, logLevel);
require('./config/loggerSetup.js')(winston, logLevel, logDir);
// set port // set port
const PORT = 3000; const PORT = 3000;
@ -92,9 +91,13 @@ const server = require('./routes/sockets-routes.js')(app, siofu, hostedContentPa
// sync sequelize // sync sequelize
// wrap the server in socket.io to intercept incoming sockets requests // wrap the server in socket.io to intercept incoming sockets requests
// start server // start server
db.sequelize.sync().then(() => { db.sequelize.sync()
.then(() => {
server.listen(PORT, () => { server.listen(PORT, () => {
winston.info('Trusting proxy?', app.get('trust proxy')); winston.info('Trusting proxy?', app.get('trust proxy'));
winston.info(`Server is listening on PORT ${PORT}`); winston.info(`Server is listening on PORT ${PORT}`);
}); });
})
.catch((error) => {
winston.log('Error syncing sequelize db:', error);
}); });

View file

@ -14,3 +14,4 @@
{{> footer}} {{> footer}}
</div> </div>
<script src="/assets/js/generalFunctions.js"></script>

View file

@ -4,10 +4,12 @@
{{> publish}} {{> publish}}
{{> learnMore}} {{> learnMore}}
</div> </div>
{{> footer}}
</div> </div>
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
<script src="/siofu/client.js"></script> <script src="/siofu/client.js"></script>
<script src="/assets/js/generalFunctions.js"></script>
<script src="/assets/js/publishFunctions.js"></script> <script src="/assets/js/publishFunctions.js"></script>
<script src="/assets/js/index.js"></script> <script src="/assets/js/index.js"></script>

View file

@ -5,11 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no"> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Spee.ch</title> <title>Spee.ch</title>
<link rel="stylesheet" href="/assets/css/allStyle.css" type="text/css"> <link rel="stylesheet" href="/assets/css/generalStyle.css" type="text/css">
<link rel="stylesheet" href="/assets/css/componentStyle.css" type="text/css"> <link rel="stylesheet" href="/assets/css/componentStyle.css" type="text/css">
</head> </head>
<body> <body>
<script src="/assets/js/generalFunctions.js"></script>
{{{ body }}} {{{ body }}}
<!-- google analytics --> <!-- google analytics -->
{{ googleAnalytics }} {{ googleAnalytics }}

View file

@ -10,5 +10,6 @@
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>
<script src="/siofu/client.js"></script> <script src="/siofu/client.js"></script>
<script src="/assets/js/generalFunctions.js"></script>
<script src="/assets/js/memeFodder-draw.js"></script> <script src="/assets/js/memeFodder-draw.js"></script>
<script src="/assets/js/memeFodder-publish.js"></script> <script src="/assets/js/memeFodder-publish.js"></script>

View file

@ -1,5 +1,5 @@
<div class="panel"> <div class="panel">
<div id="asset-placeholder" data-filename="{{{fileInfo.fileName}}}"> <div id="asset-placeholder">
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
<video class="show-asset" autoplay controls> <video class="show-asset" autoplay controls>
<source src="/api/streamFile/{{fileInfo.fileName}}"> <source src="/api/streamFile/{{fileInfo.fileName}}">

View file

@ -4,12 +4,24 @@
</div> </div>
<div class="panel links"> <div class="panel links">
<h2 class="subheader">Links</h2> <h2 class="subheader">Links</h2>
{{!--direct link to asset--}}
<a href="/{{fileInfo.name}}/{{fileInfo.claimId}}">Direct Link</a> <a href="/{{fileInfo.name}}/{{fileInfo.claimId}}">Direct Link</a>
<br/> <br/>
<input type="text" id="direct-link" class="link" readonly="true" spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}"/> <input type="text" id="direct-link" class="link" readonly="true" spellcheck="false" value="https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}}"/>
<button class="copy-button" data-elementtocopy="direct-link" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="direct-link" onclick="copyToClipboard(event)">copy</button>
<br/> <br/>
<br/> <br/>
{{!--markdown text using asset--}}
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
{{else}}
Markdown
<br/>
<input type="text" id="markdown-text" class="link" readonly="true" spellcheck="false" value='![{{fileInfo.name}}](https://spee.ch/{{fileInfo.name}}/{{fileInfo.claimId}})'/>
<button class="copy-button" data-elementtocopy="markdown-text" onclick="copyToClipboard(event)">copy</button>
<br/>
<br/>
{{/ifConditional}}
{{!-- html text for embedding asset--}}
Embed HTML Embed HTML
<br/> <br/>
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
@ -20,7 +32,8 @@
<button class="copy-button" data-elementtocopy="embed-text" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="embed-text" onclick="copyToClipboard(event)">copy</button>
<br/> <br/>
<br/> <br/>
<a href="/show/{{fileInfo.name}}/{{fileInfo.claimId}}">Details</a> {{!-- link to show route for asset--}}
<a href="/show/{{fileInfo.name}}/{{fileInfo.claimId}}">Details Link</a>
</br> </br>
<input type="text" id="show-link" class="link" readonly="true" spellcheck="false" value="https://spee.ch/show/{{fileInfo.name}}/{{fileInfo.claimId}}"/> <input type="text" id="show-link" class="link" readonly="true" spellcheck="false" value="https://spee.ch/show/{{fileInfo.name}}/{{fileInfo.claimId}}"/>
<button class="copy-button" data-elementtocopy="show-link" onclick="copyToClipboard(event)">copy</button> <button class="copy-button" data-elementtocopy="show-link" onclick="copyToClipboard(event)">copy</button>
@ -43,7 +56,7 @@
<td>{{fileInfo.fileName}}</td> <td>{{fileInfo.fileName}}</td>
</tr> </tr>
<tr> <tr>
<td class="left-column">fileType</td> <td class="left-column">File Type</td>
<td>{{#if fileInfo.fileType}} <td>{{#if fileInfo.fileType}}
{{fileInfo.fileType}} {{fileInfo.fileType}}
{{else}} {{else}}
@ -53,17 +66,3 @@
</tr> </tr>
</table> </table>
</div> </div>
<script type ="text/javascript">
// update the link holder
function copyToClipboard(event){
var elementToCopy = event.target.dataset.elementtocopy;
var element = document.getElementById(elementToCopy);
element.select();
try {
var successful = document.execCommand('copy');
} catch (err) {
alert('Oops, unable to copy');
}
}
</script>

View file

@ -5,7 +5,7 @@
</div> </div>
<div class="row"> <div class="row">
{{#each claims}} {{#each claims}}
<img class="meme-fodder-img" src="/{{this.name}}/{{this.claim_id}}" onclick="newCanvas(this)"/> <img class="asset-small" src="/{{this.name}}/{{this.claim_id}}" onclick="newCanvas(this)"/>
{{/each}} {{/each}}
</div> </div>
</div> </div>

View file

@ -0,0 +1,19 @@
<div class="full">
<h2>Trending</h2>
{{#each trendingAssets}}
{{#if this.nsfw}}
{{else }}
{{#ifConditional this.fileType '===' 'video/mp4'}}
<video class="asset-small" controls>
<source src="/api/streamFile/{{this.fileName}}">
{{!--fallback--}}
Your browser does not support the <code>video</code> element.
</video>
{{else}}
<a href="/show/{{this.name}}/{{this.claimId}}">
<img class="asset-small" src="/api/streamFile/{{this.fileName}}" />
</a>
{{/ifConditional}}
{{/if}}
{{/each}}
</div>

View file

@ -8,3 +8,16 @@
</div> </div>
{{> footer}} {{> footer}}
</div> </div>
<script type ="text/javascript">
function copyToClipboard(event){
var elementToCopy = event.target.dataset.elementtocopy;
var element = document.getElementById(elementToCopy);
element.select();
try {
document.execCommand('copy');
} catch (err) {
alert('Oops, unable to copy');
}
}
</script>

View file

@ -1,4 +1,4 @@
<div id="asset-placeholder" data-filename="{{{fileInfo.fileName}}}"> <div id="asset-placeholder">
{{#ifConditional fileInfo.fileType '===' 'video/mp4'}} {{#ifConditional fileInfo.fileType '===' 'video/mp4'}}
<video class="show-asset-lite" autoplay controls> <video class="show-asset-lite" autoplay controls>
<source src="/api/streamFile/{{fileInfo.fileName}}"> <source src="/api/streamFile/{{fileInfo.fileName}}">

View file

@ -0,0 +1,4 @@
<div class="wrapper">
{{> topBar}}
{{> trendingAssets}}
</div>