0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-07 18:32:40 +00:00

Merge branch 'master' into update-Marked-renderer

This commit is contained in:
Trevor Buckner
2020-04-20 21:40:04 -04:00
19 changed files with 1694 additions and 1587 deletions

1
.gitignore vendored
View File

@@ -10,4 +10,3 @@ config/local.*
todo.md todo.md
startDB.bat startDB.bat
startMViewer.bat startMViewer.bat
*.xlsx

View File

@@ -1,14 +1,19 @@
FROM node:8 FROM node:8
ENV NODE_ENV=docker
# Create app directory # Create app directory
WORKDIR /usr/src/app WORKDIR /usr/src/app
# Bundle app source # Copy package.json into the image, then run yarn install
# This improves caching so we don't have to download the dependencies every time the code changes
COPY package.json ./
# --ignore-scripts tells yarn not to run postbuild. We run it explicitly later
RUN yarn install --ignore-scripts
# Bundle app source and build application
COPY . . COPY . .
RUN yarn build
ENV NODE_ENV=docker
RUN yarn
EXPOSE 8000 EXPOSE 8000
CMD [ "yarn", "start" ] CMD [ "yarn", "start" ]

12
README.DOCKER.md Normal file
View File

@@ -0,0 +1,12 @@
# Running Homebrewery via Docker
The repo includes a Dockerfile and a docker-compose.yml file.
To run the application via docker-compose.yml:
`docker-compose up -d`
To stop the application:
`docker-compose down`
To stop the application and remove all data:
`docker-compose down -v`

View File

@@ -29,6 +29,10 @@ Fourth, you will need to install the program and run it using the two commands:
You should now be able to go to [http://localhost:8000](http://localhost:8000) in your browser and use the Homebrewery offline. You should now be able to go to [http://localhost:8000](http://localhost:8000) in your browser and use the Homebrewery offline.
### Running the application via Docker
Please see the docs here: [README.DOCKER.md](./README.DOCKER.md)
### Standalone PHB Stylesheet ### Standalone PHB Stylesheet
If you just want the stylesheet that is generated to make pages look like they are from the Player's Handbook, you will find it in the [phb.standalone.css](./phb.standalone.css) file. If you just want the stylesheet that is generated to make pages look like they are from the Player's Handbook, you will find it in the [phb.standalone.css](./phb.standalone.css) file.

View File

@@ -1,5 +1,9 @@
# changelog # changelog
### Wednesday, 11/03/2020 - v2.8.2
- Fixed delete button removing everyone's copy for brews with multiple authors
- Compressed homebrew text in database
### Monday, 26/11/2018 - v2.8.1 ### Monday, 26/11/2018 - v2.8.1
- Fixed some SSL issues with images in the example page so they appear now - Fixed some SSL issues with images in the example page so they appear now
- Fixed duplicate scrollbars in Edit Page - Fixed duplicate scrollbars in Edit Page

View File

@@ -50,7 +50,7 @@ const MetadataEditor = createClass({
if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return;
} }
request.get(`/api/remove/${this.props.metadata.editId}`) request.delete(`/api/${this.props.metadata.editId}`)
.send() .send()
.end(function(err, res){ .end(function(err, res){
window.location.href = '/'; window.location.href = '/';

View File

@@ -3,6 +3,7 @@ const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require('superagent'); const request = require('superagent');
const Meta = require('vitreum/meta');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
@@ -134,7 +135,7 @@ const EditPage = createClass({
})); }));
request request
.put(`/api/update/${this.props.brew.editId}`) .put(`/api/${this.props.brew.editId}`)
.send(this.state.brew) .send(this.state.brew)
.end((err, res)=>{ .end((err, res)=>{
if(err){ if(err){
@@ -202,6 +203,7 @@ const EditPage = createClass({
render : function(){ render : function(){
return <div className='editPage page'> return <div className='editPage page'>
<Meta name='robots' content='noindex, nofollow' />
{this.renderNavbar()} {this.renderNavbar()}
<div className='content'> <div className='content'>

View File

@@ -3,6 +3,7 @@ const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const request = require('superagent'); const request = require('superagent');
const Meta = require('vitreum/meta');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
@@ -72,6 +73,7 @@ const HomePage = createClass({
render : function(){ render : function(){
return <div className='homePage page'> return <div className='homePage page'>
<Meta name='google-site-verification' content='NwnAQSSJZzAT7N-p5MY6ydQ7Njm67dtbu73ZSyE5Fy4' />
{this.renderNavbar()} {this.renderNavbar()}
<div className='content'> <div className='content'>

View File

@@ -2,6 +2,7 @@ const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const Meta = require('vitreum/meta');
const Markdown = require('naturalcrit/markdown.js'); const Markdown = require('naturalcrit/markdown.js');
const PrintPage = createClass({ const PrintPage = createClass({
@@ -42,6 +43,7 @@ const PrintPage = createClass({
render : function(){ render : function(){
return <div> return <div>
<Meta name='robots' content='noindex, nofollow' />
{this.renderPages()} {this.renderPages()}
</div>; </div>;
} }

View File

@@ -2,6 +2,7 @@ const React = require('react');
const createClass = require('create-react-class'); const createClass = require('create-react-class');
const _ = require('lodash'); const _ = require('lodash');
const cx = require('classnames'); const cx = require('classnames');
const Meta = require('vitreum/meta');
const Nav = require('naturalcrit/nav/nav.jsx'); const Nav = require('naturalcrit/nav/nav.jsx');
const Navbar = require('../../navbar/navbar.jsx'); const Navbar = require('../../navbar/navbar.jsx');
@@ -45,6 +46,7 @@ const SharePage = createClass({
render : function(){ render : function(){
return <div className='sharePage page'> return <div className='sharePage page'>
<Meta name='robots' content='noindex, nofollow' />
<Navbar> <Navbar>
<Nav.section> <Nav.section>
<Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item> <Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item>

View File

@@ -26,7 +26,7 @@ const BrewItem = createClass({
if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return; if(!confirm('Are you REALLY sure? You will lose editor access to this document.')) return;
} }
request.get(`/api/remove/${this.props.brew.editId}`) request.delete(`/api/${this.props.brew.editId}`)
.send() .send()
.end(function(err, res){ .end(function(err, res){
location.reload(); location.reload();

View File

@@ -42,15 +42,6 @@ const UserPage = createClass({
}); });
}, },
renderPrivateBrews : function(privateBrews){
if(!privateBrews || !privateBrews.length) return;
return [
<h1>{this.props.username}'s unpublished brews</h1>,
this.renderBrews(privateBrews)
];
},
render : function(){ render : function(){
const brews = this.getSortedBrews(); const brews = this.getSortedBrews();
@@ -64,9 +55,14 @@ const UserPage = createClass({
<div className='content'> <div className='content'>
<div className='phb'> <div className='phb'>
<h1>{this.props.username}'s brews</h1> <div>
{this.renderBrews(brews.published)} <h1>{this.props.username}'s brews</h1>
{this.renderPrivateBrews(brews.private)} {this.renderBrews(brews.published)}
</div>
<div>
<h1>{this.props.username}'s unpublished brews</h1>
{this.renderBrews(brews.private)}
</div>
</div> </div>
</div> </div>
</div>; </div>;

View File

@@ -17,7 +17,7 @@
.phb{ .phb{
.noColumns(); .noColumns();
height : auto; height : auto;
min-height : 279.4mm; min-height : 279.4mm;
margin : 20px auto; margin : 20px auto;
&::after{ &::after{
display : none; display : none;

3078
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -40,19 +40,19 @@
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"body-parser": "^1.19.0", "body-parser": "^1.19.0",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"codemirror": "^5.52.0", "codemirror": "^5.52.2",
"cookie-parser": "^1.4.4", "cookie-parser": "^1.4.5",
"create-react-class": "^15.6.3", "create-react-class": "^15.6.3",
"express": "^4.17.1", "express": "^4.17.1",
"jwt-simple": "^0.5.6", "jwt-simple": "^0.5.6",
"lodash": "^4.17.15", "lodash": "^4.17.15",
"marked": "^0.8.2", "marked": "^0.8.2",
"moment": "^2.24.0", "moment": "^2.24.0",
"mongoose": "^5.9.2", "mongoose": "^5.9.9",
"nconf": "^0.10.0", "nconf": "^0.10.0",
"pico-router": "^2.1.0", "pico-router": "^2.1.0",
"react": "^16.13.0", "react": "^16.13.1",
"react-dom": "^16.13.0", "react-dom": "^16.13.1",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"superagent": "^5.2.2", "superagent": "^5.2.2",
"vitreum": "^4.10.1" "vitreum": "^4.10.1"

5
robots.txt Normal file
View File

@@ -0,0 +1,5 @@
# Disallow crawling brew edit and share pages
User-agent: *
Disallow: /edit/
Disallow: /share/
Disallow: /print/

View File

@@ -44,9 +44,14 @@ const HomebrewModel = require('./server/homebrew.model.js').model;
const welcomeText = require('fs').readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8'); const welcomeText = require('fs').readFileSync('./client/homebrew/pages/homePage/welcome_msg.md', 'utf8');
const changelogText = require('fs').readFileSync('./changelog.md', 'utf8'); const changelogText = require('fs').readFileSync('./changelog.md', 'utf8');
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
//Robots.txt
app.get('/robots.txt', (req, res)=>{
return res.sendFile(`${__dirname}/robots.txt`);
});
//Source page //Source page
String.prototype.replaceAll = function(s, r){return this.split(s).join(r);};
app.get('/source/:id', (req, res)=>{ app.get('/source/:id', (req, res)=>{
HomebrewModel.get({ shareId: req.params.id }) HomebrewModel.get({ shareId: req.params.id })
.then((brew)=>{ .then((brew)=>{
@@ -59,7 +64,7 @@ app.get('/source/:id', (req, res)=>{
}); });
}); });
//User Page
app.get('/user/:username', (req, res, next)=>{ app.get('/user/:username', (req, res, next)=>{
const fullAccess = req.account && (req.account.username == req.params.username); const fullAccess = req.account && (req.account.username == req.params.username);
HomebrewModel.getByUser(req.params.username, fullAccess) HomebrewModel.getByUser(req.params.username, fullAccess)
@@ -72,7 +77,7 @@ app.get('/user/:username', (req, res, next)=>{
}); });
}); });
//Edit Page
app.get('/edit/:id', (req, res, next)=>{ app.get('/edit/:id', (req, res, next)=>{
HomebrewModel.get({ editId: req.params.id }) HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{ .then((brew)=>{
@@ -115,7 +120,7 @@ app.get('/print/:id', (req, res, next)=>{
}); });
//Render Page //Render the page
const render = require('vitreum/steps/render'); const render = require('vitreum/steps/render');
const templateFn = require('./client/template.js'); const templateFn = require('./client/template.js');
app.use((req, res)=>{ app.use((req, res)=>{

View File

@@ -1,5 +1,5 @@
module.exports = (req, res, next)=>{ module.exports = (req, res, next)=>{
if(process.env.NODE_ENV === 'local') return next(); if(process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'docker') return next();
if(req.header('x-forwarded-proto') !== 'https') { if(req.header('x-forwarded-proto') !== 'https') {
return res.redirect(302, `https://${req.get('Host')}${req.url}`); return res.redirect(302, `https://${req.get('Host')}${req.url}`);
} }

View File

@@ -3,7 +3,7 @@ const HomebrewModel = require('./homebrew.model.js').model;
const router = require('express').Router(); const router = require('express').Router();
const zlib = require('zlib'); const zlib = require('zlib');
// const getTopBrews = (cb)=>{ // const getTopBrews = (cb) => {
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) { // HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
// cb(brews); // cb(brews);
// }); // });
@@ -11,53 +11,53 @@ const zlib = require('zlib');
const getGoodBrewTitle = (text)=>{ const getGoodBrewTitle = (text)=>{
const titlePos = text.indexOf('# '); const titlePos = text.indexOf('# ');
if(titlePos !== -1){ if(titlePos !== -1) {
const ending = text.indexOf('\n', titlePos); const ending = text.indexOf('\n', titlePos);
return text.substring(titlePos + 2, ending); return text.substring(titlePos + 2, ending);
} else { } else {
return _.find(text.split('\n'), (line)=>{ return _.find(text.split('\n'), (line)=>line);
return line;
});
} }
}; };
const newBrew = (req, res)=>{
const authors = (req.account) ? [req.account.username] : [];
router.post('/api', (req, res)=>{
let authors = [];
if(req.account) authors = [req.account.username];
const newHomebrew = new HomebrewModel(_.merge({}, const newHomebrew = new HomebrewModel(_.merge({},
req.body, req.body,
{ authors: authors } { authors: authors }
)); ));
if(!newHomebrew.title){ if(!newHomebrew.title) {
newHomebrew.title = getGoodBrewTitle(newHomebrew.text); newHomebrew.title = getGoodBrewTitle(newHomebrew.text);
} }
newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text); // Compress brew text to binary before saving // Compress brew text to binary before saving
newHomebrew.text = undefined; // Delete the non-binary text field since it's not needed anymore newHomebrew.textBin = zlib.deflateRawSync(newHomebrew.text);
// Delete the non-binary text field since it's not needed anymore
newHomebrew.text = undefined;
newHomebrew.save((err, obj)=>{ newHomebrew.save((err, obj)=>{
if(err){ if(err) {
console.error(err, err.toString(), err.stack); console.error(err, err.toString(), err.stack);
return res.status(500).send(`Error while creating new brew, ${err.toString()}`); return res.status(500).send(`Error while creating new brew, ${err.toString()}`);
} }
return res.json(obj); return res.json(obj);
}); });
}); };
router.put('/api/update/:id', (req, res)=>{ const updateBrew = (req, res)=>{
HomebrewModel.get({ editId: req.params.id }) HomebrewModel.get({ editId: req.params.id })
.then((brew)=>{ .then((brew)=>{
brew = _.merge(brew, req.body); brew = _.merge(brew, req.body);
brew.textBin = zlib.deflateRawSync(req.body.text); // Compress brew text to binary before saving // Compress brew text to binary before saving
brew.text = undefined; // Delete the non-binary text field since it's not needed anymore brew.textBin = zlib.deflateRawSync(req.body.text);
// Delete the non-binary text field since it's not needed anymore
brew.text = undefined;
brew.updatedAt = new Date(); brew.updatedAt = new Date();
if(req.account) brew.authors = _.uniq(_.concat(brew.authors, req.account.username)); if(req.account) {
brew.authors = _.uniq(_.concat(brew.authors, req.account.username));
}
brew.markModified('authors'); brew.markModified('authors');
brew.markModified('systems'); brew.markModified('systems');
@@ -68,62 +68,63 @@ router.put('/api/update/:id', (req, res)=>{
}); });
}) })
.catch((err)=>{ .catch((err)=>{
console.log(err); console.error(err);
return res.status(500).send('Error while saving'); return res.status(500).send('Error while saving');
}); });
}); };
router.get('/api/remove/:id', (req, res)=>{ const deleteBrew = (req, res)=>{
HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{ HomebrewModel.find({ editId: req.params.id }, (err, objs)=>{
if(!objs.length || err) return res.status(404).send('Can not find homebrew with that id'); if(!objs.length || err) {
return res.status(404).send('Can not find homebrew with that id');
}
const brew = objs[0]; const brew = objs[0];
// Remove current user as author if(req.account) {
if(req.account){ // Remove current user as author
brew.authors = _.pull(brew.authors, req.account.username); brew.authors = _.pull(brew.authors, req.account.username);
brew.markModified('authors'); brew.markModified('authors');
} }
// Delete brew if there are no authors left if(brew.authors.length === 0) {
if(!brew.authors.length) // Delete brew if there are no authors left
brew.remove((err)=>{ brew.remove((err)=>{
if(err) return res.status(500).send('Error while removing'); if(err) return res.status(500).send('Error while removing');
return res.status(200).send(); return res.status(200).send();
}); });
// Otherwise, save the brew with updated author list } else {
else // Otherwise, save the brew with updated author list
brew.save((err, savedBrew)=>{ brew.save((err, savedBrew)=>{
if(err) throw err; if(err) throw err;
return res.status(200).send(savedBrew); return res.status(200).send(savedBrew);
}); });
}
}); });
}); };
router.post('/api', newBrew);
router.put('/api/:id', updateBrew);
router.put('/api/update/:id', updateBrew);
router.delete('/api/:id', deleteBrew);
router.get('/api/remove/:id', deleteBrew);
module.exports = router; module.exports = router;
/* /*
module.exports = function(app) {
module.exports = function(app){
app; app;
app.get('/api/search', mw.adminOnly, function(req, res) {
app.get('/api/search', mw.adminOnly, function(req, res){
var page = req.query.page || 0; var page = req.query.page || 0;
var count = req.query.count || 20; var count = req.query.count || 20;
var query = {}; var query = {};
if(req.query && req.query.id){ if (req.query && req.query.id) {
query = { query = {
"$or" : [{ "$or": [{
editId : req.query.id editId : req.query.id
},{ }, {
shareId : req.query.id shareId : req.query.id
}] }]
}; };
@@ -134,21 +135,17 @@ module.exports = function(app){
}, { }, {
skip: page*count, skip: page*count,
limit: count*1 limit: count*1
}, function(err, objs){ }, function(err, objs) {
if(err) console.log(err); if (err) console.error(err);
return res.json({ return res.json({
page : page, page : page,
count : count, count : count,
total : homebrewTotal, total : homebrewTotal,
brews : objs brews : objs
}); });
}); });
}) })
return app; return app;
} }
*/ */