diff --git a/.gitignore b/.gitignore index 34977ee7..761ac590 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules -.idea \ No newline at end of file +.idea +config/config.json \ No newline at end of file diff --git a/controllers/publishController.js b/controllers/publishController.js index fd54d347..099675ba 100644 --- a/controllers/publishController.js +++ b/controllers/publishController.js @@ -8,7 +8,7 @@ module.exports = { return new Promise((resolve, reject) => { let publishResults = {}; // 1. make sure the name is available - publishHelpers.checkNameAvailability(publishParams.name) + publishHelpers.checkClaimNameAvailability(publishParams.name) // 2. publish the file .then(result => { if (result === true) { diff --git a/helpers/publishHelpers.js b/helpers/publishHelpers.js index 3462dfa2..a14ce408 100644 --- a/helpers/publishHelpers.js +++ b/helpers/publishHelpers.js @@ -100,7 +100,7 @@ module.exports = { logger.debug(`successfully deleted ${filePath}`); }); }, - checkNameAvailability (name) { + checkClaimNameAvailability (name) { return new Promise((resolve, reject) => { // find any records where the name is used db.File.findAll({ where: { name } }) @@ -133,27 +133,13 @@ module.exports = { checkChannelAvailability (name) { return new Promise((resolve, reject) => { // find any records where the name is used - db.Certificate.findAll({ where: { name } }) + db.User.findAll({ where: { channelName: name } }) .then(result => { + logger.debug('sequelize result:', result); if (result.length >= 1) { - // filter out any results that were not published from a spee.ch wallet address - getWalletList() - .then((walletList) => { - const filteredResult = result.filter((claim) => { - return walletList.includes(claim.address); - }); - if (filteredResult.length >= 1) { - resolve(false); - } else { - resolve(true); - } - }) - .catch((error) => { - reject(error); - }); - } else { - resolve(true); + return resolve(false); } + resolve(true); }) .catch(error => { reject(error); diff --git a/migrations/Add-Address-To-User.js b/migrations/Add-Address-To-User.js new file mode 100644 index 00000000..b8f88d6f --- /dev/null +++ b/migrations/Add-Address-To-User.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.addColumn( + 'User', + 'Address', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.removeColumn( + 'User', + 'Address' + ); + }, +}; diff --git a/migrations/Add-UserId-To-Certificate.js b/migrations/Add-UserId-To-Certificate.js new file mode 100644 index 00000000..f1f60684 --- /dev/null +++ b/migrations/Add-UserId-To-Certificate.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + queryInterface.addColumn( + 'Certificate', + 'UserId', + { + type : Sequelize.STRING, + allowNull: false, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + queryInterface.removeColumn( + 'Certificate', + 'UserId' + ); + }, +}; diff --git a/migrations/Add-UserId-To-File.js b/migrations/Add-UserId-To-File.js new file mode 100644 index 00000000..bde92b42 --- /dev/null +++ b/migrations/Add-UserId-To-File.js @@ -0,0 +1,20 @@ +module.exports = { + up: (queryInterface, Sequelize) => { + // logic for transforming into the new state + return queryInterface.addColumn( + 'File', + 'UserId', + { + type : Sequelize.STRING, + allowNull: true, + } + ); + }, + down: (queryInterface, Sequelize) => { + // logic for reverting the changes + return queryInterface.removeColumn( + 'File', + 'UserId' + ); + }, +}; diff --git a/models/user.js b/models/user.js index 96886925..32620407 100644 --- a/models/user.js +++ b/models/user.js @@ -18,6 +18,10 @@ module.exports = (sequelize, { STRING }) => { type : STRING, allowNull: false, }, + address: { + type : STRING, + allowNull: false, + }, }, { freezeTableName: true, diff --git a/package.json b/package.json index a89ac83b..e1aa4a85 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "passport": "^0.4.0", "passport-local": "^1.0.0", "sequelize": "^4.1.0", + "sequelize-cli": "^3.0.0-3", "sleep": "^5.1.1", "socket.io": "^2.0.1", "socketio-file-upload": "^0.6.0", diff --git a/public/assets/css/componentStyle.css b/public/assets/css/componentStyle.css index 32696fc7..3f908c29 100644 --- a/public/assets/css/componentStyle.css +++ b/public/assets/css/componentStyle.css @@ -54,16 +54,7 @@ width: 90% } -.input-text--primary { - border: 0px; - background-color: #ffffff; - border-bottom: 1px solid grey; -} -.input-text--primary:focus { - outline: none; - border-bottom: 1px solid blue; -} /* show routes */ .show-asset { diff --git a/public/assets/css/generalStyle.css b/public/assets/css/generalStyle.css index 70cfcf0b..d1d0c1c7 100644 --- a/public/assets/css/generalStyle.css +++ b/public/assets/css/generalStyle.css @@ -123,12 +123,36 @@ table { word-wrap: break-word; } -.input-error { +/* BEM */ + +.info-message { font-weight: bold; - color: red; - font-size: small; } +.info-message--success { + color: green; +} + +.info-message--failure { + color: red; +} + +.input-text { + outline: none; + border: 0px; + background-color: #ffffff; +} + +.input-text--primary { + border-bottom: 1px solid grey; +} + +.input-text--primary:focus { + border-bottom: 1px solid blue; +} + +/* MEDIA QUERIES */ + @media (max-width: 1250px) { .wrapper { diff --git a/public/assets/js/generalFunctions.js b/public/assets/js/generalFunctions.js index 3c3a60d9..4e5ba82c 100644 --- a/public/assets/js/generalFunctions.js +++ b/public/assets/js/generalFunctions.js @@ -55,18 +55,6 @@ function dataURItoBlob(dataURI) { return new Blob([ia], {type:mimeString}); } -function showError(elementId, errorMsg) { - var errorDisplay = document.getElementById(elementId); - errorDisplay.hidden = false; - errorDisplay.innerText = errorMsg; -} - -function clearError(elementId) { - var errorDisplay = document.getElementById(elementId); - errorDisplay.hidden = true; - errorDisplay.innerText = ''; -} - // Create new error objects, that prototypically inherit from the Error constructor function FileError(message) { this.name = 'FileError'; diff --git a/public/assets/js/validationFunctions.js b/public/assets/js/validationFunctions.js index b73b16c3..2f180822 100644 --- a/public/assets/js/validationFunctions.js +++ b/public/assets/js/validationFunctions.js @@ -27,56 +27,6 @@ function validateFile(file) { throw new Error(file.type + ' is not a supported file type. Only, .jpeg, .png, .gif, and .mp4 files are currently supported.') } } -// validation function that checks to make sure the claim name is not already claimed -function isChannelAvailable (name) { - return new Promise(function(resolve, reject) { - // make sure the claim name is still available - var xhttp; - xhttp = new XMLHttpRequest(); - xhttp.open('GET', '/api/isChannelAvailable/' + name, true); - xhttp.responseType = 'json'; - xhttp.onreadystatechange = function() { - if (this.readyState == 4 ) { - if ( this.status == 200) { - if (this.response == true) { - resolve(); - } else { - reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); - } - } else { - reject("request to check claim name failed with status:" + this.status); - }; - } - }; - xhttp.send(); - }); -} - -// validation function that checks to make sure the claim name is not already claimed -function isNameAvailable (name) { - return new Promise(function(resolve, reject) { - // make sure the claim name is still available - var xhttp; - xhttp = new XMLHttpRequest(); - xhttp.open('GET', '/api/isClaimAvailable/' + name, true); - xhttp.responseType = 'json'; - xhttp.onreadystatechange = function() { - if (this.readyState == 4 ) { - if ( this.status == 200) { - if (this.response == true) { - resolve(); - } else { - reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); - } - } else { - reject("request to check claim name failed with status:" + this.status); - }; - } - }; - xhttp.send(); - }); -} - // validation function that checks to make sure the claim name is valid function validateClaimName (name) { // ensure a name was entered @@ -95,51 +45,85 @@ function cleanseClaimName(name) { name = name.replace(/[^A-Za-z0-9-]/g, ''); // remove all characters that are not A-Z, a-z, 0-9, or '-' return name; } -// validaiton function to check claim name as the input changes -function checkClaimName(name){ - try { - // check to make sure the characters are valid - validateClaimName(name); - clearError('input-error-claim-name'); - // check to make sure it is availabe - isNameAvailable(name) - .then(function() { - document.getElementById('claim-name-available').hidden = false; - }) - .catch(function(error) { - document.getElementById('claim-name-available').hidden = true; - showError('input-error-claim-name', error.message); - }); - } catch (error) { - showError('input-error-claim-name', error.message); - document.getElementById('claim-name-available').hidden = true; - } + +// validation functions to check claim & channel name eligibility as the inputs change + +function isNameAvailable (name, apiUrl) { + return new Promise(function(resolve, reject) { + // make sure the claim name is still available + var xhttp; + xhttp = new XMLHttpRequest(); + xhttp.open('GET', apiUrl + name, true); + xhttp.responseType = 'json'; + xhttp.onreadystatechange = function() { + if (this.readyState == 4 ) { + if ( this.status == 200) { + if (this.response == true) { + resolve(); + } else { + reject( new NameError("That name has already been claimed by another user. Please choose a different name.")); + } + } else { + reject("request to check claim name failed with status:" + this.status); + }; + } + }; + xhttp.send(); + }); } +function showError(errorDisplay, errorMsg) { + errorDisplay.hidden = false; + errorDisplay.innerText = errorMsg; +} -// validaiton function to check claim name as the input changes -function checkChannelName(event){ - console.log(event); - const name = event.target.value; - const target = document.getElementById(event.target.id); - const errorDisplay = target.parentNode.firstChild; - console.log('error display:', errorDisplay) +function hideError(errorDisplay) { + errorDisplay.hidden = true; + errorDisplay.innerText = ''; +} + +function showSuccess (successElement) { + successElement.hidden = false; + successElement.innerHTML = "✔"; +} + +function hideSuccess (successElement) { + successElement.hidden = true; + successElement.innerHTML = ""; +} + +function checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, apiUrl) { try { + // check to make sure the characters are valid + validateClaimName(name); // check to make sure it is available - isChannelAvailable(name) + isNameAvailable(name, apiUrl) .then(function() { - errorDisplay.hidden = false; + hideError(errorDisplayElement); + showSuccess(successDisplayElement) }) .catch(function(error) { - errorDisplay.hidden = false; - showError(errorDisplay.getAttribute('id'), error.message); + hideSuccess(successDisplayElement); + showError(errorDisplayElement, error.message); }); } catch (error) { - console.log(error.message); - document.getElementById(errorDisplay.getAttribute('id')).hidden = true; + hideSuccess(successDisplayElement); + showError(errorDisplayElement, error.message); } } +function checkClaimName(name){ + const successDisplayElement = document.getElementById('claim-name-success'); + const errorDisplayElement = document.getElementById('claim-name-error'); + checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isClaimAvailable/'); +} + +function checkChannelName(name){ + const successDisplayElement = document.getElementById('channel-name-success'); + const errorDisplayElement = document.getElementById('channel-name-error'); + checkAvailability(name, successDisplayElement, errorDisplayElement, isNameAvailable, '/api/isChannelAvailable/'); +} + // validation function which checks all aspects of the publish submission function validateSubmission(stagedFiles, name){ return new Promise(function (resolve, reject) { diff --git a/routes/api-routes.js b/routes/api-routes.js index 28a54daa..d8baaf64 100644 --- a/routes/api-routes.js +++ b/routes/api-routes.js @@ -3,7 +3,7 @@ const multipart = require('connect-multiparty'); const multipartMiddleware = multipart(); const { publish } = require('../controllers/publishController.js'); const { getClaimList, resolveUri } = require('../helpers/lbryApi.js'); -const { createPublishParams, validateFile, checkNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); +const { createPublishParams, validateFile, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js'); const errorHandlers = require('../helpers/errorHandlers.js'); const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js'); @@ -25,7 +25,7 @@ module.exports = (app, hostedContentPath) => { // route to check whether spee.ch has published to a claim app.get('/api/isClaimAvailable/:name', ({ ip, originalUrl, params }, res) => { // send response - checkNameAvailability(params.name) + checkClaimNameAvailability(params.name) .then(result => { if (result === true) { res.status(200).json(true); @@ -39,8 +39,7 @@ module.exports = (app, hostedContentPath) => { }); }); // route to check whether spee.ch has published to a channel - app.get('/api/isChannelAvailable/:name', ({ ip, originalUrl, params }, res) => { - // send response + app.get('/api/isChannelAvailable/:name', ({ params }, res) => { checkChannelAvailability(params.name) .then(result => { if (result === true) { @@ -51,6 +50,7 @@ module.exports = (app, hostedContentPath) => { } }) .catch(error => { + logger.debug('api/isChannelAvailable/ error', error); res.status(500).json(error); }); }); diff --git a/views/login.handlebars b/views/login.handlebars index 60f5df49..f662d36f 100644 --- a/views/login.handlebars +++ b/views/login.handlebars @@ -7,11 +7,11 @@