fixed the anonymous discrepency
This commit is contained in:
parent
d92f499269
commit
e34e78c169
8 changed files with 97 additions and 96 deletions
20
README.md
20
README.md
|
@ -36,17 +36,17 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
|
||||||
|
|
||||||
#### POST
|
#### POST
|
||||||
* /api/publish
|
* /api/publish
|
||||||
* example: `curl -X POST -F 'name=MyPictureName' -F 'nsfw=false' -F 'file=@/path/to/my/picture.jpeg' https://spee.ch/api/publish`
|
* example: `curl -X POST -F 'name=MyPictureName' -F 'file=@/path/to/myPicture.jpeg' https://spee.ch/api/publish`
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* name (string)
|
* `name`
|
||||||
* nsfw (boolean)
|
* `file` (.mp4, .jpeg, .jpg, .gif, or .png)
|
||||||
* file (.mp4, .jpeg, .jpg, .gif, or .png)
|
* `nsfw` (optional)
|
||||||
* license (string, optional)
|
* `license` (optional)
|
||||||
* title (string, optional)
|
* `title` (optional)
|
||||||
* description (string, optional)
|
* `description` (optional)
|
||||||
* thumbnail (string, optional) (for .mp4 uploads only)
|
* `thumbnail` url to thumbnail image, for .mp4 uploads only (optional)
|
||||||
* channelName(string, optional)
|
* `channelName`(optional)
|
||||||
* channelPassword (string, optional)
|
* `channelPassword` (optional,; required if `channelName` is provided)
|
||||||
|
|
||||||
## bugs
|
## bugs
|
||||||
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack!
|
If you find a bug or experience a problem, please report your issue here on github and find us in the lbry slack!
|
||||||
|
|
|
@ -2,12 +2,14 @@ const db = require('../models');
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
authenticateChannelCredentials (channelName, userPassword) {
|
authenticateChannelCredentials (skipAuth, channelName, userPassword) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (!channelName) {
|
// skip authentication if not needed
|
||||||
resolve(true);
|
if (skipAuth) {
|
||||||
|
resolve(skipAuth);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// authentication
|
||||||
const userName = channelName.substring(1);
|
const userName = channelName.substring(1);
|
||||||
logger.debug(`authenticateChannelCredentials > channelName: ${channelName} username: ${userName} pass: ${userPassword}`);
|
logger.debug(`authenticateChannelCredentials > channelName: ${channelName} username: ${userName} pass: ${userPassword}`);
|
||||||
db.User
|
db.User
|
||||||
|
@ -18,17 +20,23 @@ module.exports = {
|
||||||
resolve(false);
|
resolve(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!user.validPassword(userPassword, user.password)) {
|
return user.comparePassword(userPassword, (passwordErr, isMatch) => {
|
||||||
|
if (passwordErr) {
|
||||||
|
logger.error('comparePassword error:', passwordErr);
|
||||||
|
resolve(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!isMatch) {
|
||||||
logger.debug('incorrect password');
|
logger.debug('incorrect password');
|
||||||
resolve(false);
|
resolve(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.debug('user found:', user.dataValues);
|
logger.debug('...password was a match...');
|
||||||
resolve(true);
|
resolve(true);
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
logger.error(error);
|
reject(error);
|
||||||
reject();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,17 +13,16 @@ module.exports = {
|
||||||
.then(tx => {
|
.then(tx => {
|
||||||
logger.info(`Successfully published ${fileName}`, tx);
|
logger.info(`Successfully published ${fileName}`, tx);
|
||||||
publishResults = tx;
|
publishResults = tx;
|
||||||
return db.Channel.findOne({where: {channelName: publishParams.channel_name}}); // note: should this be db.User ??
|
if (publishParams.channel_name) {
|
||||||
})
|
logger.debug(`this claim was published in channel: ${publishParams.channel_name}`);
|
||||||
.then(channel => {
|
return db.Channel.findOne({where: {channelName: publishParams.channel_name}}).then(channel => { return channel.channelClaimId });
|
||||||
let certificateId;
|
|
||||||
if (channel) {
|
|
||||||
certificateId = channel.channelClaimId;
|
|
||||||
logger.debug('successfully found channel in Channel table');
|
|
||||||
} else {
|
} else {
|
||||||
certificateId = null;
|
logger.debug('this claim was published in channel: n/a');
|
||||||
logger.debug('channel for publish not found in Channel table');
|
return null;
|
||||||
};
|
}
|
||||||
|
})
|
||||||
|
.then(certificateId => {
|
||||||
|
logger.debug(`certificateId: ${certificateId}`);
|
||||||
const fileRecord = {
|
const fileRecord = {
|
||||||
name : publishParams.name,
|
name : publishParams.name,
|
||||||
claimId : publishResults.claim_id,
|
claimId : publishResults.claim_id,
|
||||||
|
|
|
@ -3,11 +3,12 @@ const { postToStats } = require('../controllers/statsController.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
returnErrorMessageAndStatus: function (error) {
|
returnErrorMessageAndStatus: function (error) {
|
||||||
let status;
|
let status, message;
|
||||||
let message;
|
// check for daemon being turned off
|
||||||
if (error.code === 'ECONNREFUSED') {
|
if (error.code === 'ECONNREFUSED') {
|
||||||
status = 503;
|
status = 503;
|
||||||
message = 'Connection refused. The daemon may not be running.';
|
message = 'Connection refused. The daemon may not be running.';
|
||||||
|
// check for errors from the deamon
|
||||||
} else if (error.response) {
|
} else if (error.response) {
|
||||||
status = error.response.status || 500;
|
status = error.response.status || 500;
|
||||||
if (error.response.data) {
|
if (error.response.data) {
|
||||||
|
@ -21,7 +22,13 @@ module.exports = {
|
||||||
} else {
|
} else {
|
||||||
message = error.response;
|
message = error.response;
|
||||||
}
|
}
|
||||||
|
// check for spee.ch thrown errors
|
||||||
|
} else if (error.message) {
|
||||||
|
status = 400;
|
||||||
|
message = error.message;
|
||||||
|
// fallback for everything else
|
||||||
} else {
|
} else {
|
||||||
|
status = 400;
|
||||||
message = error;
|
message = error;
|
||||||
}
|
}
|
||||||
return [status, message];
|
return [status, message];
|
||||||
|
|
|
@ -18,11 +18,10 @@ module.exports = {
|
||||||
throw new Error('no file with key of [file] found in request');
|
throw new Error('no file with key of [file] found in request');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
validatePublishSubmission (file, claimName, nsfw) {
|
validatePublishSubmission (file, claimName) {
|
||||||
try {
|
try {
|
||||||
module.exports.validateFile(file);
|
module.exports.validateFile(file);
|
||||||
module.exports.validateClaimName(claimName);
|
module.exports.validateClaimName(claimName);
|
||||||
module.exports.validateNSFW(nsfw);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
@ -76,24 +75,6 @@ module.exports = {
|
||||||
throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch');
|
throw new Error('Only posts with a "Public Domain" or "Creative Commons" license are eligible for publishing through spee.ch');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
cleanseNSFW (nsfw) {
|
|
||||||
switch (nsfw) {
|
|
||||||
case true:
|
|
||||||
case 'on':
|
|
||||||
case 'true':
|
|
||||||
case 1:
|
|
||||||
case '1':
|
|
||||||
return true;
|
|
||||||
case false:
|
|
||||||
case 'false':
|
|
||||||
case 'off':
|
|
||||||
case 0:
|
|
||||||
case '0':
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cleanseChannelName (channelName) {
|
cleanseChannelName (channelName) {
|
||||||
if (channelName) {
|
if (channelName) {
|
||||||
if (channelName.indexOf('@') !== 0) {
|
if (channelName.indexOf('@') !== 0) {
|
||||||
|
@ -102,12 +83,6 @@ module.exports = {
|
||||||
}
|
}
|
||||||
return channelName;
|
return channelName;
|
||||||
},
|
},
|
||||||
validateNSFW (nsfw) {
|
|
||||||
if (nsfw === true || nsfw === false) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error('NSFW must be set to either true or false');
|
|
||||||
},
|
|
||||||
createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) {
|
createPublishParams (filePath, name, title, description, license, nsfw, thumbnail, channelName) {
|
||||||
logger.debug(`Creating Publish Parameters`);
|
logger.debug(`Creating Publish Parameters`);
|
||||||
// provide defaults for title
|
// provide defaults for title
|
||||||
|
|
|
@ -64,20 +64,12 @@ var publishFileFunctions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
returnNullOrChannel: function () {
|
returnNullOrChannel: function () {
|
||||||
|
const channelRadio = document.getElementById('channel-radio');
|
||||||
|
if (channelRadio.checked) {
|
||||||
const channelInput = document.getElementById('channel-name-select');
|
const channelInput = document.getElementById('channel-name-select');
|
||||||
const radios = document.getElementsByName('anonymous-or-channel');
|
|
||||||
let anonymousOrInChannel;
|
|
||||||
// replace channelName with 'anonymous' if appropriate
|
|
||||||
for (let i = 0; i < radios.length; i++) {
|
|
||||||
if (radios[i].checked) {
|
|
||||||
anonymousOrInChannel = radios[i].value; // do whatever you want with the checked radio
|
|
||||||
break; // only one radio can be logically checked, don't check the rest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (anonymousOrInChannel === 'anonymous') {
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
return channelInput.value.trim();
|
return channelInput.value.trim();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
createMetadata: function() {
|
createMetadata: function() {
|
||||||
const nameInput = document.getElementById('claim-name-input');
|
const nameInput = document.getElementById('claim-name-input');
|
||||||
|
@ -89,13 +81,13 @@ var publishFileFunctions = {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: nameInput.value.trim(),
|
name: nameInput.value.trim(),
|
||||||
channel: this.returnNullOrChannel(),
|
channelName: this.returnNullOrChannel(),
|
||||||
title: titleInput.value.trim(),
|
title: titleInput.value.trim(),
|
||||||
description: descriptionInput.value.trim(),
|
description: descriptionInput.value.trim(),
|
||||||
license: licenseInput.value.trim(),
|
license: licenseInput.value.trim(),
|
||||||
nsfw: nsfwInput.checked,
|
nsfw: nsfwInput.checked,
|
||||||
type: stagedFiles[0].type,
|
type: stagedFiles[0].type,
|
||||||
thumbnail: thumbnailInput.value.trim()
|
thumbnail: thumbnailInput.value.trim(),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
appendDataToFormData: function (file, metadata) {
|
appendDataToFormData: function (file, metadata) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ const multipartMiddleware = multipart();
|
||||||
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');
|
||||||
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseNSFW, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
|
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
|
||||||
const errorHandlers = require('../helpers/errorHandlers.js');
|
const errorHandlers = require('../helpers/errorHandlers.js');
|
||||||
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
const { postToStats, sendGoogleAnalytics } = require('../controllers/statsController.js');
|
||||||
const { authenticateChannelCredentials } = require('../auth/authentication.js');
|
const { authenticateChannelCredentials } = require('../auth/authentication.js');
|
||||||
|
@ -71,7 +71,9 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// route to run a publish request on the daemon
|
// route to run a publish request on the daemon
|
||||||
app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl }, res) => {
|
app.post('/api/publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => {
|
||||||
|
logger.debug('api/publish body:', body);
|
||||||
|
let file, fileName, filePath, fileType, name, nsfw, license, title, description, thumbnail, anonymous, skipAuth, channelName, channelPassword;
|
||||||
// validate that mandatory parts of the request are present
|
// validate that mandatory parts of the request are present
|
||||||
try {
|
try {
|
||||||
validateApiPublishRequest(body, files);
|
validateApiPublishRequest(body, files);
|
||||||
|
@ -81,12 +83,12 @@ module.exports = (app) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// validate file, name, license, and nsfw
|
// validate file, name, license, and nsfw
|
||||||
const file = files.file;
|
file = files.file;
|
||||||
const fileName = file.name;
|
fileName = file.name;
|
||||||
const filePath = file.path;
|
filePath = file.path;
|
||||||
const fileType = file.type;
|
fileType = file.type;
|
||||||
const name = body.name;
|
name = body.name;
|
||||||
const nsfw = cleanseNSFW(body.nsfw); // cleanse nsfw input
|
nsfw = (body.nsfw === 'true');
|
||||||
try {
|
try {
|
||||||
validatePublishSubmission(file, name, nsfw);
|
validatePublishSubmission(file, name, nsfw);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -94,18 +96,36 @@ module.exports = (app) => {
|
||||||
res.status(400).json({success: false, message: error.message});
|
res.status(400).json({success: false, message: error.message});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
logger.debug(`name: ${name}, nsfw: ${nsfw}`);
|
|
||||||
// optional inputs
|
// optional inputs
|
||||||
const license = body.license || null;
|
license = body.license || null;
|
||||||
const title = body.title || null;
|
title = body.title || null;
|
||||||
const description = body.description || null;
|
description = body.description || null;
|
||||||
const thumbnail = body.thumbnail || null;
|
thumbnail = body.thumbnail || null;
|
||||||
let channelName = body.channelName || null;
|
anonymous = (body.channelName === 'null') || (body.channelName === undefined);
|
||||||
|
if (user) {
|
||||||
|
channelName = user.channelName || null;
|
||||||
|
} else {
|
||||||
|
channelName = body.channelName || null;
|
||||||
|
}
|
||||||
|
channelPassword = body.channelPassword || null;
|
||||||
|
skipAuth = false;
|
||||||
|
// case 1: publish from spee.ch, client logged in
|
||||||
|
if (user) {
|
||||||
|
skipAuth = true;
|
||||||
|
if (anonymous) {
|
||||||
|
channelName = null;
|
||||||
|
}
|
||||||
|
// case 2: publish from api or spee.ch, client not logged in
|
||||||
|
} else {
|
||||||
|
if (anonymous) {
|
||||||
|
skipAuth = true;
|
||||||
|
channelName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
channelName = cleanseChannelName(channelName);
|
channelName = cleanseChannelName(channelName);
|
||||||
const channelPassword = body.channelPassword || null;
|
logger.debug(`name: ${name}, license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`);
|
||||||
logger.debug(`license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`);
|
|
||||||
// check channel authorization
|
// check channel authorization
|
||||||
authenticateChannelCredentials(channelName, channelPassword)
|
authenticateChannelCredentials(skipAuth, channelName, channelPassword)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
if (!result) {
|
if (!result) {
|
||||||
throw new Error('Authentication failed, you do not have access to that channel');
|
throw new Error('Authentication failed, you do not have access to that channel');
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
<div class="column column--10">
|
<div class="column column--10">
|
||||||
<form>
|
<form>
|
||||||
<div class="column column--3 column--med-10">
|
<div class="column column--3 column--med-10">
|
||||||
<input type="radio" name="anonymous-or-channel" id="anonymous-select" class="input-radio" value="anonymous" {{#unless user}}checked {{/unless}} onchange="toggleChannel(event.target.value)"/>
|
<input type="radio" name="anonymous-or-channel" id="anonymous-radio" class="input-radio" value="anonymous" {{#unless user}}checked {{/unless}} onchange="toggleChannel(event.target.value)"/>
|
||||||
<label class="label label--pointer" for="anonymous-select">Anonymous</label>
|
<label class="label label--pointer" for="anonymous-radio">Anonymous</label>
|
||||||
</div><div class="column column--7 column--med-10">
|
</div><div class="column column--7 column--med-10">
|
||||||
<input type="radio" name="anonymous-or-channel" id="in-a-channel-select" class="input-radio" value="in a channel" {{#if user}}checked {{/if}} onchange="toggleChannel(event.target.value)"/>
|
<input type="radio" name="anonymous-or-channel" id="channel-radio" class="input-radio" value="in a channel" {{#if user}}checked {{/if}} onchange="toggleChannel(event.target.value)"/>
|
||||||
<label class="label label--pointer" for="in-a-channel-select">In a channel</label>
|
<label class="label label--pointer" for="channel-radio">In a channel</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in a new issue