Authentication #170
17 changed files with 208 additions and 156 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
node_modules
|
||||
.idea
|
||||
config/config.json
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
20
migrations/Add-Address-To-User.js
Normal file
20
migrations/Add-Address-To-User.js
Normal file
|
@ -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'
|
||||
);
|
||||
},
|
||||
};
|
20
migrations/Add-UserId-To-Certificate.js
Normal file
20
migrations/Add-UserId-To-Certificate.js
Normal file
|
@ -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'
|
||||
);
|
||||
},
|
||||
};
|
20
migrations/Add-UserId-To-File.js
Normal file
20
migrations/Add-UserId-To-File.js
Normal file
|
@ -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'
|
||||
);
|
||||
},
|
||||
};
|
|
@ -18,6 +18,10 @@ module.exports = (sequelize, { STRING }) => {
|
|||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
address: {
|
||||
type : STRING,
|
||||
allowNull: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
freezeTableName: true,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
<form id="login-form" action="/login" method="post">
|
||||
<div>
|
||||
<label>Username:</label>
|
||||
@ <input type="text" name="username" class="input-text--primary"/>
|
||||
@ <input type="text" name="username" class="input-text input-text--primary"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password" class="input-text--primary"/>
|
||||
<input type="password" name="password" class="input-text input-text--primary"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Log In"/>
|
||||
|
@ -23,11 +23,11 @@
|
|||
<form action="/signup" method="post">
|
||||
<div>
|
||||
<label>Channel name:</label>
|
||||
@ <input type="text" name="username" class="input-text--primary"/>
|
||||
@ <input type="text" name="username" class="input-text input-text--primary"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Password:</label>
|
||||
<input type="password" name="password" class="input-text--primary"/>
|
||||
<input type="password" name="password" class="input-text input-text--primary"/>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="Create"/>
|
||||
|
|
|
@ -1,21 +1,34 @@
|
|||
<div id="claim-channel-input-area">
|
||||
<label for="publish-channel">Channel:</label></td>
|
||||
<select type="text" id="publish-channel" name="channel" value="channel" onclick="check(event)">
|
||||
{{#if user}}
|
||||
<option value="@{{user.channelName}}" >@{{user.channelName}}</option>
|
||||
{{/if}}
|
||||
<option value="@speech" >Anonymous</option>
|
||||
<option value="new" >New</option>
|
||||
</select>
|
||||
<div id="channel-create-details" hidden="true"><p id="test" style="color: red;"></p>
|
||||
<label for="channelName">Channel Name: </label>
|
||||
@<input type="text" id="channel-name-input" class="input-text--primary" oninput="checkChannelName(event)">
|
||||
<br/>
|
||||
<label for="password" >Password: </label>
|
||||
<input type="text" id="password" class="input-text--primary">
|
||||
<br/>
|
||||
<button >create</button>
|
||||
|
||||
<p>
|
||||
<label for="publish-channel">Channel:</label></td>
|
||||
<select type="text" id="publish-channel" name="channel" value="channel" onclick="check(event)">
|
||||
{{#if user}}
|
||||
<option value="@{{user.channelName}}" >@{{user.channelName}}</option>
|
||||
{{/if}}
|
||||
<option value="@speech" >Anonymous</option>
|
||||
<option value="new" >New</option>
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<div id="channel-create-details" hidden="true">
|
||||
|
||||
<span id="channel-name-error" class="info-message info-message--failure"></span>
|
||||
<p>
|
||||
<label for="channelName">Channel Name: </label>
|
||||
@<input type="text" id="channel-name-input" class="input-text input-text--primary" placeholder="exampleChannel" oninput="checkChannelName(event.target.value)">
|
||||
<span id="channel-name-success" class="info-message info-message--success"></span>
|
||||
<br/>
|
||||
|
||||
<label for="password" >Password: </label>
|
||||
<input type="password" id="password" placeholder="" class="input-text input-text--primary">
|
||||
<br/>
|
||||
</p>
|
||||
|
||||
<button >create</button>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<table>
|
||||
<tr>
|
||||
<td><label for="publish-title">Title: </label></td>
|
||||
<td><input type="text" id="publish-title" class="input-text--primary"></td>
|
||||
<td><input type="text" id="publish-title" class="input-text input-text--primary"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="publish-description">Description: </label></td>
|
||||
|
|
|
@ -11,12 +11,12 @@
|
|||
</div>
|
||||
<div class="col-right">
|
||||
<div id="publish-active-area">
|
||||
<div class="input-error" id="input-error-claim-name" hidden="true"></div>
|
||||
<div class="stop-float">
|
||||
<span id="claim-name-error" class="info-message info-message--failure" hidden="true"></span>
|
||||
<p>
|
||||
Spee.ch/
|
||||
<input type="text" id="claim-name-input" class="input-text--primary" placeholder="your-url-here" oninput="checkClaimName(event.target.value)">
|
||||
<span id="claim-name-available" hidden="true" style="color: green">✔</span>
|
||||
<input type="text" id="claim-name-input" class="input-text input-text--primary" placeholder="your-url-here" oninput="checkClaimName(event.target.value)">
|
||||
<span id="claim-name-success" class="info-message info-message--success"></span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="stop-float">
|
||||
|
@ -55,6 +55,6 @@
|
|||
document.getElementById('input-error-file-selection').innerHTML = '';
|
||||
document.getElementById('input-error-claim-name').innerHTML = '';
|
||||
document.getElementById('input-error-publish-submit').innerHTML = '';
|
||||
document.getElementById('claim-name-available').hidden = true;
|
||||
document.getElementById('claim-name-success').hidden = true;
|
||||
}
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue
What if I'm not on Chrome?