Merge branch 'speech-as-a-package' of github.com:lbryio/spee.ch into folder-structure
This commit is contained in:
commit
149dca21d2
68 changed files with 567 additions and 383 deletions
|
@ -1,3 +1,4 @@
|
|||
node_modules/
|
||||
public/
|
||||
public/bundle
|
||||
index.js
|
||||
test
|
||||
|
|
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -1,7 +1,4 @@
|
|||
node_modules
|
||||
.idea
|
||||
config/sequelizeCliConfig.js
|
||||
config/speechConfig.js
|
||||
public/bundle
|
||||
server.js
|
||||
webpack.config.js
|
||||
/devConfig/sequelizeCliConfig.js
|
||||
/devConfig/testingConfig.js
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
'config': path.resolve('config', 'sequelizeCliConfig.js'),
|
||||
'config': path.resolve('devConfig', 'sequelizeCliConfig.js'),
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
# Spee.ch
|
||||
Spee.ch is a web app that reads and publishes images and videos to and from the [LBRY](https://lbry.io/) blockchain.
|
||||
|
||||
## How to run this repository locally
|
||||
##Installation
|
||||
* start mysql
|
||||
* install mysql
|
||||
* create a database called `lbry`
|
||||
|
@ -12,7 +12,8 @@ Spee.ch is a web app that reads and publishes images and videos to and from the
|
|||
* start spee.ch
|
||||
* clone this repo
|
||||
* run `npm install`
|
||||
* create your `speechConfig.js` file
|
||||
* create your `
|
||||
speechConfig.js` file
|
||||
* copy `speechConfig.js.example` and name it `speechConfig.js`
|
||||
* replace the `null` values in the config file with the appropriate values for your environment
|
||||
* build the app by running `npm run build-prod`
|
||||
|
|
8
config/lbryConfig.js
Normal file
8
config/lbryConfig.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
const lbryConfig = {
|
||||
api: {
|
||||
apiHost: 'localhost',
|
||||
apiPort: '5279',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = lbryConfig;
|
|
@ -1,21 +1,5 @@
|
|||
module.exports = (winston, logLevel) => {
|
||||
winston.configure({
|
||||
transports: [
|
||||
new (winston.transports.Console)({
|
||||
level : logLevel,
|
||||
timestamp : false,
|
||||
colorize : true,
|
||||
prettyPrint : true,
|
||||
handleExceptions : true,
|
||||
humanReadableUnhandledException: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
winston.error('Level 0');
|
||||
winston.warn('Level 1');
|
||||
winston.info('Level 2');
|
||||
winston.verbose('Level 3');
|
||||
winston.debug('Level 4');
|
||||
winston.silly('Level 5');
|
||||
const loggerConfig = {
|
||||
logLevel: 'debug', // options: silly, debug, verbose, info
|
||||
};
|
||||
|
||||
module.exports = loggerConfig;
|
||||
|
|
16
config/mysqlConfig.js
Normal file
16
config/mysqlConfig.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
function MysqlConfig () {
|
||||
this.database = 'default';
|
||||
this.username = 'default';
|
||||
this.password = 'default';
|
||||
this.configure = (config) => {
|
||||
if (!config) {
|
||||
return console.log('No MySQL config received.');
|
||||
}
|
||||
const {database, username, password} = config;
|
||||
this.database = database;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new MysqlConfig();
|
42
config/siteConfig.js
Normal file
42
config/siteConfig.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
function SiteConfig () {
|
||||
this.analytics = {
|
||||
googleId: 'default',
|
||||
};
|
||||
this.assetDefaults = {
|
||||
description: 'An asset published on Spee.ch',
|
||||
thumbnail : 'https://spee.ch/assets/img/video_thumb_default.png',
|
||||
title : 'Spee.ch',
|
||||
};
|
||||
this.auth = {
|
||||
sessionKey: 'default',
|
||||
};
|
||||
this.details = {
|
||||
description: 'Open-source, decentralized image and video sharing.',
|
||||
host : 'default',
|
||||
port : 3000,
|
||||
title : 'Spee.ch',
|
||||
twitter : '@spee_ch',
|
||||
};
|
||||
this.publishing = {
|
||||
additionalClaimAddresses: [],
|
||||
disabled : false,
|
||||
disabledMessage : 'Please check back soon.',
|
||||
primaryClaimAddress : 'default',
|
||||
thumbnailChannel : 'default',
|
||||
thumbnailChannelId : 'default',
|
||||
uploadDirectory : '/home/lbry/Uploads',
|
||||
};
|
||||
this.configure = (config) => {
|
||||
if (!config) {
|
||||
return console.log('No site config received.');
|
||||
}
|
||||
const { analytics, assetDefaults, auth, details, publishing } = config;
|
||||
this.analytics = analytics;
|
||||
this.assetDefaults = assetDefaults;
|
||||
this.auth = auth;
|
||||
this.details = details;
|
||||
this.publishing = publishing;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new SiteConfig();
|
|
@ -1,34 +1,16 @@
|
|||
const { logging } = require('./speechConfig.js');
|
||||
const { slackWebHook, slackErrorChannel, slackInfoChannel } = logging;
|
||||
const winstonSlackWebHook = require('winston-slack-webhook').SlackWebHook;
|
||||
|
||||
module.exports = (winston) => {
|
||||
if (slackWebHook) {
|
||||
// add a transport for errors to slack
|
||||
if (slackErrorChannel) {
|
||||
winston.add(winstonSlackWebHook, {
|
||||
name : 'slack-errors-transport',
|
||||
level : 'warn',
|
||||
webhookUrl: slackWebHook,
|
||||
channel : slackErrorChannel,
|
||||
username : 'spee.ch',
|
||||
iconEmoji : ':face_with_head_bandage:',
|
||||
});
|
||||
};
|
||||
if (slackInfoChannel) {
|
||||
winston.add(winstonSlackWebHook, {
|
||||
name : 'slack-info-transport',
|
||||
level : 'info',
|
||||
webhookUrl: slackWebHook,
|
||||
channel : slackInfoChannel,
|
||||
username : 'spee.ch',
|
||||
iconEmoji : ':nerd_face:',
|
||||
});
|
||||
};
|
||||
// send test message
|
||||
winston.error('Slack "error" logging is online.');
|
||||
winston.info('Slack "info" logging is online.');
|
||||
} else {
|
||||
winston.warn('Slack logging is not enabled because no slackWebHook config var provided.');
|
||||
function SlackConfig () {
|
||||
this.slackWebHook = 'default';
|
||||
this.slackErrorChannel = 'default';
|
||||
this.slackInfoChannel = 'default';
|
||||
this.configure = (config) => {
|
||||
if (!config) {
|
||||
return console.log('No slack config received.');
|
||||
}
|
||||
const {slackWebHook, slackErrorChannel, slackInfoChannel} = config;
|
||||
this.slackWebHook = slackWebHook;
|
||||
this.slackErrorChannel = slackErrorChannel;
|
||||
this.slackInfoChannel = slackInfoChannel;
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = new SlackConfig();
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
module.exports = {
|
||||
analytics: {
|
||||
googleId: null, // google id for analytics tracking; leave `null` if not applicable
|
||||
},
|
||||
sql: {
|
||||
database: 'lbry',
|
||||
username: null, // username for mysql
|
||||
password: null, // password for mysql
|
||||
},
|
||||
logging: {
|
||||
logLevel : 'debug', // options: silly, debug, verbose, info
|
||||
slackWebHook : null, // enter a webhook if you wish to push logs to slack; otherwise leave as `null`
|
||||
slackErrorChannel: null, // enter a slack channel (#example) for errors to be sent to; otherwise leave null
|
||||
slackInfoChannel : null, // enter a slack channel (#info) for info level logs to be sent to otherwise leave null
|
||||
},
|
||||
session: {
|
||||
sessionKey: null, // enter a secret key to be used for session encryption
|
||||
},
|
||||
files: {
|
||||
uploadDirectory: null, // enter file path to where uploads/publishes should be stored
|
||||
},
|
||||
site: {
|
||||
title : 'Spee.ch',
|
||||
name : 'Spee.ch',
|
||||
host : 'https://spee.ch',
|
||||
description: 'Open-source, decentralized image and video sharing.',
|
||||
},
|
||||
publish: {
|
||||
additionalClaimAddresses: [], // // optional: add previously used claim addresses
|
||||
disabled : false,
|
||||
primaryClaimAddress : null, // choose any address from your lbry wallet
|
||||
thumbnailChannel : '@channelName', // create a channel to use for thumbnail images
|
||||
thumbnailChannelId : 'xyz123...', // the channel_id (claim id) for the channel above
|
||||
},
|
||||
claim: {
|
||||
defaultTitle : 'Spee.ch',
|
||||
defaultThumbnail : 'https://spee.ch/assets/img/video_thumb_default.png',
|
||||
defaultDescription: 'Open-source, decentralized image and video sharing.',
|
||||
},
|
||||
testing: {
|
||||
testChannel : '@testpublishchannel', // a channel to make test publishes in
|
||||
testChannelId : 'xyz123...', // the claim id for the test channel
|
||||
testChannelPassword: 'password', // password for the test channel
|
||||
},
|
||||
api: {
|
||||
apiHost: 'localhost',
|
||||
apiPort: '5279',
|
||||
},
|
||||
};
|
|
@ -2,7 +2,7 @@ const logger = require('winston');
|
|||
const db = require('../models');
|
||||
const lbryApi = require('../helpers/lbryApi.js');
|
||||
const publishHelpers = require('../helpers/publishHelpers.js');
|
||||
const { publish : { primaryClaimAddress, additionalClaimAddresses } } = require('../config/speechConfig.js');
|
||||
const { publishing: { primaryClaimAddress, additionalClaimAddresses } } = require('../config/siteConfig.js');
|
||||
const Sequelize = require('sequelize');
|
||||
const Op = Sequelize.Op;
|
||||
|
||||
|
|
|
@ -2,28 +2,6 @@ const logger = require('winston');
|
|||
const db = require('../models');
|
||||
|
||||
module.exports = {
|
||||
getTrendingClaims (startDate) {
|
||||
logger.debug('retrieving trending');
|
||||
return new Promise((resolve, reject) => {
|
||||
// get the raw requests data
|
||||
db.getTrendingFiles(startDate)
|
||||
.then(fileArray => {
|
||||
let claimsPromiseArray = [];
|
||||
if (fileArray) {
|
||||
fileArray.forEach(file => {
|
||||
claimsPromiseArray.push(db.Claim.resolveClaim(file.name, file.claimId));
|
||||
});
|
||||
return Promise.all(claimsPromiseArray);
|
||||
}
|
||||
})
|
||||
.then(claimsArray => {
|
||||
resolve(claimsArray);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
getRecentClaims () {
|
||||
logger.debug('retrieving most recent claims');
|
||||
return new Promise((resolve, reject) => {
|
||||
|
|
25
devConfig/sequelizeCliConfig.js
Normal file
25
devConfig/sequelizeCliConfig.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
const sequelizeCliConfig = {
|
||||
development: {
|
||||
username: 'lbry',
|
||||
password: 'yYa5B6f7WuGq1613q9D7UWP3HT',
|
||||
database: 'lbry',
|
||||
host : '127.0.0.1',
|
||||
dialect : 'mysql',
|
||||
},
|
||||
test: {
|
||||
username: 'lbry',
|
||||
password: 'yYa5B6f7WuGq1613q9D7UWP3HT',
|
||||
database: 'lbry',
|
||||
host : '127.0.0.1',
|
||||
dialect : 'mysql',
|
||||
},
|
||||
production: {
|
||||
username: 'lbry',
|
||||
password: 'yYa5B6f7WuGq1613q9D7UWP3HT',
|
||||
database: 'lbry',
|
||||
host : '127.0.0.1',
|
||||
dialect : 'mysql',
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = sequelizeCliConfig;
|
5
devConfig/testingConfig.example.js
Normal file
5
devConfig/testingConfig.example.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
testChannel : null, // a channel to make test publishes in
|
||||
testChannelId : null, // the claim id for the test channel
|
||||
testChannelPassword: null, // password for the test channel
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
// const db = require('../models'); // require our models for syncing
|
||||
const logger = require('winston');
|
||||
|
||||
module.exports = {
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
const config = require('../config/speechConfig.js');
|
||||
const logger = require('winston');
|
||||
|
||||
module.exports = function () {
|
||||
module.exports = (config) => {
|
||||
// get the config file
|
||||
|
||||
for (let configCategoryKey in config) {
|
||||
if (config.hasOwnProperty(configCategoryKey)) {
|
||||
// get the final variables for each config category
|
||||
|
|
24
helpers/configureLogger.js
Normal file
24
helpers/configureLogger.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
const { logLevel } = require('../config/loggerConfig');
|
||||
|
||||
module.exports = (winston) => {
|
||||
// configure
|
||||
winston.configure({
|
||||
transports: [
|
||||
new (winston.transports.Console)({
|
||||
level : logLevel,
|
||||
timestamp : false,
|
||||
colorize : true,
|
||||
prettyPrint : true,
|
||||
handleExceptions : true,
|
||||
humanReadableUnhandledException: true,
|
||||
}),
|
||||
],
|
||||
});
|
||||
// test all the log levels
|
||||
winston.error('Level 0');
|
||||
winston.warn('Level 1');
|
||||
winston.info('Level 2');
|
||||
winston.verbose('Level 3');
|
||||
winston.debug('Level 4');
|
||||
winston.silly('Level 5');
|
||||
};
|
34
helpers/configureSlack.js
Normal file
34
helpers/configureSlack.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
const winstonSlackWebHook = require('winston-slack-webhook').SlackWebHook;
|
||||
const slackConfig = require('../config/slackConfig.js');
|
||||
|
||||
module.exports = (winston) => {
|
||||
const {slackWebHook, slackErrorChannel, slackInfoChannel} = slackConfig;
|
||||
if (slackWebHook) {
|
||||
// add a transport for errors to slack
|
||||
if (slackErrorChannel) {
|
||||
winston.add(winstonSlackWebHook, {
|
||||
name : 'slack-errors-transport',
|
||||
level : 'warn',
|
||||
webhookUrl: slackWebHook,
|
||||
channel : slackErrorChannel,
|
||||
username : 'spee.ch',
|
||||
iconEmoji : ':face_with_head_bandage:',
|
||||
});
|
||||
};
|
||||
if (slackInfoChannel) {
|
||||
winston.add(winstonSlackWebHook, {
|
||||
name : 'slack-info-transport',
|
||||
level : 'info',
|
||||
webhookUrl: slackWebHook,
|
||||
channel : slackInfoChannel,
|
||||
username : 'spee.ch',
|
||||
iconEmoji : ':nerd_face:',
|
||||
});
|
||||
};
|
||||
// send test message
|
||||
winston.error('Slack "error" logging is online.');
|
||||
winston.info('Slack "info" logging is online.');
|
||||
} else {
|
||||
winston.warn('Slack logging is not enabled because no slackWebHook config var provided.');
|
||||
}
|
||||
};
|
|
@ -1,6 +1,6 @@
|
|||
const logger = require('winston');
|
||||
const ua = require('universal-analytics');
|
||||
const { analytics : { googleId }, site: { name: siteName } } = require('../config/speechConfig.js');
|
||||
const { analytics : { googleId }, details: { title } } = require('../config/siteConfig.js');
|
||||
|
||||
function createServeEventParams (headers, ip, originalUrl) {
|
||||
return {
|
||||
|
@ -49,7 +49,7 @@ module.exports = {
|
|||
},
|
||||
sendGATimingEvent (category, variable, label, startTime, endTime) {
|
||||
const params = createPublishTimingEventParams(category, variable, label, startTime, endTime);
|
||||
sendGoogleAnalyticsTiming(siteName, params);
|
||||
sendGoogleAnalyticsTiming(title, params);
|
||||
},
|
||||
chooseGaLbrynetPublishLabel ({ channel_name: channelName, channel_id: channelId }) {
|
||||
return (channelName || channelId ? 'PUBLISH_IN_CHANNEL_CLAIM' : 'PUBLISH_ANONYMOUS_CLAIM');
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
const axios = require('axios');
|
||||
const logger = require('winston');
|
||||
const config = require('../config/speechConfig.js');
|
||||
const { apiHost, apiPort } = config.api;
|
||||
const { api: { apiHost, apiPort } } = require('../config/lbryConfig.js');
|
||||
const lbryApiUri = 'http://' + apiHost + ':' + apiPort;
|
||||
const { chooseGaLbrynetPublishLabel, sendGATimingEvent } = require('./googleAnalytics.js');
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const logger = require('winston');
|
||||
const fs = require('fs');
|
||||
const { site, publish } = require('../config/speechConfig.js');
|
||||
|
||||
const { details, publishing } = require('../config/siteConfig.js');
|
||||
|
||||
module.exports = {
|
||||
parsePublishApiRequestBody ({name, nsfw, license, title, description, thumbnail}) {
|
||||
|
@ -109,12 +110,12 @@ module.exports = {
|
|||
metadata : {
|
||||
description,
|
||||
title,
|
||||
author : site.title,
|
||||
author : details.title,
|
||||
language: 'en',
|
||||
license,
|
||||
nsfw,
|
||||
},
|
||||
claim_address: publish.primaryClaimAddress,
|
||||
claim_address: publishing.primaryClaimAddress,
|
||||
};
|
||||
// add thumbnail to channel if video
|
||||
if (thumbnail) {
|
||||
|
@ -135,14 +136,14 @@ module.exports = {
|
|||
metadata : {
|
||||
title : `${claimName} thumbnail`,
|
||||
description: `a thumbnail for ${claimName}`,
|
||||
author : site.title,
|
||||
author : details.title,
|
||||
language : 'en',
|
||||
license,
|
||||
nsfw,
|
||||
},
|
||||
claim_address: publish.primaryClaimAddress,
|
||||
channel_name : publish.thumbnailChannel,
|
||||
channel_id : publish.thumbnailChannelId,
|
||||
claim_address: publishing.primaryClaimAddress,
|
||||
channel_name : publishing.thumbnailChannel,
|
||||
channel_id : publishing.thumbnailChannelId,
|
||||
};
|
||||
},
|
||||
deleteTemporaryFile (filePath) {
|
||||
|
|
86
index.js
86
index.js
File diff suppressed because one or more lines are too long
1
index.js.map
Normal file
1
index.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,6 @@
|
|||
const logger = require('winston');
|
||||
const { returnShortId } = require('../helpers/sequelizeHelpers.js');
|
||||
const { claim, site } = require('../config/speechConfig.js');
|
||||
const { defaultThumbnail } = claim;
|
||||
const { assetDefaults: { thumbnail: defaultThumbnail }, details: { host } } = require('../config/siteConfig.js');
|
||||
|
||||
function determineFileExtensionFromContentType (contentType) {
|
||||
switch (contentType) {
|
||||
|
@ -31,7 +30,7 @@ function prepareClaimData (claim) {
|
|||
// logger.debug('preparing claim data based on resolved data:', claim);
|
||||
claim['thumbnail'] = determineThumbnail(claim.thumbnail, defaultThumbnail);
|
||||
claim['fileExt'] = determineFileExtensionFromContentType(claim.contentType);
|
||||
claim['host'] = site.host;
|
||||
claim['host'] = host;
|
||||
return claim;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const Sequelize = require('sequelize');
|
||||
const logger = require('winston');
|
||||
const config = require('../config/speechConfig.js');
|
||||
const { database, username, password } = config.sql;
|
||||
const db = {};
|
||||
|
||||
console.log('exporting sequelize models');
|
||||
const { database, username, password } = require('../config/mysqlConfig');
|
||||
const db = {};
|
||||
// set sequelize options
|
||||
const sequelize = new Sequelize(database, username, password, {
|
||||
host : 'localhost',
|
||||
|
@ -18,6 +18,7 @@ const sequelize = new Sequelize(database, username, password, {
|
|||
},
|
||||
});
|
||||
|
||||
// establish mysql connection
|
||||
sequelize
|
||||
.authenticate()
|
||||
.then(() => {
|
||||
|
@ -27,7 +28,7 @@ sequelize
|
|||
logger.error('Sequelize was unable to connect to the database:', err);
|
||||
});
|
||||
|
||||
// manually add each model to the db
|
||||
// manually add each model to the db object
|
||||
const Certificate = require('./certificate.js');
|
||||
const Channel = require('./channel.js');
|
||||
const Claim = require('./claim.js');
|
||||
|
@ -55,7 +56,9 @@ db.Sequelize = Sequelize;
|
|||
// add an 'upsert' method to the db object
|
||||
db.upsert = (Model, values, condition, tableName) => {
|
||||
return Model
|
||||
.findOne({ where: condition })
|
||||
.findOne({
|
||||
where: condition,
|
||||
})
|
||||
.then(obj => {
|
||||
if (obj) { // update
|
||||
logger.debug(`updating record in db.${tableName}`);
|
||||
|
@ -71,9 +74,4 @@ db.upsert = (Model, values, condition, tableName) => {
|
|||
});
|
||||
};
|
||||
|
||||
// add a 'getTrendingFiles' method to the db object. note: change this to get claims directly. might need new association between Request and Claim
|
||||
db.getTrendingFiles = (startDate) => {
|
||||
return db.sequelize.query(`SELECT COUNT(*), File.* FROM Request LEFT JOIN File ON Request.FileId = File.id WHERE FileId IS NOT NULL AND nsfw != 1 AND trendingEligible = 1 AND Request.createdAt > "${startDate}" GROUP BY FileId ORDER BY COUNT(*) DESC LIMIT 25;`, { type: db.sequelize.QueryTypes.SELECT });
|
||||
};
|
||||
|
||||
module.exports = db;
|
||||
|
|
13
package.json
13
package.json
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"name": "spee.ch",
|
||||
"version": "0.0.1",
|
||||
"description": "a single-serving site that reads and publishes images to and from the LBRY blockchain",
|
||||
"main": "speech.js",
|
||||
"description": "a web application that reads and publishes images to and from the LBRY blockchain",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha --recursive",
|
||||
"test-all": "mocha --recursive",
|
||||
"start": "node server.js",
|
||||
"start-dev": "nodemon server.js",
|
||||
"start": "node index.js",
|
||||
"start-dev": "nodemon index.js",
|
||||
"lint": "eslint .",
|
||||
"fix": "eslint . --fix",
|
||||
"precommit": "eslint .",
|
||||
"babel": "babel",
|
||||
"build-dev": "webpack --config webpack.dev.js",
|
||||
"build-prod": "webpack --config webpack.prod.js"
|
||||
"build": "webpack --config webpack.prod.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -34,6 +34,7 @@
|
|||
"axios": "^0.16.1",
|
||||
"bcrypt": "^1.0.3",
|
||||
"body-parser": "^1.17.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"config": "^1.26.1",
|
||||
"connect-multiparty": "^2.0.0",
|
||||
"cookie-session": "^2.0.0-beta.3",
|
||||
|
@ -59,7 +60,6 @@
|
|||
"request-promise": "^4.2.2",
|
||||
"sequelize": "^4.1.0",
|
||||
"sequelize-cli": "^3.0.0-3",
|
||||
"sleep": "^5.1.1",
|
||||
"universal-analytics": "^0.4.13",
|
||||
"webpack-node-externals": "^1.6.0",
|
||||
"whatwg-fetch": "^2.0.3",
|
||||
|
@ -70,7 +70,6 @@
|
|||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.2",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
const PassportLocalStrategy = require('passport-local').Strategy;
|
||||
const db = require('../models');
|
||||
const logger = require('winston');
|
||||
const db = require('../models');
|
||||
|
||||
function returnUserAndChannelInfo (userInstance) {
|
||||
const returnUserAndChannelInfo = (userInstance) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let userInfo = {};
|
||||
userInfo['id'] = userInstance.id;
|
||||
|
@ -22,7 +22,7 @@ function returnUserAndChannelInfo (userInstance) {
|
|||
reject(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = new PassportLocalStrategy(
|
||||
{
|
||||
|
@ -61,5 +61,5 @@ module.exports = new PassportLocalStrategy(
|
|||
.catch(error => {
|
||||
return done(error);
|
||||
});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const db = require('../models');
|
||||
const PassportLocalStrategy = require('passport-local').Strategy;
|
||||
const lbryApi = require('../helpers/lbryApi.js');
|
||||
const logger = require('winston');
|
||||
const db = require('../models');
|
||||
|
||||
module.exports = new PassportLocalStrategy(
|
||||
{
|
||||
|
|
28
public/bundle/bundle.js
Normal file
28
public/bundle/bundle.js
Normal file
File diff suppressed because one or more lines are too long
1
public/bundle/bundle.js.map
Normal file
1
public/bundle/bundle.js.map
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,6 @@
|
|||
import Request from 'utils/request';
|
||||
const { site: { host } } = require('../../config/speechConfig.js');
|
||||
|
||||
export function getLongClaimId (name, modifier) {
|
||||
export function getLongClaimId (host, name, modifier) {
|
||||
let body = {};
|
||||
// create request params
|
||||
if (modifier) {
|
||||
|
@ -24,12 +23,12 @@ export function getLongClaimId (name, modifier) {
|
|||
return Request(url, params);
|
||||
};
|
||||
|
||||
export function getShortId (name, claimId) {
|
||||
export function getShortId (host, name, claimId) {
|
||||
const url = `${host}/api/claim/short-id/${claimId}/${name}`;
|
||||
return Request(url);
|
||||
};
|
||||
|
||||
export function getClaimData (name, claimId) {
|
||||
export function getClaimData (host, name, claimId) {
|
||||
const url = `${host}/api/claim/data/${name}/${claimId}`;
|
||||
return Request(url);
|
||||
};
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import Request from 'utils/request';
|
||||
const { site: { host } } = require('../../config/speechConfig.js');
|
||||
|
||||
export function getChannelData (name, id) {
|
||||
export function getChannelData (host, id, name) {
|
||||
if (!id) id = 'none';
|
||||
const url = `${host}/api/channel/data/${name}/${id}`;
|
||||
return Request(url);
|
||||
};
|
||||
|
||||
export function getChannelClaims (name, longId, page) {
|
||||
export function getChannelClaims (host, longId, name, page) {
|
||||
if (!page) page = 1;
|
||||
const url = `${host}/api/channel/claims/${name}/${longId}/${page}`;
|
||||
return Request(url);
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import Request from 'utils/request';
|
||||
const { site: { host } } = require('../../config/speechConfig.js');
|
||||
|
||||
export function checkFileAvailability (name, claimId) {
|
||||
export function checkFileAvailability (claimId, host, name) {
|
||||
const url = `${host}/api/file/availability/${name}/${claimId}`;
|
||||
return Request(url);
|
||||
}
|
||||
|
||||
export function triggerClaimGet (name, claimId) {
|
||||
export function triggerClaimGet (claimId, host, name) {
|
||||
const url = `${host}/api/claim/get/${name}/${claimId}`;
|
||||
return Request(url);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import HomePage from 'components/HomePage';
|
|||
import AboutPage from 'components/AboutPage';
|
||||
import LoginPage from 'containers/LoginPage';
|
||||
import ShowPage from 'containers/ShowPage';
|
||||
import FourOhFourPage from 'components/FourOhFourPage';
|
||||
import FourOhFourPage from 'containers/FourOhFourPage';
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
|
|
10
react/components/AssetPreview/index.js
Normal file
10
react/components/AssetPreview/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({site: {defaults: { defaultThumbnail }}}) => {
|
||||
return {
|
||||
defaultThumbnail,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(View);
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
const { claim: { defaultThumbnail } } = require('../../../config/speechConfig.js');
|
||||
|
||||
const AssetPreview = ({ claimData: { name, claimId, fileExt, contentType, thumbnail } }) => {
|
||||
const AssetPreview = ({ defaultThumbnail, claimData: { name, claimId, fileExt, contentType, thumbnail } }) => {
|
||||
const directSourceLink = `${claimId}/${name}.${fileExt}`;
|
||||
const showUrlLink = `/${claimId}/${name}`;
|
||||
return (
|
|
@ -1,10 +1,9 @@
|
|||
import React from 'react';
|
||||
import GoogleAnalytics from 'react-ga';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
const config = require('../../../config/speechConfig.js');
|
||||
const googleApiKey = config.analytics.googleId;
|
||||
const { analytics: { googleId } } = require('../../../config/siteConfig.js');
|
||||
|
||||
GoogleAnalytics.initialize(googleApiKey);
|
||||
GoogleAnalytics.initialize(googleId);
|
||||
|
||||
class GAListener extends React.Component {
|
||||
componentDidMount () {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Preview extends React.Component {
|
||||
class PublishPreview extends React.Component {
|
||||
constructor (props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -53,10 +53,10 @@ class Preview extends React.Component {
|
|||
}
|
||||
};
|
||||
|
||||
Preview.propTypes = {
|
||||
PublishPreview.propTypes = {
|
||||
dimPreview: PropTypes.bool.isRequired,
|
||||
file : PropTypes.object.isRequired,
|
||||
thumbnail : PropTypes.object,
|
||||
};
|
||||
|
||||
export default Preview;
|
||||
export default PublishPreview;
|
16
react/components/SEO/index.js
Normal file
16
react/components/SEO/index.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { connect } from 'react-redux';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ site }) => {
|
||||
const { defaultDescription, defaultThumbnail, description: siteDescription, host: siteHost, title: siteTitle, twitter: siteTwitter } = site;
|
||||
return {
|
||||
defaultDescription,
|
||||
defaultThumbnail,
|
||||
siteDescription,
|
||||
siteHost,
|
||||
siteTitle,
|
||||
siteTwitter,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(View);
|
|
@ -8,10 +8,16 @@ import { createCanonicalLink } from 'utils/canonicalLink';
|
|||
|
||||
class SEO extends React.Component {
|
||||
render () {
|
||||
let { pageTitle, asset, channel, pageUri } = this.props;
|
||||
pageTitle = createPageTitle(pageTitle);
|
||||
const metaTags = createMetaTags(asset, channel);
|
||||
const canonicalLink = createCanonicalLink(asset, channel, pageUri);
|
||||
// props from state
|
||||
const { defaultDescription, defaultThumbnail, siteDescription, siteHost, siteTitle, siteTwitter } = this.props;
|
||||
// props from parent
|
||||
const { asset, channel, pageUri } = this.props;
|
||||
let { pageTitle } = this.props;
|
||||
// create page title, tags, and canonical link
|
||||
pageTitle = createPageTitle(siteTitle, pageTitle);
|
||||
const metaTags = createMetaTags(siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail);
|
||||
const canonicalLink = createCanonicalLink(asset, channel, pageUri, siteHost);
|
||||
// render results
|
||||
return (
|
||||
<Helmet
|
||||
title={pageTitle}
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { validateFile } from 'utils/file';
|
||||
import Preview from 'components/Preview';
|
||||
import PublishPreview from 'components/PublishPreview';
|
||||
|
||||
class Dropzone extends React.Component {
|
||||
constructor (props) {
|
||||
|
@ -87,7 +87,7 @@ class Dropzone extends React.Component {
|
|||
<div id='preview-dropzone' className={'row row--padded row--tall dropzone' + (this.state.dragOver ? ' dropzone--drag-over' : '')} onDrop={this.handleDrop} onDragOver={this.handleDragOver} onDragEnd={this.handleDragEnd} onDragEnter={this.handleDragEnter} onDragLeave={this.handleDragLeave} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} onClick={this.handleClick}>
|
||||
{this.props.file ? (
|
||||
<div>
|
||||
<Preview
|
||||
<PublishPreview
|
||||
dimPreview={this.state.dimPreview}
|
||||
file={this.props.file}
|
||||
thumbnail={this.props.thumbnail}
|
||||
|
|
11
react/containers/FourOhFourPage/index.jsx
Normal file
11
react/containers/FourOhFourPage/index.jsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { connect } from 'react-redux';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ site: { host, title } }) => {
|
||||
return {
|
||||
host,
|
||||
title,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(View);
|
|
@ -1,10 +1,10 @@
|
|||
import React from 'react';
|
||||
import NavBar from 'containers/NavBar';
|
||||
import Helmet from 'react-helmet';
|
||||
const { site: { title, host } } = require('../../../config/speechConfig.js');
|
||||
|
||||
class FourOhForPage extends React.Component {
|
||||
render () {
|
||||
const {title, host} = this.props;
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
|
@ -1,13 +1,14 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { updateLoggedInChannel } from 'actions/channel';
|
||||
import {updateSelectedChannel} from 'actions/publish';
|
||||
import View from './view';
|
||||
import {updateSelectedChannel} from '../../actions/publish';
|
||||
|
||||
const mapStateToProps = ({ channel }) => {
|
||||
const mapStateToProps = ({ channel, site }) => {
|
||||
return {
|
||||
channelName : channel.loggedInChannel.name,
|
||||
channelShortId: channel.loggedInChannel.shortId,
|
||||
channelLongId : channel.loggedInChannel.longId,
|
||||
siteDescription: site.description,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -53,12 +53,13 @@ class NavBar extends React.Component {
|
|||
}
|
||||
}
|
||||
render () {
|
||||
const { siteDescription } = this.props;
|
||||
return (
|
||||
<div className='row row--wide nav-bar'>
|
||||
<div className='row row--padded row--short flex-container--row flex-container--space-between-center'>
|
||||
<Logo />
|
||||
<div className='nav-bar--center'>
|
||||
<span className='nav-bar-tagline'>Open-source, decentralized image and video sharing.</span>
|
||||
<span className='nav-bar-tagline'>{siteDescription}</span>
|
||||
</div>
|
||||
<div className='nav-bar--right'>
|
||||
<NavLink className='nav-bar-link link--nav' activeClassName='link--nav-active' to='/' exact>Publish</NavLink>
|
||||
|
|
10
react/containers/PublishDisabledMessage/index.js
Normal file
10
react/containers/PublishDisabledMessage/index.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
import {connect} from 'react-redux';
|
||||
import View from './view';
|
||||
|
||||
const mapStateToProps = ({ publish }) => {
|
||||
return {
|
||||
message: publish.disabledMessage,
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, null)(View);
|
16
react/containers/PublishDisabledMessage/view.jsx
Normal file
16
react/containers/PublishDisabledMessage/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
|
||||
class PublishDisabledMessage extends React.Component {
|
||||
render () {
|
||||
const message = this.props.message;
|
||||
console.log('this.props.message:', message);
|
||||
return (
|
||||
<div className='row dropzone--disabled row--tall flex-container--column flex-container--center-center'>
|
||||
<p className='text--disabled'>Publishing is currently disabled.</p>
|
||||
<p className='text--disabled'>{message}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PublishDisabledMessage;
|
|
@ -2,17 +2,17 @@ import React from 'react';
|
|||
import Dropzone from 'containers/Dropzone';
|
||||
import PublishDetails from 'containers/PublishDetails';
|
||||
import PublishStatus from 'containers/PublishStatus';
|
||||
import PublishDisabledMessage from 'containers/PublishDisabledMessage';
|
||||
|
||||
class PublishTool extends React.Component {
|
||||
render () {
|
||||
if (this.props.disabled) {
|
||||
console.log('publish is disabled');
|
||||
return (
|
||||
<div className='row dropzone--disabled row--tall flex-container--column flex-container--center-center'>
|
||||
<p className='text--disabled'>Publishing is temporarily disabled.</p>
|
||||
<p className='text--disabled'>Please check back soon or join our <a className='link--disabled-text' href='https://discord.gg/YjYbwhS'>discord channel</a> for updates.</p>
|
||||
</div>
|
||||
<PublishDisabledMessage />
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.log('publish is not disabled');
|
||||
if (this.props.file) {
|
||||
if (this.props.status) {
|
||||
return (
|
||||
|
@ -24,6 +24,7 @@ class PublishTool extends React.Component {
|
|||
}
|
||||
return <Dropzone />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default PublishTool;
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import * as actions from 'constants/publish_action_types';
|
||||
import { LOGIN } from 'constants/publish_channel_select_states';
|
||||
const { publish } = require('../../config/speechConfig.js');
|
||||
const { publishing } = require('../../config/siteConfig.js');
|
||||
|
||||
const initialState = {
|
||||
disabled : publish.disabled,
|
||||
disabled : publishing.disabled,
|
||||
disabledMessage : publishing.disabledMessage,
|
||||
publishInChannel : false,
|
||||
selectedChannel : LOGIN,
|
||||
showMetadataInputs: false,
|
||||
|
@ -25,9 +26,7 @@ const initialState = {
|
|||
license : '',
|
||||
nsfw : false,
|
||||
},
|
||||
thumbnailChannel : publish.thumbnailChannel,
|
||||
thumbnailChannelId: publish.thumbnailChannelId,
|
||||
thumbnail : null,
|
||||
thumbnail: null,
|
||||
};
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
|
|
|
@ -1,7 +1,29 @@
|
|||
const { site } = require('../../config/speechConfig.js');
|
||||
const siteConfig = require('../../config/siteConfig.js');
|
||||
|
||||
const {
|
||||
analytics: {
|
||||
googleId: googleAnalyticsId,
|
||||
},
|
||||
assetDefaults: {
|
||||
thumbnail: defaultThumbnail,
|
||||
description: defaultDescription,
|
||||
},
|
||||
details: {
|
||||
description,
|
||||
host,
|
||||
title,
|
||||
twitter,
|
||||
},
|
||||
} = siteConfig;
|
||||
|
||||
const initialState = {
|
||||
host: site.host,
|
||||
description,
|
||||
googleAnalyticsId,
|
||||
host,
|
||||
title,
|
||||
twitter,
|
||||
defaultDescription,
|
||||
defaultThumbnail,
|
||||
};
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
|
|
|
@ -1,16 +1,18 @@
|
|||
import { call, put, takeLatest } from 'redux-saga/effects';
|
||||
import {call, put, select, takeLatest} from 'redux-saga/effects';
|
||||
import * as actions from 'constants/show_action_types';
|
||||
import { updateFileAvailability, updateDisplayAssetError } from 'actions/show';
|
||||
import { UNAVAILABLE, AVAILABLE } from 'constants/asset_display_states';
|
||||
import { checkFileAvailability, triggerClaimGet } from 'api/fileApi';
|
||||
import { selectSiteHost } from 'selectors/site';
|
||||
|
||||
function * retrieveFile (action) {
|
||||
const name = action.data.name;
|
||||
const claimId = action.data.claimId;
|
||||
const host = yield select(selectSiteHost);
|
||||
// see if the file is available
|
||||
let isAvailable;
|
||||
try {
|
||||
({ data: isAvailable } = yield call(checkFileAvailability, name, claimId));
|
||||
({ data: isAvailable } = yield call(checkFileAvailability, claimId, host, name));
|
||||
} catch (error) {
|
||||
return yield put(updateDisplayAssetError(error.message));
|
||||
};
|
||||
|
@ -21,7 +23,7 @@ function * retrieveFile (action) {
|
|||
yield put(updateFileAvailability(UNAVAILABLE));
|
||||
// initiate get request for the file
|
||||
try {
|
||||
yield call(triggerClaimGet, name, claimId);
|
||||
yield call(triggerClaimGet, claimId, host, name);
|
||||
} catch (error) {
|
||||
return yield put(updateDisplayAssetError(error.message));
|
||||
};
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as actions from 'constants/show_action_types';
|
|||
import { addRequestToRequestList, onRequestError, onRequestUpdate, addAssetToAssetList } from 'actions/show';
|
||||
import { getLongClaimId, getShortId, getClaimData } from 'api/assetApi';
|
||||
import { selectShowState } from 'selectors/show';
|
||||
import { selectSiteHost } from 'selectors/site';
|
||||
|
||||
export function * newAssetRequest (action) {
|
||||
const { requestType, requestId, name, modifier } = action.data;
|
||||
|
@ -11,13 +12,14 @@ export function * newAssetRequest (action) {
|
|||
// is this an existing request?
|
||||
// If this uri is in the request list, it's already been fetched
|
||||
const state = yield select(selectShowState);
|
||||
const host = yield select(selectSiteHost);
|
||||
if (state.requestList[requestId]) {
|
||||
return null;
|
||||
}
|
||||
// get long id && add request to request list
|
||||
let longId;
|
||||
try {
|
||||
({data: longId} = yield call(getLongClaimId, name, modifier));
|
||||
({data: longId} = yield call(getLongClaimId, host, name, modifier));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
|
@ -31,14 +33,14 @@ export function * newAssetRequest (action) {
|
|||
// get short Id
|
||||
let shortId;
|
||||
try {
|
||||
({data: shortId} = yield call(getShortId, name, longId));
|
||||
({data: shortId} = yield call(getShortId, host, name, longId));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
// get asset claim data
|
||||
let claimData;
|
||||
try {
|
||||
({data: claimData} = yield call(getClaimData, name, longId));
|
||||
({data: claimData} = yield call(getClaimData, host, name, longId));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import * as actions from 'constants/show_action_types';
|
|||
import { addNewChannelToChannelList, addRequestToRequestList, onRequestError, onRequestUpdate, updateChannelClaims } from 'actions/show';
|
||||
import { getChannelClaims, getChannelData } from 'api/channelApi';
|
||||
import { selectShowState } from 'selectors/show';
|
||||
import { selectSiteHost } from 'selectors/site';
|
||||
|
||||
export function * newChannelRequest (action) {
|
||||
const { requestType, requestId, channelName, channelId } = action.data;
|
||||
|
@ -11,13 +12,14 @@ export function * newChannelRequest (action) {
|
|||
// is this an existing request?
|
||||
// If this uri is in the request list, it's already been fetched
|
||||
const state = yield select(selectShowState);
|
||||
const host = yield select(selectSiteHost);
|
||||
if (state.requestList[requestId]) {
|
||||
return null;
|
||||
}
|
||||
// get channel long id
|
||||
let longId, shortId;
|
||||
try {
|
||||
({ data: {longChannelClaimId: longId, shortChannelClaimId: shortId} } = yield call(getChannelData, channelName, channelId));
|
||||
({ data: {longChannelClaimId: longId, shortChannelClaimId: shortId} } = yield call(getChannelData, host, channelName, channelId));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
|
@ -32,7 +34,7 @@ export function * newChannelRequest (action) {
|
|||
// get channel claims data
|
||||
let claimsData;
|
||||
try {
|
||||
({ data: claimsData } = yield call(getChannelClaims, channelName, longId, 1));
|
||||
({ data: claimsData } = yield call(getChannelClaims, host, longId, channelName, 1));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
|
@ -48,9 +50,10 @@ export function * watchNewChannelRequest () {
|
|||
|
||||
function * getNewClaimsAndUpdateChannel (action) {
|
||||
const { channelKey, name, longId, page } = action.data;
|
||||
const host = yield select(selectSiteHost);
|
||||
let claimsData;
|
||||
try {
|
||||
({ data: claimsData } = yield call(getChannelClaims, name, longId, page));
|
||||
({ data: claimsData } = yield call(getChannelClaims, host, longId, name, page));
|
||||
} catch (error) {
|
||||
return yield put(onRequestError(error.message));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
export const selectSiteState = (state) => {
|
||||
return state.site;
|
||||
};
|
||||
|
||||
export const selectSiteHost = (state) => {
|
||||
return state.site.host;
|
||||
};
|
||||
|
|
|
@ -1,37 +1,29 @@
|
|||
const { site: { host } } = require('../../config/speechConfig.js');
|
||||
|
||||
const createBasicCanonicalLink = (page) => {
|
||||
if (!page) {
|
||||
return `${host}`;
|
||||
};
|
||||
return `${host}/${page}`;
|
||||
const createBasicCanonicalLink = (page, siteHost) => {
|
||||
return `${siteHost}/${page}`;
|
||||
};
|
||||
|
||||
const createAssetCanonicalLink = (asset) => {
|
||||
const createAssetCanonicalLink = (asset, siteHost) => {
|
||||
let channelName, certificateId, name, claimId;
|
||||
if (asset.claimData) {
|
||||
({ channelName, certificateId, name, claimId } = asset.claimData);
|
||||
};
|
||||
if (channelName) {
|
||||
return `${host}/${channelName}:${certificateId}/${name}`;
|
||||
return `${siteHost}/${channelName}:${certificateId}/${name}`;
|
||||
};
|
||||
return `${host}/${claimId}/${name}`;
|
||||
return `${siteHost}/${claimId}/${name}`;
|
||||
};
|
||||
|
||||
const createChannelCanonicalLink = (channel) => {
|
||||
const createChannelCanonicalLink = (channel, siteHost) => {
|
||||
const { name, longId } = channel;
|
||||
return `${host}/${name}:${longId}`;
|
||||
return `${siteHost}/${name}:${longId}`;
|
||||
};
|
||||
|
||||
export const createCanonicalLink = (asset, channel, page) => {
|
||||
export const createCanonicalLink = (asset, channel, page, siteHost) => {
|
||||
if (asset) {
|
||||
return createAssetCanonicalLink(asset);
|
||||
return createAssetCanonicalLink(asset, siteHost);
|
||||
}
|
||||
if (channel) {
|
||||
return createChannelCanonicalLink(channel);
|
||||
return createChannelCanonicalLink(channel, siteHost);
|
||||
}
|
||||
if (page) {
|
||||
return createBasicCanonicalLink(page);
|
||||
}
|
||||
return createBasicCanonicalLink();
|
||||
return createBasicCanonicalLink(page, siteHost);
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
const { site: { title, host, description }, claim: { defaultThumbnail, defaultDescription } } = require('../../config/speechConfig.js');
|
||||
|
||||
const determineOgThumbnailContentType = (thumbnail) => {
|
||||
if (thumbnail) {
|
||||
const fileExt = thumbnail.substring(thumbnail.lastIndexOf('.'));
|
||||
|
@ -20,35 +18,35 @@ const determineOgThumbnailContentType = (thumbnail) => {
|
|||
return '';
|
||||
};
|
||||
|
||||
const createBasicMetaTags = () => {
|
||||
const createBasicMetaTags = (siteHost, siteDescription, siteTitle, siteTwitter) => {
|
||||
return [
|
||||
{property: 'og:title', content: title},
|
||||
{property: 'og:url', content: host},
|
||||
{property: 'og:site_name', content: title},
|
||||
{property: 'og:description', content: description},
|
||||
{property: 'twitter:site', content: '@spee_ch'},
|
||||
{property: 'og:title', content: siteTitle},
|
||||
{property: 'og:url', content: siteHost},
|
||||
{property: 'og:site_name', content: siteTitle},
|
||||
{property: 'og:description', content: siteDescription},
|
||||
{property: 'twitter:site', content: siteTwitter},
|
||||
{property: 'twitter:card', content: 'summary'},
|
||||
];
|
||||
};
|
||||
|
||||
const createChannelMetaTags = (channel) => {
|
||||
const createChannelMetaTags = (siteTitle, siteHost, siteTwitter, channel) => {
|
||||
const { name, longId } = channel;
|
||||
return [
|
||||
{property: 'og:title', content: `${name} on ${title}`},
|
||||
{property: 'og:url', content: `${host}/${name}:${longId}`},
|
||||
{property: 'og:site_name', content: title},
|
||||
{property: 'og:description', content: `${name}, a channel on ${title}`},
|
||||
{property: 'twitter:site', content: '@spee_ch'},
|
||||
{property: 'og:title', content: `${name} on ${siteTitle}`},
|
||||
{property: 'og:url', content: `${siteHost}/${name}:${longId}`},
|
||||
{property: 'og:site_name', content: siteTitle},
|
||||
{property: 'og:description', content: `${name}, a channel on ${siteTitle}`},
|
||||
{property: 'twitter:site', content: siteTwitter},
|
||||
{property: 'twitter:card', content: 'summary'},
|
||||
];
|
||||
};
|
||||
|
||||
const createAssetMetaTags = (asset) => {
|
||||
const createAssetMetaTags = (siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail) => {
|
||||
const { claimData } = asset;
|
||||
const { contentType } = claimData;
|
||||
const embedUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
||||
const showUrl = `${host}/${claimData.claimId}/${claimData.name}`;
|
||||
const source = `${host}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
|
||||
const embedUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
|
||||
const showUrl = `${siteHost}/${claimData.claimId}/${claimData.name}`;
|
||||
const source = `${siteHost}/${claimData.claimId}/${claimData.name}.${claimData.fileExt}`;
|
||||
const ogTitle = claimData.title || claimData.name;
|
||||
const ogDescription = claimData.description || defaultDescription;
|
||||
const ogThumbnailContentType = determineOgThumbnailContentType(claimData.thumbnail);
|
||||
|
@ -56,11 +54,11 @@ const createAssetMetaTags = (asset) => {
|
|||
const metaTags = [
|
||||
{property: 'og:title', content: ogTitle},
|
||||
{property: 'og:url', content: showUrl},
|
||||
{property: 'og:site_name', content: title},
|
||||
{property: 'og:site_name', content: siteTitle},
|
||||
{property: 'og:description', content: ogDescription},
|
||||
{property: 'og:image:width', content: 600},
|
||||
{property: 'og:image:height', content: 315},
|
||||
{property: 'twitter:site', content: '@spee_ch'},
|
||||
{property: 'twitter:site', content: siteTwitter},
|
||||
];
|
||||
if (contentType === 'video/mp4' || contentType === 'video/webm') {
|
||||
metaTags.push({property: 'og:video', content: source});
|
||||
|
@ -85,12 +83,12 @@ const createAssetMetaTags = (asset) => {
|
|||
return metaTags;
|
||||
};
|
||||
|
||||
export const createMetaTags = (asset, channel) => {
|
||||
export const createMetaTags = (siteDescription, siteHost, siteTitle, siteTwitter, asset, channel, defaultDescription, defaultThumbnail) => {
|
||||
if (asset) {
|
||||
return createAssetMetaTags(asset);
|
||||
return createAssetMetaTags(siteHost, siteTitle, siteTwitter, asset, defaultDescription, defaultThumbnail);
|
||||
};
|
||||
if (channel) {
|
||||
return createChannelMetaTags(channel);
|
||||
return createChannelMetaTags(siteHost, siteTitle, siteTwitter, channel);
|
||||
};
|
||||
return createBasicMetaTags();
|
||||
return createBasicMetaTags(siteDescription, siteHost, siteTitle, siteTwitter);
|
||||
};
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const { site: { title: siteTitle } } = require('../../config/speechConfig.js');
|
||||
|
||||
export const createPageTitle = (pageTitle) => {
|
||||
export const createPageTitle = (siteTitle, pageTitle) => {
|
||||
if (!pageTitle) {
|
||||
return `${siteTitle}`;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const logger = require('winston');
|
||||
const multipart = require('connect-multiparty');
|
||||
const { files, site } = require('../config/speechConfig.js');
|
||||
const multipartMiddleware = multipart({uploadDir: files.uploadDirectory});
|
||||
const { publishing: { uploadDirectory }, details: { host } } = require('../config/siteConfig.js');
|
||||
const multipartMiddleware = multipart({uploadDir: uploadDirectory});
|
||||
const db = require('../models');
|
||||
const { claimNameIsAvailable, checkChannelAvailability, publish } = require('../controllers/publishController.js');
|
||||
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
||||
|
@ -168,7 +168,7 @@ module.exports = (app) => {
|
|||
data : {
|
||||
name,
|
||||
claimId: result.claim_id,
|
||||
url : `${site.host}/${result.claim_id}/${name}`,
|
||||
url : `${host}/${result.claim_id}/${name}`,
|
||||
lbryTx : result,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -15,7 +15,6 @@ module.exports = (app) => {
|
|||
// route for log in
|
||||
app.post('/login', (req, res, next) => {
|
||||
passport.authenticate('local-login', (err, user, info) => {
|
||||
logger.debug('info:', info);
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
const { site } = require('../config/speechConfig.js');
|
||||
const { details: host } = require('../config/siteConfig.js');
|
||||
const handlePageRender = require('../helpers/handlePageRender.jsx');
|
||||
|
||||
module.exports = (app) => {
|
||||
|
@ -29,7 +29,6 @@ module.exports = (app) => {
|
|||
app.get('/embed/:claimId/:name', ({ params }, res) => {
|
||||
const claimId = params.claimId;
|
||||
const name = params.name;
|
||||
const host = site.host;
|
||||
// get and render the content
|
||||
res.status(200).render('embed', { layout: 'embed', host, claimId, name });
|
||||
});
|
||||
|
|
106
server.js
Normal file
106
server.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
// app dependencies
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const expressHandlebars = require('express-handlebars');
|
||||
const Handlebars = require('handlebars');
|
||||
const helmet = require('helmet');
|
||||
const passport = require('passport');
|
||||
const { populateLocalsDotUser, serializeSpeechUser, deserializeSpeechUser } = require('./helpers/authHelpers.js');
|
||||
const cookieSession = require('cookie-session');
|
||||
const http = require('http');
|
||||
// logging dependencies
|
||||
const logger = require('winston');
|
||||
|
||||
function SpeechServer ({ mysqlConfig, siteConfig, slackConfig }) {
|
||||
this.start = () => {
|
||||
this.configureConfigFiles();
|
||||
this.configureLogging();
|
||||
this.configureApp();
|
||||
this.configureServer();
|
||||
this.startServer();
|
||||
};
|
||||
this.configureConfigFiles = () => {
|
||||
const mysqlAppConfig = require('./config/mysqlConfig.js');
|
||||
mysqlAppConfig.configure(mysqlConfig);
|
||||
const siteAppConfig = require('./config/siteConfig.js');
|
||||
siteAppConfig.configure(siteConfig);
|
||||
this.PORT = siteAppConfig.details.port;
|
||||
const slackAppConfig = require('./config/slackConfig.js');
|
||||
slackAppConfig.configure(slackConfig);
|
||||
};
|
||||
this.configureLogging = () => {
|
||||
require('./helpers/configureLogger.js')(logger);
|
||||
require('./helpers/configureSlack.js')(logger);
|
||||
};
|
||||
this.configureApp = () => {
|
||||
const app = express(); // create an Express application
|
||||
|
||||
// trust the proxy to get ip address for us
|
||||
app.enable('trust proxy');
|
||||
|
||||
// add middleware
|
||||
app.use(helmet()); // set HTTP headers to protect against well-known web vulnerabilties
|
||||
app.use(express.static(`${__dirname}/public`)); // 'express.static' to serve static files from public directory
|
||||
app.use(bodyParser.json()); // 'body parser' for parsing application/json
|
||||
app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded
|
||||
app.use((req, res, next) => { // custom logging middleware to log all incoming http requests
|
||||
logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`);
|
||||
next();
|
||||
});
|
||||
|
||||
// configure passport
|
||||
passport.serializeUser(serializeSpeechUser);
|
||||
passport.deserializeUser(deserializeSpeechUser);
|
||||
const localSignupStrategy = require('./passport/local-signup.js');
|
||||
const localLoginStrategy = require('./passport/local-login.js');
|
||||
passport.use('local-signup', localSignupStrategy);
|
||||
passport.use('local-login', localLoginStrategy);
|
||||
// initialize passport
|
||||
app.use(cookieSession({
|
||||
name : 'session',
|
||||
keys : [siteConfig.auth.sessionKey],
|
||||
maxAge: 24 * 60 * 60 * 1000, // i.e. 24 hours
|
||||
}));
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
// configure handlebars & register it with express app
|
||||
const hbs = expressHandlebars.create({
|
||||
defaultLayout: 'embed',
|
||||
handlebars : Handlebars,
|
||||
});
|
||||
app.engine('handlebars', hbs.engine);
|
||||
app.set('view engine', 'handlebars');
|
||||
|
||||
// middleware to pass user info back to client (for handlebars access), if user is logged in
|
||||
app.use(populateLocalsDotUser); // note: I don't think I need this any more?
|
||||
|
||||
// set the routes on the app
|
||||
require('./routes/auth-routes.js')(app);
|
||||
require('./routes/api-routes.js')(app);
|
||||
require('./routes/page-routes.js')(app);
|
||||
require('./routes/serve-routes.js')(app);
|
||||
require('./routes/fallback-routes.js')(app);
|
||||
|
||||
this.app = app;
|
||||
};
|
||||
this.configureServer = () => {
|
||||
this.server = http.Server(this.app);
|
||||
};
|
||||
this.startServer = () => {
|
||||
const db = require('./models');
|
||||
// sync sequelize
|
||||
db.sequelize.sync()
|
||||
// start the server
|
||||
.then(() => {
|
||||
this.server.listen(this.PORT, () => {
|
||||
logger.info(`Server is listening on PORT ${this.PORT}`);
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
logger.error(`Startup Error:`, error);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = SpeechServer;
|
|
@ -2,9 +2,7 @@
|
|||
const logger = require('winston');
|
||||
const db = require('../models'); // require our models for syncing
|
||||
// configure logging
|
||||
const config = require('../config/speechConfig.js');
|
||||
const { logLevel } = config.logging;
|
||||
require('../config/loggerConfig.js')(logger, logLevel);
|
||||
require('../helpers/configureLogger.js')(logger);
|
||||
|
||||
let totalClaims = 0;
|
||||
let totalClaimsNoCertificate = 0;
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
// load dependencies
|
||||
const logger = require('winston');
|
||||
const db = require('../models'); // require our models for syncing
|
||||
const db = require('../models');
|
||||
// configure logging
|
||||
const config = require('../config/speechConfig.js');
|
||||
const { logLevel } = config.logging;
|
||||
require('../config/loggerConfig.js')(logger, logLevel);
|
||||
require('../helpers/configureLogger.js')(logger);
|
||||
|
||||
const userName = process.argv[2];
|
||||
logger.debug('user name:', userName);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
const chai = require('chai');
|
||||
const expect = chai.expect;
|
||||
const chaiHttp = require('chai-http');
|
||||
const { site, testing } = require('../../config/speechConfig.js');
|
||||
const { host } = site;
|
||||
const { testChannel, testChannelId, testChannelPassword } = testing;
|
||||
const { details: { host } } = require('../../config/siteConfig.js');
|
||||
const { testChannel, testChannelId, testChannelPassword } = require('../../devConfig/testingConfig.js');
|
||||
const requestTimeout = 20000;
|
||||
const publishTimeout = 120000;
|
||||
const fs = require('fs');
|
||||
|
|
7
webpack.config.js
Normal file
7
webpack.config.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
const serverBaseConfig = require('./webpack.server.common.js');
|
||||
const clientBaseConfig = require('./webpack.client.common.js');
|
||||
|
||||
module.exports = [
|
||||
serverBaseConfig,
|
||||
clientBaseConfig,
|
||||
];
|
|
@ -8,11 +8,13 @@ module.exports = {
|
|||
__dirname: false,
|
||||
},
|
||||
externals: [nodeExternals()],
|
||||
entry : ['babel-polyfill', 'whatwg-fetch', './index.js'],
|
||||
entry : ['babel-polyfill', 'whatwg-fetch', './server.js'],
|
||||
output : {
|
||||
path : Path.join(__dirname, '/'),
|
||||
publicPath: '/',
|
||||
filename : 'server.js',
|
||||
publicPath : '/',
|
||||
filename : 'index.js',
|
||||
library : '',
|
||||
libraryTarget: 'commonjs-module',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
|
Loading…
Reference in a new issue