Merge pull request #263 from lbryio/db-datatypes-migration

Db datatypes migration
This commit is contained in:
Bill Bittner 2017-11-20 12:35:12 -05:00 committed by GitHub
commit ab3d6243e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 150 additions and 101 deletions

1
.gitignore vendored
View file

@ -1,5 +1,4 @@
node_modules node_modules
.idea .idea
config/config.json
config/sequelizeCliConfig.js config/sequelizeCliConfig.js
config/speechConfig.js config/speechConfig.js

5
.sequelizerc Normal file
View file

@ -0,0 +1,5 @@
const path = require('path');
module.exports = {
'config': path.resolve('config', 'sequelizeCliConfig.js'),
}

View file

@ -0,0 +1,23 @@
module.exports = {
development: {
username: '',
password: '',
database: '',
host : '127.0.0.1',
dialect : 'mysql',
},
test: {
username: '',
password: '',
database: '',
host : '127.0.0.1',
dialect : 'mysql',
},
production: {
username: '',
password: '',
database: '',
host : '127.0.0.1',
dialect : 'mysql',
},
};

View file

@ -19,4 +19,7 @@ module.exports = {
session: { session: {
sessionKey: null, // enter a secret key to be used for session encryption 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
},
}; };

View file

@ -1,22 +0,0 @@
module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
const p1 = queryInterface.addColumn(
'Claim',
'channelName',
{
type : Sequelize.STRING,
allowNull: true,
}
);
return Promise.all([p1]);
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
const p1 = queryInterface.removeColumn(
'Claim',
'channelName'
);
return Promise.all([p1]);
},
};

View file

@ -0,0 +1,42 @@
module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
const p1 = queryInterface.changeColumn(
'Certificate',
'amount',
{
type : Sequelize.DECIMAL(19, 8),
allowNull: true,
}
);
const p2 = queryInterface.changeColumn(
'Certificate',
'effectiveAmount',
{
type : Sequelize.DECIMAL(19, 8),
allowNull: true,
}
);
return Promise.all([p1, p2]);
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
const p1 = queryInterface.changeColumn(
'Certificate',
'amount',
{
type : Sequelize.DOUBLE,
allowNull: true,
}
);
const p2 = queryInterface.changeColumn(
'Certificate',
'effectiveAmount',
{
type : Sequelize.DOUBLE,
allowNull: true,
}
);
return Promise.all([p1, p2]);
},
};

View file

@ -0,0 +1,42 @@
module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
const p1 = queryInterface.changeColumn(
'Claim',
'amount',
{
type : Sequelize.DECIMAL(19, 8),
allowNull: true,
}
);
const p2 = queryInterface.changeColumn(
'Claim',
'effectiveAmount',
{
type : Sequelize.DECIMAL(19, 8),
allowNull: true,
}
);
return Promise.all([p1, p2]);
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
const p1 = queryInterface.changeColumn(
'Claim',
'amount',
{
type : Sequelize.DOUBLE,
allowNull: true,
}
);
const p2 = queryInterface.changeColumn(
'Claim',
'effectiveAmount',
{
type : Sequelize.DOUBLE,
allowNull: true,
}
);
return Promise.all([p1, p2]);
},
};

View file

@ -1,46 +0,0 @@
const db = require('../models');
const bcrypt = require('bcrypt');
const logger = require('winston');
module.exports = {
up: (queryInterface, Sequelize) => {
// get all the users
return db.User
.findAll()
.then((users) => {
// create an array of promises, with each promise bcrypting a password and updating the record
const promises = users.map((record) => {
// bcrypt
// generate a salt string to use for hashing
return new Promise((resolve, reject) => {
bcrypt.genSalt((saltError, salt) => {
if (saltError) {
logger.error('salt error', saltError);
reject(saltError);
return;
}
// generate a hashed version of the user's password
bcrypt.hash(record.password, salt, (hashError, hash) => {
// if there is an error with the hash generation return the error
if (hashError) {
logger.error('hash error', hashError);
reject(hashError);
return;
}
// replace the password string with the hash password value
resolve(queryInterface.sequelize.query(`UPDATE User SET User.password = "${hash}" WHERE User.id = ${record.id}`));
});
});
});
});
// return the array of promises
return Promise.all(promises);
})
.catch(error => {
logger.error('error prepping promises array', error);
});
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
},
};

View file

@ -2,7 +2,7 @@ const logger = require('winston');
const { returnShortId } = require('../helpers/sequelizeHelpers.js'); const { returnShortId } = require('../helpers/sequelizeHelpers.js');
const NO_CHANNEL = 'NO_CHANNEL'; const NO_CHANNEL = 'NO_CHANNEL';
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT }) => { module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
const Certificate = sequelize.define( const Certificate = sequelize.define(
'Certificate', 'Certificate',
{ {
@ -11,7 +11,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT }) => {
default: null, default: null,
}, },
amount: { amount: {
type : STRING, type : DECIMAL(19, 8),
default: null, default: null,
}, },
claimId: { claimId: {
@ -31,7 +31,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT }) => {
default: null, default: null,
}, },
effectiveAmount: { effectiveAmount: {
type : STRING, type : DECIMAL(19, 8),
default: null, default: null,
}, },
hasSignature: { hasSignature: {
@ -39,7 +39,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT }) => {
default: null, default: null,
}, },
height: { height: {
type : STRING, type : INTEGER,
default: null, default: null,
}, },
hex: { hex: {
@ -59,7 +59,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT }) => {
default: null, default: null,
}, },
validAtHeight: { validAtHeight: {
type : STRING, type : INTEGER,
default: null, default: null,
}, },
outpoint: { outpoint: {

View file

@ -2,7 +2,7 @@ const logger = require('winston');
const { returnShortId } = require('../helpers/sequelizeHelpers.js'); const { returnShortId } = require('../helpers/sequelizeHelpers.js');
const NO_CLAIM = 'NO_CLAIM'; const NO_CLAIM = 'NO_CLAIM';
module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, DOUBLE, Op }) => { module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, DECIMAL }) => {
const Claim = sequelize.define( const Claim = sequelize.define(
'Claim', 'Claim',
{ {
@ -11,7 +11,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D
default: null, default: null,
}, },
amount: { amount: {
type : STRING, type : DECIMAL(19, 8),
default: null, default: null,
}, },
claimId: { claimId: {
@ -31,7 +31,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D
default: null, default: null,
}, },
effectiveAmount: { effectiveAmount: {
type : STRING, type : DECIMAL(19, 8),
default: null, default: null,
}, },
hasSignature: { hasSignature: {
@ -39,7 +39,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D
default: null, default: null,
}, },
height: { height: {
type : STRING, type : INTEGER,
default: null, default: null,
}, },
hex: { hex: {
@ -59,7 +59,7 @@ module.exports = (sequelize, { STRING, BOOLEAN, INTEGER, TEXT, ARRAY, DECIMAL, D
default: null, default: null,
}, },
validAtHeight: { validAtHeight: {
type : STRING, type : INTEGER,
default: null, default: null,
}, },
outpoint: { outpoint: {

View file

@ -2,17 +2,18 @@ const fs = require('fs');
const path = require('path'); const path = require('path');
const Sequelize = require('sequelize'); const Sequelize = require('sequelize');
const basename = path.basename(module.filename); const basename = path.basename(module.filename);
const config = require('../config/speechConfig.js');
const db = {};
const logger = require('winston'); const logger = require('winston');
const config = require('../config/speechConfig.js');
const database = config.sql.database; const database = config.sql.database;
const username = config.sql.username; const username = config.sql.username;
const password = config.sql.password; const password = config.sql.password;
const db = {};
// set sequelize options
const sequelize = new Sequelize(database, username, password, { const sequelize = new Sequelize(database, username, password, {
host : 'localhost', host : 'localhost',
dialect : 'mysql', dialect : 'mysql',
dialectOptions: {decimalNumbers: true}, // fix to ensure DECIMAL will not be stored as a string
logging : false, logging : false,
pool : { pool : {
max : 5, max : 5,
@ -53,6 +54,7 @@ Object.keys(db).forEach(modelName => {
db.sequelize = sequelize; db.sequelize = sequelize;
db.Sequelize = Sequelize; db.Sequelize = Sequelize;
// add an 'upsert' method to the db object
db.upsert = (Model, values, condition, tableName) => { db.upsert = (Model, values, condition, tableName) => {
return Model return Model
.findOne({ where: condition }) .findOne({ where: condition })
@ -70,6 +72,7 @@ db.upsert = (Model, values, condition, tableName) => {
}); });
}; };
// add a 'getTrendingClaims' method to the db object
db.getTrendingClaims = (startDate) => { db.getTrendingClaims = (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 }); 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 });
}; };

View file

@ -75,7 +75,7 @@ body, .flex-container--column {
/* TEXT */ /* TEXT */
body, button, input, textarea, label, select, option { body, button, input, textarea, label, select, option, #channel-publish-in-progress > p, #channel-publish-done > p {
font-family: 'Lekton', monospace; font-family: 'Lekton', monospace;
font-size: large; font-size: large;
} }

View file

@ -1,11 +1,14 @@
// display the content that shows channel creation has started
function showChannelCreateInProgressDisplay () { function showChannelCreateInProgressDisplay () {
const publishChannelForm = document.getElementById('publish-channel-form'); const publishChannelForm = document.getElementById('publish-channel-form');
publishChannelForm.hidden = true;
const inProgress = document.getElementById('channel-publish-in-progress'); const inProgress = document.getElementById('channel-publish-in-progress');
const channelProgressBar = document.getElementById('create-channel-progress-bar');
publishChannelForm.hidden = true;
inProgress.hidden = false; inProgress.hidden = false;
createProgressBar(document.getElementById('create-channel-progress-bar'), 12); createProgressBar(channelProgressBar, 12);
} }
// display the content that shows channle creation is done
function showChannelCreateDoneDisplay() { function showChannelCreateDoneDisplay() {
const inProgress = document.getElementById('channel-publish-in-progress'); const inProgress = document.getElementById('channel-publish-in-progress');
inProgress.hidden=true; inProgress.hidden=true;
@ -38,7 +41,6 @@ function publishNewChannel (event) {
if (window.location.pathname === '/') { if (window.location.pathname === '/') {
// remove old channel and replace with new one & select it // remove old channel and replace with new one & select it
replaceChannelOptionInPublishChannelSelect(); replaceChannelOptionInPublishChannelSelect();
// remove old channel and replace with new one & select it
replaceChannelOptionInNavBarChannelSelect(); replaceChannelOptionInNavBarChannelSelect();
} else { } else {
window.location = '/'; window.location = '/';
@ -47,7 +49,7 @@ function publishNewChannel (event) {
.catch(error => { .catch(error => {
if (error.name === 'ChannelNameError' || error.name === 'ChannelPasswordError'){ if (error.name === 'ChannelNameError' || error.name === 'ChannelPasswordError'){
const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name'); const channelNameErrorDisplayElement = document.getElementById('input-error-channel-name');
showError(channelNameErrorDisplayElement, error.message); validationFunctions.showError(channelNameErrorDisplayElement, error.message);
} else { } else {
console.log('signup failure:', error); console.log('signup failure:', error);
showChannelCreationError('Unfortunately, Spee.ch encountered an error while creating your channel. Please let us know in slack!'); showChannelCreationError('Unfortunately, Spee.ch encountered an error while creating your channel. Please let us know in slack!');

View file

@ -151,7 +151,7 @@ function copyToClipboard(event){
try { try {
document.execCommand('copy'); document.execCommand('copy');
} catch (err) { } catch (err) {
showError(errorElement, 'Oops, unable to copy'); validationFunctions.showError(errorElement, 'Oops, unable to copy');
} }
} }

View file

@ -38,7 +38,6 @@ const validationFunctions = {
}, },
// validation function that checks to make sure the claim name is valid // validation function that checks to make sure the claim name is valid
validateClaimName: function (name) { validateClaimName: function (name) {
console.log('validating the claim name');
// ensure a name was entered // ensure a name was entered
if (name.length < 1) { if (name.length < 1) {
throw new NameError("You must enter a name for your url"); throw new NameError("You must enter a name for your url");

View file

@ -1,6 +1,7 @@
const logger = require('winston'); const logger = require('winston');
const multipart = require('connect-multiparty'); const multipart = require('connect-multiparty');
const multipartMiddleware = multipart({uploadDir: '/home/lbry/test/'}); const config = require('../config/speechConfig.js');
const multipartMiddleware = multipart({uploadDir: config.files.uploadDirectory});
const db = require('../models'); const db = require('../models');
const { publish } = require('../controllers/publishController.js'); const { publish } = require('../controllers/publishController.js');
const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js');

View file

@ -1,7 +1,6 @@
// load dependencies // load dependencies
const express = require('express'); const express = require('express');
const bodyParser = require('body-parser'); const bodyParser = require('body-parser');
const siofu = require('socketio-file-upload');
const expressHandlebars = require('express-handlebars'); const expressHandlebars = require('express-handlebars');
const Handlebars = require('handlebars'); const Handlebars = require('handlebars');
const handlebarsHelpers = require('./helpers/handlebarsHelpers.js'); const handlebarsHelpers = require('./helpers/handlebarsHelpers.js');
@ -32,7 +31,6 @@ app.use(helmet()); // set HTTP headers to protect against well-known web vulnera
app.use(express.static(`${__dirname}/public`)); // 'express.static' to serve static files from public directory 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.json()); // 'body parser' for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: true })); // 'body parser' for parsing application/x-www-form-urlencoded
app.use(siofu.router); // 'socketio-file-upload' router for uploading with socket.io
app.use((req, res, next) => { // custom logging middleware to log all incoming http requests app.use((req, res, next) => { // custom logging middleware to log all incoming http requests
logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`); logger.verbose(`Request on ${req.originalUrl} from ${req.ip}`);
logger.debug('req.body:', req.body); logger.debug('req.body:', req.body);
@ -76,7 +74,7 @@ db.sequelize
.then(hostedContentPath => { .then(hostedContentPath => {
// add the hosted content folder at a static path // add the hosted content folder at a static path
app.use('/media', express.static(hostedContentPath)); app.use('/media', express.static(hostedContentPath));
// require routes & wrap in socket.io // require routes
require('./routes/auth-routes.js')(app); require('./routes/auth-routes.js')(app);
require('./routes/api-routes.js')(app); require('./routes/api-routes.js')(app);
require('./routes/page-routes.js')(app); require('./routes/page-routes.js')(app);

View file

@ -1,8 +1,8 @@
<div class="row row--tall flex-container--column"> <div class="row row--tall flex-container--column">
<form> <form>
<input class="input-file" type="file" id="siofu_input" name="siofu_input" accept="video/*,image/*" onchange="publishFileFunctions.previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/> <input class="input-file" type="file" id="file_input" name="file_input" accept="video/*,image/*" onchange="publishFileFunctions.previewAndStageFile(event.target.files[0])" enctype="multipart/form-data"/>
</form> </form>
<div id="primary-dropzone" class="dropzone row row--margined row--padded row--tall flex-container--column flex-container--center-center" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="dragenter_handler(event)" ondragleave="dragexit_handler(event)" onclick="publishFileFunctions.triggerFileChooser('siofu_input', event)"> <div id="primary-dropzone" class="dropzone row row--margined row--padded row--tall flex-container--column flex-container--center-center" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="dragenter_handler(event)" ondragleave="dragexit_handler(event)" onclick="publishFileFunctions.triggerFileChooser('file_input', event)">
<div id="primary-dropzone-instructions"> <div id="primary-dropzone-instructions">
<p class="info-message-placeholder info-message--failure" id="input-error-file-selection" hidden="true"></p> <p class="info-message-placeholder info-message--failure" id="input-error-file-selection" hidden="true"></p>
<p>Drag & drop image or video here to publish</p> <p>Drag & drop image or video here to publish</p>
@ -22,7 +22,7 @@
<div class="column column--5 column--sml-10" > <div class="column column--5 column--sml-10" >
<!-- preview --> <!-- preview -->
<div class="row row--padded"> <div class="row row--padded">
<div id="asset-preview-holder" class="dropzone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="preview_onmouseenter_handler()" ondragleave="preview_onmouseleave_handler()" onmouseenter="preview_onmouseenter_handler()" onmouseleave="preview_onmouseleave_handler()" onclick="publishFileFunctions.triggerFileChooser('siofu_input', event)"> <div id="asset-preview-holder" class="dropzone" ondrop="drop_handler(event);" ondragover="dragover_handler(event);" ondragend="dragend_handler(event)" ondragenter="preview_onmouseenter_handler()" ondragleave="preview_onmouseleave_handler()" onmouseenter="preview_onmouseenter_handler()" onmouseleave="preview_onmouseleave_handler()" onclick="publishFileFunctions.triggerFileChooser('file_input', event)">
<div id="asset-preview-dropzone-instructions" class="hidden"> <div id="asset-preview-dropzone-instructions" class="hidden">
<p>Drag & drop image or video here</p> <p>Drag & drop image or video here</p>
<p class="fine-print">OR</p> <p class="fine-print">OR</p>

View file

@ -75,7 +75,7 @@
// hide the login and new channel forms // hide the login and new channel forms
loginToChannelTool.hidden = true; loginToChannelTool.hidden = true;
createChannelTool.hidden = true; createChannelTool.hidden = true;
hideError(document.getElementById('input-error-channel-select')); validationFunctions.hideError(document.getElementById('input-error-channel-select'));
} }
// update url // update url
updateUrl(selectedChannel); updateUrl(selectedChannel);