mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-13 15:12:43 +00:00
Merge branch 'newAdmin' into v3
This commit is contained in:
@@ -1,38 +1,41 @@
|
|||||||
var React = require('react');
|
const React = require('react');
|
||||||
var _ = require('lodash');
|
const _ = require('lodash');
|
||||||
var cx = require('classnames');
|
|
||||||
|
|
||||||
var HomebrewAdmin = require('./homebrewAdmin/homebrewAdmin.jsx');
|
const Nav = require('naturalcrit/nav/nav.jsx');
|
||||||
|
|
||||||
var Admin = React.createClass({
|
const BrewLookup = require('./brewLookup/brewLookup.jsx');
|
||||||
|
const AdminSearch = require('./adminSearch/adminSearch.jsx');
|
||||||
|
const InvalidBrew = require('./invalidBrew/invalidBrew.jsx');
|
||||||
|
|
||||||
|
const Admin = React.createClass({
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
return {
|
return {
|
||||||
url : "",
|
admin_key : '',
|
||||||
admin_key : "",
|
|
||||||
homebrews : [],
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
renderNavbar : function(){
|
||||||
|
return <Nav.base>
|
||||||
|
<Nav.section>
|
||||||
|
<Nav.item icon='fa-magic' className='homebreweryLogo'>
|
||||||
|
Homebrewery Admin
|
||||||
|
</Nav.item>
|
||||||
|
</Nav.section>
|
||||||
|
</Nav.base>
|
||||||
|
},
|
||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
var self = this;
|
return <div className='admin'>
|
||||||
return(
|
{this.renderNavbar()}
|
||||||
<div className='admin'>
|
<main className='content'>
|
||||||
|
<BrewLookup adminKey={this.props.admin_key} />
|
||||||
|
<AdminSearch adminKey={this.props.admin_key} />
|
||||||
|
|
||||||
<header>
|
<div className='dangerZone'>Danger Zone</div>
|
||||||
<div className='container'>
|
|
||||||
<i className='fa fa-rocket' />
|
|
||||||
naturalcrit admin
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div className='container'>
|
<InvalidBrew adminKey={this.props.admin_key} />
|
||||||
|
</main>
|
||||||
<HomebrewAdmin homebrews={this.props.homebrews} admin_key={this.props.admin_key} />
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,53 @@
|
|||||||
@import 'naturalcrit/styles/reset.less';
|
|
||||||
@import 'naturalcrit/styles/elements.less';
|
|
||||||
@import 'naturalcrit/styles/animations.less';
|
|
||||||
@import 'naturalcrit/styles/colors.less';
|
|
||||||
@import 'naturalcrit/styles/tooltip.less';
|
|
||||||
|
|
||||||
@import 'font-awesome/css/font-awesome.css';
|
@import 'naturalcrit/styles/core.less';
|
||||||
|
html,body, #reactRoot{
|
||||||
html,body, #reactContainer, .naturalCrit{
|
|
||||||
min-height : 100%;
|
min-height : 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@sidebarWidth : 250px;
|
|
||||||
|
|
||||||
body{
|
body{
|
||||||
background-color : #eee;
|
height : 100%;
|
||||||
font-family : 'Open Sans', sans-serif;
|
|
||||||
color : #4b5055;
|
|
||||||
font-weight : 100;
|
|
||||||
text-rendering : optimizeLegibility;
|
|
||||||
margin : 0;
|
margin : 0;
|
||||||
padding : 0;
|
padding : 0;
|
||||||
height : 100%;
|
background-color : #ddd;
|
||||||
|
font-family : 'Open Sans', sans-serif;
|
||||||
|
font-weight : 100;
|
||||||
|
color : #4b5055;
|
||||||
|
text-rendering : optimizeLegibility;
|
||||||
}
|
}
|
||||||
|
.admin {
|
||||||
.admin{
|
nav {
|
||||||
|
|
||||||
header{
|
|
||||||
background-color : @red;
|
background-color : @red;
|
||||||
font-size: 2em;
|
.navItem{
|
||||||
padding : 20px 0px;
|
background-color : @red;
|
||||||
color : white;
|
}
|
||||||
margin-bottom: 30px;
|
.homebreweryLogo{
|
||||||
i{
|
font-family : CodeBold;
|
||||||
margin-right: 30px;
|
font-size : 12px;
|
||||||
|
color : white;
|
||||||
|
div{
|
||||||
|
margin-top : 2px;
|
||||||
|
margin-bottom : -2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h1{
|
||||||
|
margin-bottom : 10px;
|
||||||
|
font-size : 2em;
|
||||||
|
font-weight : 800;
|
||||||
|
border-bottom : 1px solid #ddd;
|
||||||
|
}
|
||||||
|
main.content{
|
||||||
|
width : 1000px;
|
||||||
|
margin : 0 auto;
|
||||||
|
padding : 50px 20px;
|
||||||
|
background-color : white;
|
||||||
|
.dangerZone{
|
||||||
|
margin : 30px 0px;
|
||||||
|
padding : 10px 20px;
|
||||||
|
background : repeating-linear-gradient(45deg, @yellow, @yellow 10px, darken(#333, 10%) 10px, darken(#333, 10%) 20px);
|
||||||
|
font-size : 1em;
|
||||||
|
font-weight : 800;
|
||||||
|
color : white;
|
||||||
|
text-transform : uppercase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
86
client/admin/adminSearch/adminSearch.jsx
Normal file
86
client/admin/adminSearch/adminSearch.jsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
|
||||||
|
const React = require('react');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const cx = require('classnames');
|
||||||
|
const request = require('superagent');
|
||||||
|
|
||||||
|
const BrewTable = require('../brewTable/brewTable.jsx');
|
||||||
|
|
||||||
|
const LIMIT = 10;
|
||||||
|
|
||||||
|
const AdminSearch = React.createClass({
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
adminKey : '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
totalBrews : 1,
|
||||||
|
brews: [],
|
||||||
|
|
||||||
|
searching : false,
|
||||||
|
error : null,
|
||||||
|
|
||||||
|
page : 1,
|
||||||
|
searchTerms : ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
handleSearch : function(e){
|
||||||
|
this.setState({
|
||||||
|
searchTerms : e.target.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
handlePage : function(e){
|
||||||
|
this.setState({
|
||||||
|
page : e.target.value
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
search : function(){
|
||||||
|
this.setState({ searching : true, error : null });
|
||||||
|
|
||||||
|
request.get(`/api/brew`)
|
||||||
|
.query({
|
||||||
|
terms : this.state.searchTerms,
|
||||||
|
limit : LIMIT,
|
||||||
|
page : this.state.page - 1
|
||||||
|
})
|
||||||
|
.set('x-homebrew-admin', this.props.adminKey)
|
||||||
|
.end((err, res) => {
|
||||||
|
if(err){
|
||||||
|
this.setState({
|
||||||
|
searching : false,
|
||||||
|
error : err && err.toString()
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
this.setState({
|
||||||
|
brews : res.body.brews,
|
||||||
|
totalBrews : res.body.total
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function(){
|
||||||
|
return <div className='adminSearch'>
|
||||||
|
<h1>Admin Search</h1>
|
||||||
|
<div className='controls'>
|
||||||
|
<input className='search' type='text' value={this.state.searchTerms} onChange={this.handleSearch} />
|
||||||
|
|
||||||
|
<button onClick={this.search}> <i className='fa fa-search' /> search </button>
|
||||||
|
|
||||||
|
|
||||||
|
<div className='page'>
|
||||||
|
page:
|
||||||
|
<input type='text' value={this.state.page} onChange={this.handlePage} />
|
||||||
|
/ {Math.ceil(this.state.totalBrews / LIMIT)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<BrewTable brews={this.state.brews} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = AdminSearch;
|
||||||
17
client/admin/adminSearch/adminSearch.less
Normal file
17
client/admin/adminSearch/adminSearch.less
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
.adminSearch{
|
||||||
|
.controls{
|
||||||
|
margin-bottom : 20px;
|
||||||
|
input.search{
|
||||||
|
height : 33px;
|
||||||
|
padding : 10px;
|
||||||
|
}
|
||||||
|
.page {
|
||||||
|
float : right;
|
||||||
|
font-weight : 800;
|
||||||
|
input{
|
||||||
|
width : 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,7 @@ const _ = require('lodash');
|
|||||||
const cx = require('classnames');
|
const cx = require('classnames');
|
||||||
|
|
||||||
const request = require('superagent');
|
const request = require('superagent');
|
||||||
const Moment = require('moment');
|
const BrewTable = require('../brewTable/brewTable.jsx');
|
||||||
|
|
||||||
|
|
||||||
const BrewLookup = React.createClass({
|
const BrewLookup = React.createClass({
|
||||||
getDefaultProps: function() {
|
getDefaultProps: function() {
|
||||||
@@ -16,7 +15,8 @@ const BrewLookup = React.createClass({
|
|||||||
return {
|
return {
|
||||||
query:'',
|
query:'',
|
||||||
resultBrew : null,
|
resultBrew : null,
|
||||||
searching : false
|
searching : false,
|
||||||
|
error : null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -26,13 +26,14 @@ const BrewLookup = React.createClass({
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
lookup : function(){
|
lookup : function(){
|
||||||
this.setState({ searching : true });
|
this.setState({ searching : true, error : null });
|
||||||
|
|
||||||
request.get(`/admin/lookup/${this.state.query}`)
|
request.get(`/admin/lookup/${this.state.query}`)
|
||||||
.query({ admin_key : this.props.adminKey })
|
.set('x-homebrew-admin', this.props.adminKey)
|
||||||
.end((err, res) => {
|
.end((err, res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
searching : false,
|
searching : false,
|
||||||
|
error : err && err.toString(),
|
||||||
resultBrew : (err ? null : res.body)
|
resultBrew : (err ? null : res.body)
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
@@ -42,14 +43,31 @@ const BrewLookup = React.createClass({
|
|||||||
if(this.state.searching) return <div className='searching'><i className='fa fa-spin fa-spinner' /></div>;
|
if(this.state.searching) return <div className='searching'><i className='fa fa-spin fa-spinner' /></div>;
|
||||||
if(!this.state.resultBrew) return <div className='noBrew'>No brew found.</div>;
|
if(!this.state.resultBrew) return <div className='noBrew'>No brew found.</div>;
|
||||||
|
|
||||||
|
return <BrewTable brews={[this.state.resultBrew ]} />
|
||||||
|
|
||||||
|
/*
|
||||||
const brew = this.state.resultBrew;
|
const brew = this.state.resultBrew;
|
||||||
return <div className='brewRow'>
|
return <div className='brewRow'>
|
||||||
<div>{brew.title}</div>
|
<div>{brew.title}</div>
|
||||||
<div>{brew.authors.join(', ')}</div>
|
<div>{brew.authors.join(', ')}</div>
|
||||||
<div><a href={'/edit/' + brew.editId} target='_blank'>/edit/{brew.editId}</a></div>
|
<div><a href={'/edit/' + brew.editId} target='_blank'>{brew.editId}</a></div>
|
||||||
<div><a href={'/share/' + brew.shareId} target='_blank'>/share/{brew.shareId}</a></div>
|
<div><a href={'/share/' + brew.shareId} target='_blank'>{brew.shareId}</a></div>
|
||||||
<div>{Moment(brew.updatedAt).fromNow()}</div>
|
<div>{Moment(brew.updatedAt).fromNow()}</div>
|
||||||
<div>{brew.views}</div>
|
<div>{brew.views}</div>
|
||||||
|
<div>
|
||||||
|
<div className='deleteButton'>
|
||||||
|
<i className='fa fa-trash' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
*/
|
||||||
|
},
|
||||||
|
|
||||||
|
renderError : function(){
|
||||||
|
if(!this.state.error) return;
|
||||||
|
|
||||||
|
return <div className='error'>
|
||||||
|
{this.state.error}
|
||||||
</div>
|
</div>
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -60,6 +78,7 @@ const BrewLookup = React.createClass({
|
|||||||
<button onClick={this.lookup}><i className='fa fa-search'/></button>
|
<button onClick={this.lookup}><i className='fa fa-search'/></button>
|
||||||
|
|
||||||
{this.renderFoundBrew()}
|
{this.renderFoundBrew()}
|
||||||
|
{this.renderError()}
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
13
client/admin/brewLookup/brewLookup.less
Normal file
13
client/admin/brewLookup/brewLookup.less
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
.brewLookup{
|
||||||
|
height : 200px;
|
||||||
|
input{
|
||||||
|
height : 33px;
|
||||||
|
margin-bottom : 20px;
|
||||||
|
padding : 0px 10px;
|
||||||
|
}
|
||||||
|
.error{
|
||||||
|
font-weight : 800;
|
||||||
|
color : @red;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
client/admin/brewTable/brewTable.jsx
Normal file
54
client/admin/brewTable/brewTable.jsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const cx = require('classnames');
|
||||||
|
|
||||||
|
const Moment = require('moment');
|
||||||
|
|
||||||
|
//TODO: Add in delete
|
||||||
|
|
||||||
|
const BrewTable = React.createClass({
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
brews : []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
renderRows : function(){
|
||||||
|
return _.map(this.props.brews, (brew) => {
|
||||||
|
let authors = 'None.';
|
||||||
|
if(brew.authors && brew.authors.length) authors = brew.authors.join(', ');
|
||||||
|
|
||||||
|
return <tr className={cx('brewRow', {'isEmpty' : brew.text == "false"})} key={brew.shareId || brew}>
|
||||||
|
<td>{brew.title}</td>
|
||||||
|
<td>{authors}</td>
|
||||||
|
<td><a href={'/edit/' + brew.editId} target='_blank'>{brew.editId}</a></td>
|
||||||
|
<td><a href={'/share/' + brew.shareId} target='_blank'>{brew.shareId}</a></td>
|
||||||
|
<td>{Moment(brew.updatedAt).fromNow()}</td>
|
||||||
|
<td>{brew.views}</td>
|
||||||
|
<td className='deleteButton'>
|
||||||
|
<i className='fa fa-trash' />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
});
|
||||||
|
},
|
||||||
|
render: function(){
|
||||||
|
return <table className='brewTable'>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Title</th>
|
||||||
|
<th>Authors</th>
|
||||||
|
<th>Edit Link</th>
|
||||||
|
<th>Share Link</th>
|
||||||
|
<th>Last Updated</th>
|
||||||
|
<th>Views</th>
|
||||||
|
<th>Remove</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.renderRows()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = BrewTable;
|
||||||
44
client/admin/brewTable/brewTable.less
Normal file
44
client/admin/brewTable/brewTable.less
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
table.brewTable{
|
||||||
|
th{
|
||||||
|
padding : 10px;
|
||||||
|
background-color : fade(@blue, 20%);
|
||||||
|
font-weight : 800;
|
||||||
|
}
|
||||||
|
tr:nth-child(even){
|
||||||
|
background-color : fade(@green, 10%);
|
||||||
|
}
|
||||||
|
tr.isEmpty{
|
||||||
|
background-color : fade(@red, 30%);
|
||||||
|
}
|
||||||
|
td{
|
||||||
|
min-width : 100px;
|
||||||
|
padding : 10px;
|
||||||
|
text-align : center;
|
||||||
|
|
||||||
|
/*
|
||||||
|
&.preview{
|
||||||
|
position : relative;
|
||||||
|
&:hover{
|
||||||
|
.content{
|
||||||
|
display : block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.content{
|
||||||
|
position : absolute;
|
||||||
|
display : none;
|
||||||
|
top : 100%;
|
||||||
|
left : 0px;
|
||||||
|
z-index : 1000;
|
||||||
|
max-height : 500px;
|
||||||
|
width : 300px;
|
||||||
|
padding : 30px;
|
||||||
|
background-color : white;
|
||||||
|
font-family : monospace;
|
||||||
|
text-align : left;
|
||||||
|
pointer-events : none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
.brewLookup{
|
|
||||||
height : 200px;
|
|
||||||
input{
|
|
||||||
height : 33px;
|
|
||||||
padding : 0px 10px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
var React = require('react');
|
|
||||||
var _ = require('lodash');
|
|
||||||
var cx = require('classnames');
|
|
||||||
|
|
||||||
var request = require('superagent');
|
|
||||||
|
|
||||||
var BrewSearch = React.createClass({
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
admin_key : ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
searchTerm: '',
|
|
||||||
brew : null,
|
|
||||||
searching : false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
search : function(){
|
|
||||||
this.setState({
|
|
||||||
searching : true
|
|
||||||
});
|
|
||||||
|
|
||||||
request.get('/homebrew/api/search?id=' + this.state.searchTerm)
|
|
||||||
.query({
|
|
||||||
admin_key : this.props.admin_key,
|
|
||||||
})
|
|
||||||
.end((err, res)=>{
|
|
||||||
console.log(err, res, res.body.brews[0]);
|
|
||||||
this.setState({
|
|
||||||
brew : res.body.brews[0],
|
|
||||||
|
|
||||||
searching : false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handleChange : function(e){
|
|
||||||
this.setState({
|
|
||||||
searchTerm : e.target.value
|
|
||||||
});
|
|
||||||
},
|
|
||||||
handleSearchClick : function(){
|
|
||||||
this.search();
|
|
||||||
},
|
|
||||||
|
|
||||||
renderBrew : function(){
|
|
||||||
if(!this.state.brew) return null;
|
|
||||||
return <div className='brew'>
|
|
||||||
<div>Edit id : {this.state.brew.editId}</div>
|
|
||||||
<div>Share id : {this.state.brew.shareId}</div>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function(){
|
|
||||||
return <div className='search'>
|
|
||||||
<input type='text' value={this.state.searchTerm} onChange={this.handleChange} />
|
|
||||||
|
|
||||||
<button onClick={this.handleSearchClick}>Search</button>
|
|
||||||
|
|
||||||
{this.renderBrew()}
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = BrewSearch;
|
|
||||||
@@ -1,172 +0,0 @@
|
|||||||
var React = require('react');
|
|
||||||
var _ = require('lodash');
|
|
||||||
var cx = require('classnames');
|
|
||||||
var request = require('superagent');
|
|
||||||
|
|
||||||
var Moment = require('moment');
|
|
||||||
|
|
||||||
var BrewSearch = require('./brewSearch.jsx');
|
|
||||||
|
|
||||||
var BrewLookup = require('./brewLookup/brewLookup.jsx');
|
|
||||||
|
|
||||||
|
|
||||||
var HomebrewAdmin = React.createClass({
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
admin_key : ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
page: 0,
|
|
||||||
count : 20,
|
|
||||||
brewCache : {},
|
|
||||||
total : 0,
|
|
||||||
|
|
||||||
processingOldBrews : false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
fetchBrews : function(page){
|
|
||||||
request.get('/api/search')
|
|
||||||
.query({
|
|
||||||
admin_key : this.props.admin_key,
|
|
||||||
count : this.state.count,
|
|
||||||
page : page
|
|
||||||
})
|
|
||||||
.end((err, res)=>{
|
|
||||||
if(err || !res.body || !res.body.brews) return;
|
|
||||||
this.state.brewCache[page] = res.body.brews;
|
|
||||||
this.setState({
|
|
||||||
brewCache : this.state.brewCache,
|
|
||||||
total : res.body.total,
|
|
||||||
count : res.body.count
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this.fetchBrews(this.state.page);
|
|
||||||
},
|
|
||||||
|
|
||||||
changePageTo : function(page){
|
|
||||||
if(!this.state.brewCache[page]){
|
|
||||||
this.fetchBrews(page);
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
page : page
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
clearInvalidBrews : function(){
|
|
||||||
request.get('/api/invalid')
|
|
||||||
.query({admin_key : this.props.admin_key})
|
|
||||||
.end((err, res)=>{
|
|
||||||
if(!confirm("This will remove " + res.body.count + " brews. Are you sure?")) return;
|
|
||||||
request.get('/api/invalid')
|
|
||||||
.query({admin_key : this.props.admin_key, do_it : true})
|
|
||||||
.end((err, res)=>{
|
|
||||||
alert("Done!")
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
deleteBrew : function(brewId){
|
|
||||||
if(!confirm("Are you sure you want to delete '" + brewId + "'?")) return;
|
|
||||||
request.get('/api/remove/' + brewId)
|
|
||||||
.query({admin_key : this.props.admin_key})
|
|
||||||
.end(function(err, res){
|
|
||||||
window.location.reload();
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePageChange : function(dir){
|
|
||||||
this.changePageTo(this.state.page + dir);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
renderPagnination : function(){
|
|
||||||
var outOf;
|
|
||||||
if(this.state.total){
|
|
||||||
outOf = this.state.page + ' / ' + Math.round(this.state.total/this.state.count);
|
|
||||||
}
|
|
||||||
return <div className='pagnination'>
|
|
||||||
<i className='fa fa-chevron-left' onClick={this.handlePageChange.bind(this, -1)}/>
|
|
||||||
{outOf}
|
|
||||||
<i className='fa fa-chevron-right' onClick={this.handlePageChange.bind(this, 1)}/>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
renderBrews : function(){
|
|
||||||
var brews = this.state.brewCache[this.state.page] || _.times(this.state.count);
|
|
||||||
return _.map(brews, (brew)=>{
|
|
||||||
return <tr className={cx('brewRow', {'isEmpty' : brew.text == "false"})} key={brew.shareId || brew}>
|
|
||||||
<td><a href={'/edit/' + brew.editId} target='_blank'>{brew.editId}</a></td>
|
|
||||||
<td><a href={'/share/' + brew.shareId} target='_blank'>{brew.shareId}</a></td>
|
|
||||||
<td>{Moment(brew.createdAt).fromNow()}</td>
|
|
||||||
<td>{Moment(brew.updatedAt).fromNow()}</td>
|
|
||||||
<td>{Moment(brew.lastViewed).fromNow()}</td>
|
|
||||||
<td>{brew.views}</td>
|
|
||||||
<td>
|
|
||||||
<div className='deleteButton' onClick={this.deleteBrew.bind(this, brew.editId)}>
|
|
||||||
<i className='fa fa-trash' />
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
renderBrewTable : function(){
|
|
||||||
return <div className='brewTable'>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Edit Id</th>
|
|
||||||
<th>Share Id</th>
|
|
||||||
<th>Created At</th>
|
|
||||||
<th>Last Updated</th>
|
|
||||||
<th>Last Viewed</th>
|
|
||||||
<th>Views</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{this.renderBrews()}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
},
|
|
||||||
|
|
||||||
render : function(){
|
|
||||||
var self = this;
|
|
||||||
return <div className='homebrewAdmin'>
|
|
||||||
|
|
||||||
<BrewLookup adminKey={this.props.admin_key} />
|
|
||||||
|
|
||||||
{/*
|
|
||||||
<h2>
|
|
||||||
Homebrews - {this.state.total}
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{this.renderPagnination()}
|
|
||||||
{this.renderBrewTable()}
|
|
||||||
|
|
||||||
<button className='clearOldButton' onClick={this.clearInvalidBrews}>
|
|
||||||
Clear Old
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<BrewSearch admin_key={this.props.admin_key} />
|
|
||||||
*/}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = HomebrewAdmin;
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
|
|
||||||
.homebrewAdmin{
|
|
||||||
margin-bottom: 80px;
|
|
||||||
.brewTable{
|
|
||||||
table{
|
|
||||||
|
|
||||||
th{
|
|
||||||
padding : 10px;
|
|
||||||
font-weight : 800;
|
|
||||||
}
|
|
||||||
tr:nth-child(even){
|
|
||||||
background-color : fade(@green, 10%);
|
|
||||||
}
|
|
||||||
tr.isEmpty{
|
|
||||||
background-color : fade(@red, 30%);
|
|
||||||
}
|
|
||||||
td{
|
|
||||||
min-width : 100px;
|
|
||||||
padding : 10px;
|
|
||||||
text-align : center;
|
|
||||||
|
|
||||||
&.preview{
|
|
||||||
position : relative;
|
|
||||||
&:hover{
|
|
||||||
.content{
|
|
||||||
display : block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.content{
|
|
||||||
position : absolute;
|
|
||||||
display : none;
|
|
||||||
top : 100%;
|
|
||||||
left : 0px;
|
|
||||||
z-index : 1000;
|
|
||||||
max-height : 500px;
|
|
||||||
width : 300px;
|
|
||||||
padding : 30px;
|
|
||||||
background-color : white;
|
|
||||||
font-family : monospace;
|
|
||||||
text-align : left;
|
|
||||||
pointer-events : none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.deleteButton{
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
button.clearOldButton{
|
|
||||||
float : right;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
client/admin/invalidBrew/invalidBrew.jsx
Normal file
54
client/admin/invalidBrew/invalidBrew.jsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
const React = require('react');
|
||||||
|
const _ = require('lodash');
|
||||||
|
const cx = require('classnames');
|
||||||
|
|
||||||
|
const request = require('superagent');
|
||||||
|
const BrewTable = require('../brewTable/brewTable.jsx');
|
||||||
|
|
||||||
|
|
||||||
|
const InvalidBrew = React.createClass({
|
||||||
|
getDefaultProps: function() {
|
||||||
|
return {
|
||||||
|
adminKey : '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getInitialState: function() {
|
||||||
|
return {
|
||||||
|
brews: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
getInvalid : function(){
|
||||||
|
request.get(`/admin/invalid`)
|
||||||
|
.set('x-homebrew-admin', this.props.adminKey)
|
||||||
|
.end((err, res) => {
|
||||||
|
this.setState({
|
||||||
|
brews : res.body
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
removeInvalid : function(){
|
||||||
|
if(!this.state.brews.length) return;
|
||||||
|
if(!confirm(`Are you sure you want to remove ${this.state.brews.length} brews`)) return;
|
||||||
|
if(!confirm('Sure you are sure?')) return;
|
||||||
|
|
||||||
|
request.delete(`/admin/invalid`)
|
||||||
|
.set('x-homebrew-admin', this.props.adminKey)
|
||||||
|
.end((err, res) => {
|
||||||
|
console.log(err, res.body);
|
||||||
|
alert('Invalid brews removed!');
|
||||||
|
this.getInvalid();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render: function(){
|
||||||
|
return <div className='invalidBrew'>
|
||||||
|
<h1>Remove Invalid Brews</h1>
|
||||||
|
<div>This will removes all brews older than 3 days and shorter than a tweet.</div>
|
||||||
|
<button className='get' onClick={this.getInvalid}> Get Invalid Brews</button>
|
||||||
|
<button className='remove' disabled={this.state.brews.length == 0} onClick={this.removeInvalid}> Remove invalid Brews</button>
|
||||||
|
|
||||||
|
<BrewTable brews={this.state.brews} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = InvalidBrew;
|
||||||
5
client/admin/invalidBrew/invalidBrew.less
Normal file
5
client/admin/invalidBrew/invalidBrew.less
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.invalidBrew{
|
||||||
|
button{
|
||||||
|
margin: 10px 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -37,7 +37,6 @@ const Homebrew = React.createClass({
|
|||||||
loginPath : this.props.loginPath
|
loginPath : this.props.loginPath
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Router = CreateRouter({
|
Router = CreateRouter({
|
||||||
'/edit/:id' : <EditPage />,
|
'/edit/:id' : <EditPage />,
|
||||||
'/share/:id' : <SharePage />,
|
'/share/:id' : <SharePage />,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ const UserPage = React.createClass({
|
|||||||
|
|
||||||
render : function(){
|
render : function(){
|
||||||
const brews = this.getSortedBrews();
|
const brews = this.getSortedBrews();
|
||||||
|
console.log('user brews', brews);
|
||||||
|
|
||||||
return <div className='userPage page'>
|
return <div className='userPage page'>
|
||||||
<Navbar>
|
<Navbar>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
"quick": "node scripts/quick.js",
|
"quick": "node scripts/quick.js",
|
||||||
"build": "node scripts/build.js",
|
"build": "node scripts/build.js",
|
||||||
"phb": "node scripts/phb.js",
|
"phb": "node scripts/phb.js",
|
||||||
|
"populate": "node scripts/populate.js",
|
||||||
"prod": "set NODE_ENV=production&& npm run build",
|
"prod": "set NODE_ENV=production&& npm run build",
|
||||||
"postinstall": "npm run build",
|
"postinstall": "npm run build",
|
||||||
"start": "node server.js",
|
"start": "node server.js",
|
||||||
@@ -43,6 +44,7 @@
|
|||||||
"app-module-path": "^2.1.0",
|
"app-module-path": "^2.1.0",
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"chai-as-promised": "^6.0.0",
|
"chai-as-promised": "^6.0.0",
|
||||||
|
"chai-subset": "^1.4.0",
|
||||||
"mocha": "^3.2.0",
|
"mocha": "^3.2.0",
|
||||||
"supertest": "^2.0.1",
|
"supertest": "^2.0.1",
|
||||||
"supertest-as-promised": "^4.0.2"
|
"supertest-as-promised": "^4.0.2"
|
||||||
|
|||||||
22
scripts/populate.js
Normal file
22
scripts/populate.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
//Populates the DB with a bunch of brews for UI testing
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
const DB = require('../server/db.js');
|
||||||
|
const BrewData = require('../server/brew.data.js');
|
||||||
|
const BrewGen = require('../test/brew.gen.js');
|
||||||
|
|
||||||
|
return Promise.resolve()
|
||||||
|
.then(DB.connect)
|
||||||
|
.then(BrewData.removeAll)
|
||||||
|
.then(() => {
|
||||||
|
console.log('Adding random brews...');
|
||||||
|
return BrewGen.populateDB(BrewGen.random(50));
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
console.log('Adding specific brews...');
|
||||||
|
return BrewGen.populateDB(BrewGen.static());
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
return DB.close();
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
@@ -3,10 +3,21 @@ const router = require('express').Router();
|
|||||||
const vitreumRender = require('vitreum/steps/render');
|
const vitreumRender = require('vitreum/steps/render');
|
||||||
const templateFn = require('../client/template.js');
|
const templateFn = require('../client/template.js');
|
||||||
const config = require('nconf');
|
const config = require('nconf');
|
||||||
|
const Moment = require('moment');
|
||||||
|
|
||||||
const mw = require('./middleware.js');
|
const mw = require('./middleware.js');
|
||||||
const BrewData = require('./brew.data.js');
|
const BrewData = require('./brew.data.js');
|
||||||
|
|
||||||
|
|
||||||
|
const getInvalidBrewQuery = ()=>{
|
||||||
|
return BrewData.model.find({
|
||||||
|
'$where' : "this.text.length < 140",
|
||||||
|
createdAt: {
|
||||||
|
$lt: Moment().subtract(3, 'days').toDate()
|
||||||
|
}
|
||||||
|
}).select({ text : false });
|
||||||
|
}
|
||||||
|
|
||||||
router.get('/admin', mw.adminLogin, (req, res, next) => {
|
router.get('/admin', mw.adminLogin, (req, res, next) => {
|
||||||
return vitreumRender('admin', templateFn, {
|
return vitreumRender('admin', templateFn, {
|
||||||
url : req.originalUrl,
|
url : req.originalUrl,
|
||||||
@@ -19,12 +30,29 @@ router.get('/admin', mw.adminLogin, (req, res, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
//Removes all empty brews that are older than 3 days and that are shorter than a tweet
|
//Removes all empty brews that are older than 3 days and that are shorter than a tweet
|
||||||
|
router.get('/admin/invalid', mw.adminOnly, (req, res, next)=>{
|
||||||
|
getInvalidBrewQuery().exec()
|
||||||
|
.then((brews) => {
|
||||||
|
return res.json(brews);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{
|
router.delete('/admin/invalid', mw.adminOnly, (req, res, next)=>{
|
||||||
BrewData.removeInvalid(!!req.query.do_it)
|
getInvalidBrewQuery().remove()
|
||||||
.then((removedCount) => {
|
.then(()=>{
|
||||||
return res.join({
|
return res.status(200).send();
|
||||||
count : removedCount
|
})
|
||||||
});
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/admin/lookup/:search', mw.adminOnly, (req, res, next) => {
|
||||||
|
BrewData.get({ $or:[
|
||||||
|
//Searches for partial matches on either edit or share
|
||||||
|
{editId : { "$regex": req.params.search, "$options": "i" }},
|
||||||
|
{shareId : { "$regex": req.params.search, "$options": "i" }},
|
||||||
|
]})
|
||||||
|
.then((brews) => {
|
||||||
|
return res.json(brews);
|
||||||
})
|
})
|
||||||
.catch(next);
|
.catch(next);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,10 +6,25 @@ const mw = require('./middleware.js');
|
|||||||
|
|
||||||
//Search
|
//Search
|
||||||
router.get('/api/brew', (req, res, next) => {
|
router.get('/api/brew', (req, res, next) => {
|
||||||
|
const opts = _.pick(req.query, ['limit', 'sort', 'page']);
|
||||||
|
|
||||||
//TODO
|
BrewData.termSearch(req.query.terms, opts, req.admin)
|
||||||
|
.then((result) => {
|
||||||
|
return res.status(200).json(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
|
});
|
||||||
|
|
||||||
|
//User
|
||||||
|
router.get('/api/user/:username', (req, res, next) => {
|
||||||
|
const fullAccess = req.admin ||
|
||||||
|
!!(req.account && req.params.username == req.account.username);
|
||||||
|
|
||||||
|
BrewData.userSearch(req.params.username, fullAccess)
|
||||||
|
.then((result) => {
|
||||||
|
return res.status(200).json(result);
|
||||||
|
})
|
||||||
|
.catch(next);
|
||||||
});
|
});
|
||||||
|
|
||||||
//Get
|
//Get
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ const BrewSchema = mongoose.Schema({
|
|||||||
createdAt : { type: Date, default: Date.now },
|
createdAt : { type: Date, default: Date.now },
|
||||||
updatedAt : { type: Date, default: Date.now},
|
updatedAt : { type: Date, default: Date.now},
|
||||||
lastViewed : { type: Date, default: Date.now},
|
lastViewed : { type: Date, default: Date.now},
|
||||||
views : {type:Number, default:0}
|
views : {type:Number, default:0},
|
||||||
|
version : {type: Number, default:1}
|
||||||
}, {
|
}, {
|
||||||
versionKey: false,
|
versionKey: false,
|
||||||
toJSON : {
|
toJSON : {
|
||||||
@@ -32,6 +33,9 @@ const BrewSchema = mongoose.Schema({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//Index these fields for fast text searching
|
||||||
|
BrewSchema.index({ title: "text", description: "text" });
|
||||||
|
|
||||||
BrewSchema.methods.increaseView = function(){
|
BrewSchema.methods.increaseView = function(){
|
||||||
this.views = this.views + 1;
|
this.views = this.views + 1;
|
||||||
return this.save();
|
return this.save();
|
||||||
@@ -39,9 +43,6 @@ BrewSchema.methods.increaseView = function(){
|
|||||||
|
|
||||||
|
|
||||||
const Brew = mongoose.model('Brew', BrewSchema);
|
const Brew = mongoose.model('Brew', BrewSchema);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const BrewData = {
|
const BrewData = {
|
||||||
schema : BrewSchema,
|
schema : BrewSchema,
|
||||||
model : Brew,
|
model : Brew,
|
||||||
@@ -65,6 +66,9 @@ const BrewData = {
|
|||||||
delete newBrew.shareId;
|
delete newBrew.shareId;
|
||||||
delete newBrew.editId;
|
delete newBrew.editId;
|
||||||
brew = _.merge(brew, newBrew, { updatedAt : Date.now() });
|
brew = _.merge(brew, newBrew, { updatedAt : Date.now() });
|
||||||
|
|
||||||
|
brew.markModified('authors');
|
||||||
|
brew.markModified('systems');
|
||||||
return brew.save();
|
return brew.save();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -89,30 +93,8 @@ const BrewData = {
|
|||||||
getByEdit : (editId) => {
|
getByEdit : (editId) => {
|
||||||
return BrewData.get({ editId });
|
return BrewData.get({ editId });
|
||||||
},
|
},
|
||||||
|
|
||||||
//Removes all empty brews that are older than 3 days and that are shorter than a tweet
|
|
||||||
removeInvalid : (force = false) => {
|
|
||||||
const invalidBrewQuery = Brew.find({
|
|
||||||
'$where' : "this.text.length < 140",
|
|
||||||
createdAt: {
|
|
||||||
$lt: Moment().subtract(3, 'days').toDate()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(force) return invalidBrewQuery.remove().exec();
|
|
||||||
return invalidBrewQuery.exec()
|
|
||||||
.then((objs) => {
|
|
||||||
return objs.length;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
search : (query, req={}) => {
|
|
||||||
//defaults with page and count
|
|
||||||
//returns a non-text version of brews
|
|
||||||
//assume sanatized ?
|
|
||||||
return Promise.resolve([]);
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = BrewData;
|
const BrewSearch = require('./brew.search.js')(Brew);
|
||||||
|
|
||||||
|
module.exports = _.merge(BrewData, BrewSearch);
|
||||||
66
server/brew.search.js
Normal file
66
server/brew.search.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
module.exports = (Brew) => {
|
||||||
|
const cmds = {
|
||||||
|
termSearch : (terms='', opts, fullAccess) => {
|
||||||
|
let query = {};
|
||||||
|
if(terms){
|
||||||
|
query = {$text: {
|
||||||
|
//Wrap terms in quotes to perform an AND operation
|
||||||
|
$search: _.map(terms.split(' '), (term)=>{
|
||||||
|
return `\"${term}\"`;
|
||||||
|
}).join(' '),
|
||||||
|
$caseSensitive : false
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
return cmds.search(query, opts, fullAccess);
|
||||||
|
},
|
||||||
|
|
||||||
|
userSearch : (username, fullAccess) => {
|
||||||
|
const query = {
|
||||||
|
authors : username
|
||||||
|
};
|
||||||
|
|
||||||
|
return cmds.search(query, {}, fullAccess);
|
||||||
|
},
|
||||||
|
|
||||||
|
search : (queryObj={}, options={}, fullAccess = true) => {
|
||||||
|
const opts = _.merge({
|
||||||
|
limit : 25,
|
||||||
|
page : 0,
|
||||||
|
sort : {}
|
||||||
|
}, options);
|
||||||
|
opts.limit = _.toNumber(opts.limit);
|
||||||
|
opts.page = _.toNumber(opts.page);
|
||||||
|
|
||||||
|
let filter = {
|
||||||
|
text : 0
|
||||||
|
};
|
||||||
|
|
||||||
|
if(!fullAccess){
|
||||||
|
filter.editId = 0;
|
||||||
|
queryObj.published = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchQuery = Brew
|
||||||
|
.find(queryObj)
|
||||||
|
.sort(opts.sort)
|
||||||
|
.select(filter)
|
||||||
|
.limit(opts.limit)
|
||||||
|
.skip(opts.page * opts.limit)
|
||||||
|
.lean()
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
const countQuery = Brew.count(queryObj).exec();
|
||||||
|
|
||||||
|
return Promise.all([searchQuery, countQuery])
|
||||||
|
.then((result) => {
|
||||||
|
return {
|
||||||
|
brews : result[0],
|
||||||
|
total : result[1]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return cmds;
|
||||||
|
};
|
||||||
14
server/db.js
14
server/db.js
@@ -8,14 +8,14 @@ module.exports = {
|
|||||||
connect : ()=>{
|
connect : ()=>{
|
||||||
return new Promise((resolve, reject)=>{
|
return new Promise((resolve, reject)=>{
|
||||||
if(mongoose.connection.readyState == 1){
|
if(mongoose.connection.readyState == 1){
|
||||||
log.info('DB already connected');
|
log.warn('DB already connected');
|
||||||
return resolve();
|
return resolve();
|
||||||
}
|
}
|
||||||
mongoose.connect(dbPath,
|
mongoose.connect(dbPath,
|
||||||
(err) => {
|
(err) => {
|
||||||
if(err){
|
if(err){
|
||||||
log.info('Error : Could not connect to a Mongo Database.');
|
log.error('Error : Could not connect to a Mongo Database.');
|
||||||
log.info(' If you are running locally, make sure mongodb.exe is running.');
|
log.error(' If you are running locally, make sure mongodb.exe is running.');
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
log.info('DB connected.');
|
log.info('DB connected.');
|
||||||
@@ -24,5 +24,13 @@ module.exports = {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
close : ()=>{
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
mongoose.connection.close(()=>{
|
||||||
|
log.info('DB connection closed.');
|
||||||
|
return resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
instance : mongoose
|
instance : mongoose
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,8 @@ const Middleware = {
|
|||||||
return next();
|
return next();
|
||||||
},
|
},
|
||||||
admin : (req, res, next) => {
|
admin : (req, res, next) => {
|
||||||
if(req.query.admin_key === config.get('admin:key')){
|
req.admin = false;
|
||||||
|
if(req.headers['x-homebrew-admin'] === config.get('admin:key')){
|
||||||
req.admin = true;
|
req.admin = true;
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
@@ -44,6 +45,7 @@ const Middleware = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: REMOVE
|
||||||
//Loaders
|
//Loaders
|
||||||
loadBrew : (req, res, next) => {
|
loadBrew : (req, res, next) => {
|
||||||
BrewData.getByEdit(req.params.editId)
|
BrewData.getByEdit(req.params.editId)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ Store.init = (state)=>{
|
|||||||
Store.getLoginPath = ()=>{
|
Store.getLoginPath = ()=>{
|
||||||
let path = State.loginPath;
|
let path = State.loginPath;
|
||||||
if(typeof window !== 'undefined'){
|
if(typeof window !== 'undefined'){
|
||||||
console.log('yo here');
|
|
||||||
path = `${path}?redirect=${encodeURIComponent(window.location.href)}`;
|
path = `${path}?redirect=${encodeURIComponent(window.location.href)}`;
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
|
|||||||
@@ -76,4 +76,7 @@
|
|||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
line-height : 1.5em;
|
line-height : 1.5em;
|
||||||
}
|
}
|
||||||
|
.thumbnail.field{
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
90
test/admin.test.js
Normal file
90
test/admin.test.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
const testing = require('./test.init.js');
|
||||||
|
const request = require('supertest-as-promised');
|
||||||
|
|
||||||
|
const config = require('nconf');
|
||||||
|
|
||||||
|
const app = require('app.js');
|
||||||
|
const DB = require('db.js');
|
||||||
|
const BrewData = require('brew.data.js');
|
||||||
|
const Error = require('error.js');
|
||||||
|
|
||||||
|
|
||||||
|
let brewA = {
|
||||||
|
title : 'good title',
|
||||||
|
text : 'original text',
|
||||||
|
authors : ['your_dm']
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
describe('Admin API', ()=>{
|
||||||
|
before('Connect DB', DB.connect);
|
||||||
|
|
||||||
|
|
||||||
|
describe('Brew Lookup', ()=>{
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Create brew', ()=>{
|
||||||
|
return BrewData.create(brewA)
|
||||||
|
.then((brew)=>{ brewA = brew; });
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('throws an error if not admin', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/admin/lookup/${brewA.editId}`)
|
||||||
|
.expect(401);
|
||||||
|
});
|
||||||
|
it('looks up a brew based on the share id', () => {
|
||||||
|
return request(app)
|
||||||
|
.get(`/admin/lookup/${brewA.shareId}`)
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const brew = res.body;
|
||||||
|
brew.should.have.property('editId').equal(brewA.editId);
|
||||||
|
brew.should.have.property('shareId').equal(brewA.shareId);
|
||||||
|
brew.should.have.property('text').equal(brewA.text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('looks up a brew based on the edit id', () => {
|
||||||
|
return request(app)
|
||||||
|
.get(`/admin/lookup/${brewA.editId}`)
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const brew = res.body;
|
||||||
|
brew.should.have.property('editId').equal(brewA.editId);
|
||||||
|
brew.should.have.property('shareId').equal(brewA.shareId);
|
||||||
|
brew.should.have.property('text').equal(brewA.text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('looks up a brew based on a partial id', () => {
|
||||||
|
const query = brewA.editId.substring(0, brewA.editId.length -2);
|
||||||
|
return request(app)
|
||||||
|
.get(`/admin/lookup/${query}`)
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const brew = res.body;
|
||||||
|
brew.should.have.property('editId').equal(brewA.editId);
|
||||||
|
brew.should.have.property('shareId').equal(brewA.shareId);
|
||||||
|
brew.should.have.property('text').equal(brewA.text);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('throws an error if it can not find a brew', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/admin/lookup/BADID`)
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.expect(404);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Invalid Brew', ()=>{
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Create brew', ()=>{
|
||||||
|
return BrewData.create(brewA)
|
||||||
|
.then((brew)=>{ brewA = brew; });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
302
test/api.test.js
302
test/api.test.js
@@ -1,112 +1,246 @@
|
|||||||
const testing = require('./test.init.js');
|
const Test = require('./test.init.js');
|
||||||
|
const _ = require('lodash');
|
||||||
const request = require('supertest-as-promised');
|
const request = require('supertest-as-promised');
|
||||||
const jwt = require('jwt-simple');
|
|
||||||
const config = require('nconf');
|
const config = require('nconf');
|
||||||
|
|
||||||
const app = require('app.js');
|
const app = require('app.js');
|
||||||
const DB = require('db.js');
|
const DB = require('db.js');
|
||||||
const BrewData = require('brew.data.js');
|
const BrewData = require('brew.data.js');
|
||||||
|
const BrewGen = require('./brew.gen.js');
|
||||||
const Error = require('error.js');
|
const Error = require('error.js');
|
||||||
|
|
||||||
const apiPath = '/api/brew';
|
|
||||||
|
|
||||||
let session_token;
|
const UserX = { username : 'userX' };
|
||||||
const test_user = {
|
const UserA = { username : 'userA' };
|
||||||
username : 'cool guy'
|
let UserXToken, UserAToken;
|
||||||
};
|
|
||||||
let storedBrew = {
|
|
||||||
title : 'good title',
|
|
||||||
text : 'original text',
|
|
||||||
authors : ['your_dm']
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Brew API', () => {
|
describe('Brew API', () => {
|
||||||
before('Connect DB', DB.connect);
|
|
||||||
before('Clear DB', BrewData.removeAll);
|
|
||||||
before('Create session token', () => {
|
before('Create session token', () => {
|
||||||
session_token = jwt.encode(test_user, config.get('jwt_secret'));
|
UserXToken = Test.getSessionToken(UserX);
|
||||||
});
|
UserAToken = Test.getSessionToken(UserA);
|
||||||
before('Create brew', ()=>{
|
|
||||||
return BrewData.create(storedBrew)
|
|
||||||
.then((brew)=>{ storedBrew = brew; });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('CRUD', ()=>{
|
||||||
|
before('Connect DB', DB.connect);
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Populate brews', ()=>{
|
||||||
|
return BrewGen.populateDB(BrewGen.static());
|
||||||
|
});
|
||||||
|
describe('Create', () => {
|
||||||
|
it('creates a new brew', () => {
|
||||||
|
return request(app)
|
||||||
|
.post(`/api/brew`)
|
||||||
|
.send({ text : 'Brew Text' })
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const brew = res.body;
|
||||||
|
brew.should.have.property('editId').that.is.a('string');
|
||||||
|
brew.should.have.property('shareId').that.is.a('string');
|
||||||
|
brew.should.have.property('text').equal('Brew Text');
|
||||||
|
brew.should.not.have.property('_id');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Create', () => {
|
it('creates a new brew with a session author', () => {
|
||||||
it('creates a new brew', () => {
|
return request(app)
|
||||||
return request(app)
|
.post(`/api/brew`)
|
||||||
.post(apiPath)
|
.set('Cookie', `nc_session=${UserXToken}`)
|
||||||
.send({ text : 'Brew Text' })
|
.send({ text : 'Brew Text' })
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const brew = res.body;
|
const brew = res.body;
|
||||||
brew.should.have.property('editId').that.is.a('string');
|
brew.should.have.property('authors').include(UserX.username);
|
||||||
brew.should.have.property('shareId').that.is.a('string');
|
});
|
||||||
brew.should.have.property('text').equal('Brew Text');
|
});
|
||||||
brew.should.not.have.property('_id');
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('creates a new brew with a session author', () => {
|
describe('Update', () => {
|
||||||
return request(app)
|
it('updates an existing brew', () => {
|
||||||
.post(apiPath)
|
const storedBrew = BrewGen.get('BrewA');
|
||||||
.set('Cookie', `nc_session=${session_token}`)
|
return request(app)
|
||||||
.send({ text : 'Brew Text' })
|
.put(`/api/brew/${storedBrew.editId}`)
|
||||||
.expect(200)
|
.send({ text : 'New Text' })
|
||||||
.then((res) => {
|
.expect(200)
|
||||||
const brew = res.body;
|
.then((res) => {
|
||||||
brew.should.have.property('authors').include(test_user.username);
|
const brew = res.body;
|
||||||
});
|
brew.should.have.property('editId').equal(storedBrew.editId);
|
||||||
});
|
brew.should.have.property('text').equal('New Text');
|
||||||
});
|
brew.should.have.property('authors').include(storedBrew.authors[0]);
|
||||||
|
brew.should.not.have.property('_id');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Update', () => {
|
it('adds the user as author', () => {
|
||||||
it('updates an existing brew', () => {
|
const storedBrew = BrewGen.get('BrewA');
|
||||||
return request(app)
|
return request(app)
|
||||||
.put(`${apiPath}/${storedBrew.editId}`)
|
.put(`/api/brew/${storedBrew.editId}`)
|
||||||
.send({ text : 'New Text' })
|
.set('Cookie', `nc_session=${UserXToken}`)
|
||||||
.expect(200)
|
.send({ text : 'New Text' })
|
||||||
.then((res) => {
|
.expect(200)
|
||||||
const brew = res.body;
|
.then((res) => {
|
||||||
brew.should.have.property('editId').equal(storedBrew.editId);
|
const brew = res.body;
|
||||||
brew.should.have.property('text').equal('New Text');
|
brew.should.have.property('authors').include(UserX.username);
|
||||||
brew.should.have.property('authors').include('your_dm');
|
brew.should.have.property('authors').include(storedBrew.authors[0]);
|
||||||
brew.should.not.have.property('_id');
|
});
|
||||||
});
|
});
|
||||||
|
it('should throw error on bad edit id', ()=>{
|
||||||
|
const storedBrew = BrewGen.get('BrewA');
|
||||||
|
return request(app)
|
||||||
|
.put(`/api/brew/BADEDITID`)
|
||||||
|
.send({ text : 'New Text' })
|
||||||
|
.expect(404)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('adds the user as author', () => {
|
describe('Remove', () => {
|
||||||
return request(app)
|
it('should removes a brew', ()=>{
|
||||||
.put(`${apiPath}/${storedBrew.editId}`)
|
const storedBrew = BrewGen.get('BrewA');
|
||||||
.set('Cookie', `nc_session=${session_token}`)
|
return request(app)
|
||||||
.send({ text : 'New Text' })
|
.del(`/api/brew/${storedBrew.editId}`)
|
||||||
.expect(200)
|
.send()
|
||||||
.then((res) => {
|
.expect(200)
|
||||||
const brew = res.body;
|
.then(() => {
|
||||||
brew.should.have.property('authors').include(test_user.username);
|
BrewData.getByEdit(storedBrew.editId)
|
||||||
brew.should.have.property('authors').include('your_dm');
|
.then(() => { throw 'Brew found when one should not have been'; })
|
||||||
});
|
.catch((err) => {
|
||||||
|
err.should.be.instanceof(Error.noBrew);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
it('should throw error on bad edit id', ()=>{
|
})
|
||||||
return request(app)
|
|
||||||
.put(`${apiPath}/BADEDITID`)
|
|
||||||
.send({ text : 'New Text' })
|
|
||||||
.expect(404)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Remove', () => {
|
|
||||||
it('should removes a brew', ()=>{
|
describe('Search', () => {
|
||||||
|
before('Connect DB', DB.connect);
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Populate brews', ()=>{
|
||||||
|
return BrewGen.populateDB(BrewGen.static());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to search for all published brews', ()=>{
|
||||||
return request(app)
|
return request(app)
|
||||||
.del(`${apiPath}/${storedBrew.editId}`)
|
.get(`/api/brew`)
|
||||||
|
.query({})
|
||||||
.send()
|
.send()
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then(() => {
|
.then((res) => {
|
||||||
BrewData.getByEdit(storedBrew.editId)
|
const result = res.body;
|
||||||
.then(() => { throw 'Brew found when one should not have been'; })
|
result.total.should.be.equal(2);
|
||||||
.catch((err) => {
|
result.brews.should.have.brews('BrewB','BrewD');
|
||||||
err.should.be.instanceof(Error.noBrew);
|
result.brews[0].should.not.have.property('editId');
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to search for brews with given terms', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/brew`)
|
||||||
|
.query({
|
||||||
|
terms : '5e ranger'
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(1);
|
||||||
|
result.brews.should.have.brews('BrewD');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should be able to sort the search', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/brew`)
|
||||||
|
.query({
|
||||||
|
sort : { views : 1}
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(2);
|
||||||
|
result.brews[0].should.be.brew('BrewD');
|
||||||
|
result.brews[1].should.be.brew('BrewB');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should use pagniation on the search', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/brew`)
|
||||||
|
.query({
|
||||||
|
limit : 1,
|
||||||
|
page : 1,
|
||||||
|
sort : { views : -1}
|
||||||
|
})
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(2);
|
||||||
|
result.brews[0].should.be.brew('BrewD');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
it('should return all brews and editIds if admin', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/brew`)
|
||||||
|
.query({})
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
const brewCount = _.size(BrewGen.static());
|
||||||
|
result.total.should.be.equal(brewCount);
|
||||||
|
result.brews.length.should.be.equal(brewCount);
|
||||||
|
result.brews[0].should.have.property('editId');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('User', () => {
|
||||||
|
before('Connect DB', DB.connect);
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Populate brews', ()=>{
|
||||||
|
return BrewGen.populateDB(BrewGen.static());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to query brews for a specific user', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/user/userA`)
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(1);
|
||||||
|
result.brews.length.should.be.equal(1);
|
||||||
|
result.brews.should.have.brews('BrewB');
|
||||||
|
result.brews[0].should.not.have.property('editId');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should have full access if loggedin user is queried user', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/user/userA`)
|
||||||
|
.set('Cookie', `nc_session=${UserAToken}`)
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(3);
|
||||||
|
result.brews.length.should.be.equal(3);
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC');
|
||||||
|
result.brews[0].should.have.property('editId');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should have full access if admin', ()=>{
|
||||||
|
return request(app)
|
||||||
|
.get(`/api/user/userA`)
|
||||||
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
|
.send()
|
||||||
|
.expect(200)
|
||||||
|
.then((res) => {
|
||||||
|
const result = res.body;
|
||||||
|
result.total.should.be.equal(3);
|
||||||
|
result.brews.length.should.be.equal(3);
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC');
|
||||||
|
result.brews[0].should.have.property('editId');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
111
test/brew.gen.js
Normal file
111
test/brew.gen.js
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
const _ = require('lodash');
|
||||||
|
const BrewData = require('../server/brew.data.js');
|
||||||
|
|
||||||
|
let PopulatedBrews = {};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
//TODO: Add in a generator for old brews to test the old rendering code
|
||||||
|
|
||||||
|
random : (num = 20)=>{
|
||||||
|
return _.times(num, ()=>{
|
||||||
|
//TODO: Build better generator
|
||||||
|
return {
|
||||||
|
title : 'BrewA',
|
||||||
|
description : '',
|
||||||
|
text : '',
|
||||||
|
authors : _.sampleSize(['userA','userB','userC','userD'], _.random(0, 3)),
|
||||||
|
systems : _.sampleSize(['5e', '4e', '3.5e', 'Pathfinder'], _.random(0,2)),
|
||||||
|
views : _.random(0,1000),
|
||||||
|
published : !!_.random(0,1)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
static : () => {
|
||||||
|
return {
|
||||||
|
BrewA : {
|
||||||
|
title : 'Brew-Alpha',
|
||||||
|
description : 'fancy',
|
||||||
|
authors : ['userA'],
|
||||||
|
systems : [],
|
||||||
|
views : 12,
|
||||||
|
published : false
|
||||||
|
},
|
||||||
|
BrewB : {
|
||||||
|
title : 'Brew-Beta',
|
||||||
|
description : 'very fancy',
|
||||||
|
authors : ['userA'],
|
||||||
|
systems : [],
|
||||||
|
views : 7,
|
||||||
|
published : true
|
||||||
|
},
|
||||||
|
BrewC : {
|
||||||
|
title : 'Brew-Charlie',
|
||||||
|
description : 'test',
|
||||||
|
authors : ['userA', 'userB'],
|
||||||
|
systems : [],
|
||||||
|
views : 0,
|
||||||
|
published : false
|
||||||
|
},
|
||||||
|
BrewD : {
|
||||||
|
title : 'Brew-Delta',
|
||||||
|
description : 'test super amazing brew for 5e. Geared for Rangers.',
|
||||||
|
authors : ['userC'],
|
||||||
|
systems : [],
|
||||||
|
views : 1,
|
||||||
|
published : true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
populateDB : (brewCollection)=>{
|
||||||
|
PopulatedBrews = {};
|
||||||
|
return Promise.all(_.map(brewCollection, (brewData, id) => {
|
||||||
|
return BrewData.create(brewData)
|
||||||
|
.then((brew)=>{
|
||||||
|
PopulatedBrews[id] = brew;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
get : (brewId) => {
|
||||||
|
return PopulatedBrews[brewId]
|
||||||
|
},
|
||||||
|
|
||||||
|
chaiPlugin : (chai, utils) => {
|
||||||
|
chai.Assertion.addMethod('brews', function(...brewIds){
|
||||||
|
new chai.Assertion(this._obj).to.be.instanceof(Array);
|
||||||
|
const valid = _.every(brewIds, (brewId) => {
|
||||||
|
const storedBrew = PopulatedBrews[brewId];
|
||||||
|
if(!storedBrew) return false;
|
||||||
|
return _.some(this._obj, (brew)=>{
|
||||||
|
return brew.shareId == storedBrew.shareId &&
|
||||||
|
brew.title == storedBrew.title &&
|
||||||
|
brew.views == storedBrew.views;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
this.assert(
|
||||||
|
valid,
|
||||||
|
`expect #{this} to have brews ${brewIds.join(', ')}`,
|
||||||
|
`expect #{this} to not have brews ${brewIds.join(', ')}`
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
chai.Assertion.addMethod('brew', function(brewId){
|
||||||
|
new chai.Assertion(this._obj).to.be.instanceof(Object);
|
||||||
|
const brew = this._obj;
|
||||||
|
const storedBrew = PopulatedBrews[brewId];
|
||||||
|
|
||||||
|
const valid = storedBrew &&
|
||||||
|
brew.shareId == storedBrew.shareId &&
|
||||||
|
brew.title == storedBrew.title &&
|
||||||
|
brew.views == storedBrew.views;
|
||||||
|
|
||||||
|
this.assert(
|
||||||
|
valid,
|
||||||
|
`expect #{this} to be brew ${brewId}`,
|
||||||
|
`expect #{this} to not be brew ${brewId}`
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -15,8 +15,6 @@ const requestHandler = (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
console.log(config.get('admin:key'));
|
|
||||||
|
|
||||||
const test_user = {
|
const test_user = {
|
||||||
username : 'cool guy'
|
username : 'cool guy'
|
||||||
};
|
};
|
||||||
@@ -105,8 +103,8 @@ describe('Middleware', () => {
|
|||||||
it('should detect when you use the admin key', () => {
|
it('should detect when you use the admin key', () => {
|
||||||
app.use(mw.admin);
|
app.use(mw.admin);
|
||||||
app.use(requestHandler)
|
app.use(requestHandler)
|
||||||
return request(app).get(`/?admin_key=${config.get('admin:key')}`)
|
return request(app).get('/')
|
||||||
.send()
|
.set('x-homebrew-admin', config.get('admin:key'))
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
const req = res.body;
|
const req = res.body;
|
||||||
@@ -118,7 +116,8 @@ describe('Middleware', () => {
|
|||||||
app.use(mw.adminOnly);
|
app.use(mw.adminOnly);
|
||||||
app.get(requestHandler);
|
app.get(requestHandler);
|
||||||
app.use(Error.expressHandler);
|
app.use(Error.expressHandler);
|
||||||
return request(app).get(`/?admin_key=BADKEY`)
|
return request(app).get('/')
|
||||||
|
.set('x-homebrew-admin', 'BADADMIN')
|
||||||
.send()
|
.send()
|
||||||
.expect(401);
|
.expect(401);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
const Test = require('./test.init.js');
|
||||||
|
const _ = require('lodash');
|
||||||
|
|
||||||
|
const DB = require('db.js');
|
||||||
|
const BrewData = require('brew.data.js');
|
||||||
|
const BrewGen = require('./brew.gen.js');
|
||||||
|
//const Error = require('error.js');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe('Brew Search', () => {
|
||||||
|
before('Connect DB', DB.connect);
|
||||||
|
before('Clear DB', BrewData.removeAll);
|
||||||
|
before('Populate brews', ()=>{
|
||||||
|
return BrewGen.populateDB(BrewGen.static());
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('Searching', ()=>{
|
||||||
|
it('should return a total and a brew array', ()=>{
|
||||||
|
return BrewData.search()
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.a('number');
|
||||||
|
result.brews.should.be.an('array');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to search for all brews', ()=>{
|
||||||
|
return BrewData.search()
|
||||||
|
.then((result) => {
|
||||||
|
const brewCount = _.size(BrewGen.static());
|
||||||
|
result.total.should.be.equal(brewCount);
|
||||||
|
result.brews.length.should.be.equal(brewCount);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Pagniation', () => {
|
||||||
|
it('should return the exact number of brews based on limit', () => {
|
||||||
|
return BrewData.search({}, {
|
||||||
|
limit : 2
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(_.size(BrewGen.static()));
|
||||||
|
result.brews.length.should.be.equal(2);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the correct pages when specified', () => {
|
||||||
|
return BrewData.search({}, {
|
||||||
|
limit : 2,
|
||||||
|
page : 1,
|
||||||
|
sort : { views : 1 }
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return a partial list if on the last page', () => {
|
||||||
|
return BrewData.search({}, {
|
||||||
|
limit : 3,
|
||||||
|
page : 1
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
result.brews.length.should.be.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Sorting', ()=>{
|
||||||
|
it('should sort ASC', () => {
|
||||||
|
return BrewData.search({}, {
|
||||||
|
sort : { views : 1 }
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
result.brews[0].should.be.brew('BrewC');
|
||||||
|
result.brews[1].should.be.brew('BrewD');
|
||||||
|
result.brews[2].should.be.brew('BrewB');
|
||||||
|
result.brews[3].should.be.brew('BrewA');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
it('should sort DESC', () => {
|
||||||
|
return BrewData.search({}, {
|
||||||
|
sort : { views : -1 }
|
||||||
|
})
|
||||||
|
.then((result) => {
|
||||||
|
result.brews[0].should.be.brew('BrewA');
|
||||||
|
result.brews[1].should.be.brew('BrewB');
|
||||||
|
result.brews[2].should.be.brew('BrewD');
|
||||||
|
result.brews[3].should.be.brew('BrewC');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Permissions', () => {
|
||||||
|
it('should only fetch published brews', () => {
|
||||||
|
return BrewData.search({}, {}, false)
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(2);
|
||||||
|
result.brews.should.have.brews('BrewB', 'BrewD');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
it('fetched brews should not have text or editId', () => {
|
||||||
|
return BrewData.search({}, {}, false)
|
||||||
|
.then((result) => {
|
||||||
|
result.brews[0].should.not.have.property('text');
|
||||||
|
result.brews[0].should.not.have.property('editId');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
it('if full access, brews should have editid, but no text', () => {
|
||||||
|
return BrewData.search({}, {}, true)
|
||||||
|
.then((result) => {
|
||||||
|
result.brews[0].should.not.have.property('text');
|
||||||
|
result.brews[0].should.have.property('editId');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
describe('Term Search', ()=>{
|
||||||
|
it('should search brews based on title', () => {
|
||||||
|
return BrewData.termSearch('Charlie')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(1);
|
||||||
|
result.brews.should.have.brews('BrewC');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should search brews based on description', () => {
|
||||||
|
return BrewData.termSearch('fancy')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(2);
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should search brews based on multiple terms', () => {
|
||||||
|
return BrewData.termSearch('ranger 5e')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(1);
|
||||||
|
result.brews.should.have.brews('BrewD');
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should perform an AND operation on the provided terms', () => {
|
||||||
|
return BrewData.termSearch('Brew Delta GARBAGE')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should search brews based on a combination of both', () => {
|
||||||
|
return BrewData.termSearch('Brew Beta fancy')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(1);
|
||||||
|
result.brews.should.have.brews('BrewB');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should not worry about the case of the terms', () => {
|
||||||
|
return BrewData.termSearch('FANCY')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(2);
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('User Search', ()=>{
|
||||||
|
it('should return brews just for a single user', () => {
|
||||||
|
return BrewData.userSearch('userA')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(3);
|
||||||
|
result.brews.should.have.brews('BrewA', 'BrewB', 'BrewC');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('should return nothing if provided a non-exsistent user', () => {
|
||||||
|
return BrewData.userSearch('userXYZ')
|
||||||
|
.then((result) => {
|
||||||
|
result.total.should.be.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -7,10 +7,18 @@ const config = require('nconf')
|
|||||||
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
|
.file('environment', { file: `config/${process.env.NODE_ENV}.json` })
|
||||||
.file('defaults', { file: 'config/default.json' });
|
.file('defaults', { file: 'config/default.json' });
|
||||||
|
|
||||||
const should = require('chai').use(require('chai-as-promised')).should();
|
const Chai = require('chai')
|
||||||
|
.use(require('chai-as-promised'))
|
||||||
|
.use(require('chai-subset'))
|
||||||
|
.use(require('./brew.gen.js').chaiPlugin);
|
||||||
|
|
||||||
const log = require('loglevel');
|
const log = require('loglevel');
|
||||||
log.setLevel(config.get('log_level'));
|
log.setLevel(config.get('log_level'));
|
||||||
|
|
||||||
|
const jwt = require('jwt-simple');
|
||||||
module.exports = {
|
module.exports = {
|
||||||
should: should
|
should: Chai.should(),
|
||||||
|
getSessionToken : (userInfo) => {
|
||||||
|
return jwt.encode(userInfo, config.get('jwt_secret'));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user