commit
8faccd881a
13 changed files with 323 additions and 160 deletions
|
@ -1,2 +1,3 @@
|
||||||
node_modules/
|
node_modules/
|
||||||
public/
|
public/
|
||||||
|
test
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
],
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"always",
|
"always",
|
||||||
{ "omitLastInOneLineBlock": true }
|
{ "omitLastInOneLineBlock": true }
|
||||||
],
|
],
|
||||||
"key-spacing": [
|
"key-spacing": [
|
||||||
|
|
|
@ -21,6 +21,11 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
|
||||||
* To run hot, use `nodemon` instead of `node`
|
* To run hot, use `nodemon` instead of `node`
|
||||||
* visit [localhost:3000](http://localhost:3000)
|
* visit [localhost:3000](http://localhost:3000)
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
* Spee.ch uses `mocha` with `chai` for testing.
|
||||||
|
* To run all tests that do not require LBC, run `npm test -- --grep @usesLbc --invert`
|
||||||
|
* To run all tests, including those that require LBC (like publishing), simply run `npm test`
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
#### GET
|
#### GET
|
||||||
|
|
|
@ -34,9 +34,9 @@ module.exports = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
authenticateOrSkip (skipAuth, channelName, channelPassword) {
|
authenticateIfNoUserToken (channelName, channelPassword, user) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (skipAuth) {
|
if (user || !channelName) {
|
||||||
return resolve(true);
|
return resolve(true);
|
||||||
}
|
}
|
||||||
return resolve(module.exports.authenticateChannelCredentials(channelName, channelPassword));
|
return resolve(module.exports.authenticateChannelCredentials(channelName, channelPassword));
|
||||||
|
|
|
@ -2,6 +2,7 @@ const logger = require('winston');
|
||||||
const db = require('../models');
|
const db = require('../models');
|
||||||
const lbryApi = require('../helpers/lbryApi.js');
|
const lbryApi = require('../helpers/lbryApi.js');
|
||||||
const publishHelpers = require('../helpers/publishHelpers.js');
|
const publishHelpers = require('../helpers/publishHelpers.js');
|
||||||
|
const config = require('../config/speechConfig.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
publish (publishParams, fileName, fileType) {
|
publish (publishParams, fileName, fileType) {
|
||||||
|
@ -85,4 +86,45 @@ module.exports = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
checkClaimNameAvailability (name) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// find any records where the name is used
|
||||||
|
db.File.findAll({ where: { name } })
|
||||||
|
.then(result => {
|
||||||
|
if (result.length >= 1) {
|
||||||
|
const claimAddress = config.wallet.lbryClaimAddress;
|
||||||
|
// filter out any results that were not published from spee.ch's wallet address
|
||||||
|
const filteredResult = result.filter((claim) => {
|
||||||
|
return (claim.address === claimAddress);
|
||||||
|
});
|
||||||
|
// return based on whether any non-spee.ch claims were left
|
||||||
|
if (filteredResult.length >= 1) {
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
checkChannelAvailability (name) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
// find any records where the name is used
|
||||||
|
db.Channel.findAll({ where: { channelName: name } })
|
||||||
|
.then(result => {
|
||||||
|
if (result.length >= 1) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,41 +1,87 @@
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const db = require('../models');
|
|
||||||
const { site, wallet } = require('../config/speechConfig.js');
|
const { site, wallet } = require('../config/speechConfig.js');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
validateApiPublishRequest (body, files) {
|
parsePublishApiRequestBody ({name, nsfw, license, title, description, thumbnail}) {
|
||||||
if (!body) {
|
// validate name
|
||||||
throw new Error('no body found in request');
|
if (!name) {
|
||||||
}
|
|
||||||
if (!body.name) {
|
|
||||||
throw new Error('no name field found in request');
|
throw new Error('no name field found in request');
|
||||||
}
|
}
|
||||||
if (!files) {
|
const invalidNameCharacters = /[^A-Za-z0-9,-]/.exec(name);
|
||||||
throw new Error('no files found in request');
|
if (invalidNameCharacters) {
|
||||||
|
throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
|
||||||
}
|
}
|
||||||
if (!files.file) {
|
// optional parameters
|
||||||
|
nsfw = (nsfw === 'true');
|
||||||
|
license = license || null;
|
||||||
|
title = title || null;
|
||||||
|
description = description || null;
|
||||||
|
thumbnail = thumbnail || null;
|
||||||
|
// return results
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
nsfw,
|
||||||
|
license,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
thumbnail,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parsePublishApiRequestFiles ({file}) {
|
||||||
|
logger.debug('file', file);
|
||||||
|
// make sure a file was provided
|
||||||
|
if (!file) {
|
||||||
throw new Error('no file with key of [file] found in request');
|
throw new Error('no file with key of [file] found in request');
|
||||||
}
|
}
|
||||||
},
|
if (!file.path) {
|
||||||
validatePublishSubmission (file, claimName) {
|
throw new Error('no file path found');
|
||||||
try {
|
|
||||||
module.exports.validateFile(file);
|
|
||||||
module.exports.validateClaimName(claimName);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
},
|
if (!file.type) {
|
||||||
validateFile (file) {
|
throw new Error('no file type found');
|
||||||
if (!file) {
|
|
||||||
logger.debug('publish > file validation > no file found');
|
|
||||||
throw new Error('no file provided');
|
|
||||||
}
|
}
|
||||||
// check the file name
|
if (!file.size) {
|
||||||
|
throw new Error('no file type found');
|
||||||
|
}
|
||||||
|
// validate the file name
|
||||||
if (/'/.test(file.name)) {
|
if (/'/.test(file.name)) {
|
||||||
logger.debug('publish > file validation > file name had apostrophe in it');
|
logger.debug('publish > file validation > file name had apostrophe in it');
|
||||||
throw new Error('apostrophes are not allowed in the file name');
|
throw new Error('apostrophes are not allowed in the file name');
|
||||||
}
|
}
|
||||||
|
// validate the file
|
||||||
|
module.exports.validateFileTypeAndSize(file);
|
||||||
|
// return results
|
||||||
|
return {
|
||||||
|
fileName: file.name,
|
||||||
|
filePath: file.path,
|
||||||
|
fileType: file.type,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
parsePublishApiChannel ({channelName, channelPassword}, user) {
|
||||||
|
logger.debug('publish api parser input:', {channelName, channelPassword, user});
|
||||||
|
// if anonymous or '' provided, publish will be anonymous (even if client is logged in)
|
||||||
|
// if a channel name is provided...
|
||||||
|
if (channelName) {
|
||||||
|
// make sure a password was provided if no user token is provided
|
||||||
|
if (!user && !channelPassword) {
|
||||||
|
throw new Error('Unauthenticated channel name provided without password');
|
||||||
|
}
|
||||||
|
// if request comes from the client with a token
|
||||||
|
// ensure this publish uses that channel name
|
||||||
|
if (user) {
|
||||||
|
channelName = user.channelName;
|
||||||
|
} ;
|
||||||
|
// add the @ if the channel name is missing it
|
||||||
|
if (channelName.indexOf('@') !== 0) {
|
||||||
|
channelName = `@${channelName}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
channelName,
|
||||||
|
channelPassword,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
validateFileTypeAndSize (file) {
|
||||||
// check file type and size
|
// check file type and size
|
||||||
switch (file.type) {
|
switch (file.type) {
|
||||||
case 'image/jpeg':
|
case 'image/jpeg':
|
||||||
|
@ -64,21 +110,6 @@ module.exports = {
|
||||||
}
|
}
|
||||||
return file;
|
return file;
|
||||||
},
|
},
|
||||||
validateClaimName (claimName) {
|
|
||||||
const invalidCharacters = /[^A-Za-z0-9,-]/.exec(claimName);
|
|
||||||
if (invalidCharacters) {
|
|
||||||
throw new Error('The claim name you provided is not allowed. Only the following characters are allowed: A-Z, a-z, 0-9, and "-"');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cleanseChannelName (channelName) {
|
|
||||||
if (!channelName) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (channelName.indexOf('@') !== 0) {
|
|
||||||
channelName = `@${channelName}`;
|
|
||||||
}
|
|
||||||
return channelName;
|
|
||||||
},
|
|
||||||
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
|
||||||
|
@ -127,45 +158,5 @@ module.exports = {
|
||||||
logger.debug(`successfully deleted ${filePath}`);
|
logger.debug(`successfully deleted ${filePath}`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
checkClaimNameAvailability (name) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// find any records where the name is used
|
|
||||||
db.File.findAll({ where: { name } })
|
|
||||||
.then(result => {
|
|
||||||
if (result.length >= 1) {
|
|
||||||
const claimAddress = wallet.lbryClaimAddress;
|
|
||||||
// filter out any results that were not published from the site's wallet address
|
|
||||||
const filteredResult = result.filter((claim) => {
|
|
||||||
return (claim.address === claimAddress);
|
|
||||||
});
|
|
||||||
// return based on whether any claims were left
|
|
||||||
if (filteredResult.length >= 1) {
|
|
||||||
resolve(false);
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
resolve(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
checkChannelAvailability (name) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// find any records where the name is used
|
|
||||||
db.Channel.findAll({ where: { channelName: name } })
|
|
||||||
.then(result => {
|
|
||||||
if (result.length >= 1) {
|
|
||||||
return resolve(false);
|
|
||||||
}
|
|
||||||
resolve(true);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
reject(error);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
10
package.json
10
package.json
|
@ -4,7 +4,8 @@
|
||||||
"description": "a single-serving site that reads and publishes images to and from the LBRY blockchain",
|
"description": "a single-serving site that reads and publishes images to and from the LBRY blockchain",
|
||||||
"main": "speech.js",
|
"main": "speech.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "mocha --recursive",
|
||||||
|
"test-all": "mocha --recursive",
|
||||||
"start": "node speech.js",
|
"start": "node speech.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"fix": "eslint . --fix",
|
"fix": "eslint . --fix",
|
||||||
|
@ -40,6 +41,8 @@
|
||||||
"nodemon": "^1.11.0",
|
"nodemon": "^1.11.0",
|
||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-local": "^1.0.0",
|
"passport-local": "^1.0.0",
|
||||||
|
"request": "^2.83.0",
|
||||||
|
"request-promise": "^4.2.2",
|
||||||
"sequelize": "^4.1.0",
|
"sequelize": "^4.1.0",
|
||||||
"sequelize-cli": "^3.0.0-3",
|
"sequelize-cli": "^3.0.0-3",
|
||||||
"sleep": "^5.1.1",
|
"sleep": "^5.1.1",
|
||||||
|
@ -48,6 +51,8 @@
|
||||||
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"chai": "^4.1.2",
|
||||||
|
"chai-http": "^3.0.0",
|
||||||
"eslint": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
"eslint-config-standard": "10.2.1",
|
"eslint-config-standard": "10.2.1",
|
||||||
"eslint-plugin-import": "^2.2.0",
|
"eslint-plugin-import": "^2.2.0",
|
||||||
|
@ -55,6 +60,7 @@
|
||||||
"eslint-plugin-promise": "3.5.0",
|
"eslint-plugin-promise": "3.5.0",
|
||||||
"eslint-plugin-react": "6.10.3",
|
"eslint-plugin-react": "6.10.3",
|
||||||
"eslint-plugin-standard": "3.0.1",
|
"eslint-plugin-standard": "3.0.1",
|
||||||
"husky": "^0.13.4"
|
"husky": "^0.13.4",
|
||||||
|
"mocha": "^4.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,17 +78,20 @@ const publishFileFunctions = {
|
||||||
const licenseInput = document.getElementById('publish-license');
|
const licenseInput = document.getElementById('publish-license');
|
||||||
const nsfwInput = document.getElementById('publish-nsfw');
|
const nsfwInput = document.getElementById('publish-nsfw');
|
||||||
const thumbnailInput = document.getElementById('claim-thumbnail-input');
|
const thumbnailInput = document.getElementById('claim-thumbnail-input');
|
||||||
|
const channelName = this.returnNullOrChannel();
|
||||||
return {
|
let metadata = {
|
||||||
name: nameInput.value.trim(),
|
name: nameInput.value.trim(),
|
||||||
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(),
|
||||||
|
};
|
||||||
|
if (channelName) {
|
||||||
|
metadata['channelName'] = channelName;
|
||||||
}
|
}
|
||||||
|
return metadata;
|
||||||
},
|
},
|
||||||
appendDataToFormData: function (file, metadata) {
|
appendDataToFormData: function (file, metadata) {
|
||||||
var fd = new FormData();
|
var fd = new FormData();
|
||||||
|
@ -132,8 +135,6 @@ const publishFileFunctions = {
|
||||||
} else {
|
} else {
|
||||||
that.showFilePublishFailure(JSON.parse(xhr.response).message);
|
that.showFilePublishFailure(JSON.parse(xhr.response).message);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
console.log('xhr.readyState', xhr.readyState, 'xhr.status', xhr.status);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Initiate a multipart/form-data upload
|
// Initiate a multipart/form-data upload
|
||||||
|
@ -143,7 +144,7 @@ const publishFileFunctions = {
|
||||||
publishStagedFile: function (event) {
|
publishStagedFile: function (event) {
|
||||||
event.preventDefault(); // prevent default so this script can handle submission
|
event.preventDefault(); // prevent default so this script can handle submission
|
||||||
const metadata = this.createMetadata();
|
const metadata = this.createMetadata();
|
||||||
const that = this; // note: necessary ?
|
const that = this;
|
||||||
const fileSelectionInputError = document.getElementById('input-error-file-selection');
|
const fileSelectionInputError = document.getElementById('input-error-file-selection');
|
||||||
const claimNameError = document.getElementById('input-error-claim-name');
|
const claimNameError = document.getElementById('input-error-claim-name');
|
||||||
const channelSelectError = document.getElementById('input-error-channel-select');
|
const channelSelectError = document.getElementById('input-error-channel-select');
|
||||||
|
|
|
@ -3,11 +3,11 @@ const multipart = require('connect-multiparty');
|
||||||
const { files, site } = require('../config/speechConfig.js');
|
const { files, site } = require('../config/speechConfig.js');
|
||||||
const multipartMiddleware = multipart({uploadDir: files.uploadDirectory});
|
const multipartMiddleware = multipart({uploadDir: files.uploadDirectory});
|
||||||
const db = require('../models');
|
const db = require('../models');
|
||||||
const { publish } = require('../controllers/publishController.js');
|
const { checkClaimNameAvailability, checkChannelAvailability, publish } = require('../controllers/publishController.js');
|
||||||
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
const { getClaimList, resolveUri, getClaim } = require('../helpers/lbryApi.js');
|
||||||
const { createPublishParams, validateApiPublishRequest, validatePublishSubmission, cleanseChannelName, checkClaimNameAvailability, checkChannelAvailability } = require('../helpers/publishHelpers.js');
|
const { createPublishParams, parsePublishApiRequestBody, parsePublishApiRequestFiles, parsePublishApiChannel } = require('../helpers/publishHelpers.js');
|
||||||
const errorHandlers = require('../helpers/errorHandlers.js');
|
const errorHandlers = require('../helpers/errorHandlers.js');
|
||||||
const { authenticateOrSkip } = require('../auth/authentication.js');
|
const { authenticateIfNoUserToken } = require('../auth/authentication.js');
|
||||||
|
|
||||||
function addGetResultsToFileData (fileInfo, getResult) {
|
function addGetResultsToFileData (fileInfo, getResult) {
|
||||||
fileInfo.fileName = getResult.file_name;
|
fileInfo.fileName = getResult.file_name;
|
||||||
|
@ -125,60 +125,21 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
// route to run a publish request on the daemon
|
// route to run a publish request on the daemon
|
||||||
app.post('/api/claim-publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => {
|
app.post('/api/claim-publish', multipartMiddleware, ({ body, files, ip, originalUrl, user }, res) => {
|
||||||
let file, fileName, filePath, fileType, name, nsfw, license, title, description, thumbnail, anonymous, skipAuth, channelName, channelPassword;
|
logger.debug('api/claim-publish body:', body);
|
||||||
// validate that mandatory parts of the request are present
|
logger.debug('api/claim-publish files:', files);
|
||||||
|
let name, fileName, filePath, fileType, nsfw, license, title, description, thumbnail, channelName, channelPassword;
|
||||||
|
// validate the body and files of the request
|
||||||
try {
|
try {
|
||||||
validateApiPublishRequest(body, files);
|
// validateApiPublishRequest(body, files);
|
||||||
|
({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body));
|
||||||
|
({fileName, filePath, fileType} = parsePublishApiRequestFiles(files));
|
||||||
|
({channelName, channelPassword} = parsePublishApiChannel(body, user));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.debug('publish request rejected, insufficient request parameters');
|
logger.debug('publish request rejected, insufficient request parameters', error);
|
||||||
res.status(400).json({success: false, message: error.message});
|
return res.status(400).json({success: false, message: error.message});
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
logger.debug('publish req.files:', files);
|
|
||||||
// validate file, name, license, and nsfw
|
|
||||||
file = files.file;
|
|
||||||
fileName = file.path.substring(file.path.lastIndexOf('/') + 1);
|
|
||||||
filePath = file.path;
|
|
||||||
fileType = file.type;
|
|
||||||
name = body.name;
|
|
||||||
nsfw = (body.nsfw === 'true');
|
|
||||||
try {
|
|
||||||
validatePublishSubmission(file, name, nsfw);
|
|
||||||
} catch (error) {
|
|
||||||
logger.debug('publish request rejected');
|
|
||||||
res.status(400).json({success: false, message: error.message});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// optional inputs
|
|
||||||
license = body.license || null;
|
|
||||||
title = body.title || null;
|
|
||||||
description = body.description || null;
|
|
||||||
thumbnail = body.thumbnail || 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 client, client logged in
|
|
||||||
if (user) {
|
|
||||||
skipAuth = true;
|
|
||||||
if (anonymous) {
|
|
||||||
channelName = null;
|
|
||||||
}
|
|
||||||
// case 2: publish from api or client, client not logged in
|
|
||||||
} else {
|
|
||||||
if (anonymous) {
|
|
||||||
skipAuth = true;
|
|
||||||
channelName = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channelName = cleanseChannelName(channelName);
|
|
||||||
logger.debug(`name: ${name}, license: ${license} title: "${title}" description: "${description}" channelName: "${channelName}" channelPassword: "${channelPassword}" nsfw: "${nsfw}"`);
|
|
||||||
// check channel authorization
|
// check channel authorization
|
||||||
authenticateOrSkip(skipAuth, channelName, channelPassword)
|
authenticateIfNoUserToken(channelName, channelPassword, user)
|
||||||
.then(authenticated => {
|
.then(authenticated => {
|
||||||
if (!authenticated) {
|
if (!authenticated) {
|
||||||
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');
|
||||||
|
|
110
test/end-to-end/end-to-end.tests.js
Normal file
110
test/end-to-end/end-to-end.tests.js
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
const chai = require('chai');
|
||||||
|
const expect = chai.expect;
|
||||||
|
const chaiHttp = require('chai-http');
|
||||||
|
const { host } = require('../../config/speechConfig.js').site;
|
||||||
|
const requestTimeout = 20000;
|
||||||
|
const publishTimeout = 120000;
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
chai.use(chaiHttp);
|
||||||
|
|
||||||
|
function testFor200StatusResponse (host, url) {
|
||||||
|
return it(`should receive a status code 200 within ${requestTimeout}ms`, function (done) {
|
||||||
|
chai.request(host)
|
||||||
|
.get(url)
|
||||||
|
.end(function (err, res) {
|
||||||
|
expect(err).to.be.null;
|
||||||
|
expect(res).to.have.status(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}).timeout(requestTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testShowRequestFor200StatusResponse (host, url) {
|
||||||
|
return it(`should receive a status code 200 within ${requestTimeout}ms`, function (done) {
|
||||||
|
chai.request(host)
|
||||||
|
.get(url)
|
||||||
|
.set('accept', 'text/html')
|
||||||
|
.end(function (err, res) {
|
||||||
|
expect(err).to.be.null;
|
||||||
|
expect(res).to.have.status(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}).timeout(requestTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('end-to-end', function () {
|
||||||
|
describe('serve requests not from browser', function () {
|
||||||
|
const claimUrl = '/doitlive.jpg';
|
||||||
|
const claimUrlWithShortClaimId = '/d/doitlive.jpg';
|
||||||
|
const claimUrlWithLongClaimId = '/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg';
|
||||||
|
|
||||||
|
describe(claimUrl, function () {
|
||||||
|
testFor200StatusResponse(host, claimUrl);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithShortClaimId, function () {
|
||||||
|
testFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithLongClaimId, function () {
|
||||||
|
testFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('show requests from browser', function () {
|
||||||
|
const claimUrl = '/doitlive';
|
||||||
|
const claimUrlWithShortClaimId = '/d/doitlive';
|
||||||
|
const claimUrlWithLongClaimId = '/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive';
|
||||||
|
|
||||||
|
describe(claimUrl, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrl);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithShortClaimId, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithLongClaimId, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('serve requests browser (show lite)', function () {
|
||||||
|
const claimUrl = '/doitlive.jpg';
|
||||||
|
const claimUrlWithShortClaimId = '/d/doitlive.jpg';
|
||||||
|
const claimUrlWithLongClaimId = '/ca3023187e901df9e9aabd95d6ae09b6cc69b3f0/doitlive.jpg';
|
||||||
|
|
||||||
|
describe(claimUrl, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrl);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithShortClaimId, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
describe(claimUrlWithLongClaimId, function () {
|
||||||
|
testShowRequestFor200StatusResponse(host, claimUrlWithShortClaimId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('publish', function () {
|
||||||
|
const publishUrl = '/api/claim-publish';
|
||||||
|
const date = new Date();
|
||||||
|
const name = `test-publish-${date.getFullYear()}-${date.getMonth()}-${date.getDate()}-${date.getTime()}`;
|
||||||
|
const filePath = './test/mock-data/bird.jpeg';
|
||||||
|
const fileName = 'byrd.jpeg';
|
||||||
|
|
||||||
|
describe(publishUrl, function () {
|
||||||
|
it(`should receive a status code 200 within ${publishTimeout}ms @usesLbc`, function (done) {
|
||||||
|
chai.request(host)
|
||||||
|
.post(publishUrl)
|
||||||
|
.type('form')
|
||||||
|
.attach('file', fs.readFileSync(filePath), fileName)
|
||||||
|
.field('name', name)
|
||||||
|
.end(function (err, res) {
|
||||||
|
// expect(err).to.be.null;
|
||||||
|
expect(res).to.have.status(200);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
}).timeout(publishTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
BIN
test/mock-data/bird.jpeg
Normal file
BIN
test/mock-data/bird.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 90 KiB |
63
test/unit/publishHelpers.test.js
Normal file
63
test/unit/publishHelpers.test.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
const chai = require('chai');
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
describe('publishHelpers.js', function () {
|
||||||
|
const publishHelpers = require('../../helpers/publishHelpers.js');
|
||||||
|
|
||||||
|
describe('#parsePublishApiRequestBody()', function () {
|
||||||
|
it('should throw an error if no body', function () {
|
||||||
|
expect(publishHelpers.parsePublishApiRequestBody.bind(this, null)).to.throw();
|
||||||
|
});
|
||||||
|
it('should throw an error if no body.name', function () {
|
||||||
|
const bodyNoName = {};
|
||||||
|
expect(publishHelpers.parsePublishApiRequestBody.bind(this, bodyNoName)).to.throw();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#parsePublishApiRequestFiles()', function () {
|
||||||
|
it('should throw an error if no files', function () {
|
||||||
|
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, null)).to.throw();
|
||||||
|
});
|
||||||
|
it('should throw an error if no files.file', function () {
|
||||||
|
const filesNoFile = {};
|
||||||
|
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesNoFile)).to.throw();
|
||||||
|
});
|
||||||
|
it('should throw an error if file.size is too large', function () {
|
||||||
|
const filesTooBig = {
|
||||||
|
file: {
|
||||||
|
name: 'file.jpg',
|
||||||
|
path: '/path/to/file.jpg',
|
||||||
|
type: 'image/jpg',
|
||||||
|
size: 10000001,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesTooBig)).to.throw();
|
||||||
|
});
|
||||||
|
it('should throw error if not an accepted file type', function () {
|
||||||
|
const filesWrongType = {
|
||||||
|
file: {
|
||||||
|
name: 'file.jpg',
|
||||||
|
path: '/path/to/file.jpg',
|
||||||
|
type: 'someType/ext',
|
||||||
|
size: 10000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesWrongType)).to.throw();
|
||||||
|
});
|
||||||
|
it('should throw NO error if no problems', function () {
|
||||||
|
const filesNoProblems = {
|
||||||
|
file: {
|
||||||
|
name: 'file.jpg',
|
||||||
|
path: '/path/to/file.jpg',
|
||||||
|
type: 'image/jpg',
|
||||||
|
size: 10000000,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(publishHelpers.parsePublishApiRequestFiles.bind(this, filesNoProblems)).to.not.throw();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#parsePublishApiChannel()', function () {
|
||||||
|
it('should pass the tests I write here');
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,17 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>Test Page</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img src="https://dev1.spee.ch/zackmath"/>
|
|
||||||
<!--<img src="https://dev1.spee.ch/8/zackmath"/>-->
|
|
||||||
<!--<img src="https://dev1.spee.ch/zackmath.ext"/>-->
|
|
||||||
<!--<img src="https://dev1.spee.ch/8/zackmath.ext"/>-->
|
|
||||||
<video width="50%" controls poster="https://dev1.spee.ch/assets/img/video_thumb_default.png" src="https://dev1.spee.ch/LBRY-Hype"></video>
|
|
||||||
<!--<video width="50%" controls poster="https://dev1.spee.ch/assets/img/video_thumb_default.png" src="https://staging.spee.ch/a/LBRY-Hype"></video>-->
|
|
||||||
<!--<video width="50%" controls poster="https://dev1.spee.ch/assets/img/video_thumb_default.png" src="https://staging.spee.ch/LBRY-Hype.test"></video>-->
|
|
||||||
<!--<video width="50%" controls poster="https://dev1.spee.ch/assets/img/video_thumb_default.png" src="https://staging.spee.ch/a/LBRY-Hype.test"></video>-->
|
|
||||||
</body>
|
|
||||||
</html>
|
|
Loading…
Reference in a new issue